Fix #6673 - mobile cards 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                     this.dom.className = this.dom.className + " " + className;
7679                 }
7680             }
7681             return this;
7682         },
7683
7684         /**
7685          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
7686          * @param {String/Array} className The CSS class to add, or an array of classes
7687          * @return {Roo.Element} this
7688          */
7689         radioClass : function(className){
7690             var siblings = this.dom.parentNode.childNodes;
7691             for(var i = 0; i < siblings.length; i++) {
7692                 var s = siblings[i];
7693                 if(s.nodeType == 1){
7694                     Roo.get(s).removeClass(className);
7695                 }
7696             }
7697             this.addClass(className);
7698             return this;
7699         },
7700
7701         /**
7702          * Removes one or more CSS classes from the element.
7703          * @param {String/Array} className The CSS class to remove, or an array of classes
7704          * @return {Roo.Element} this
7705          */
7706         removeClass : function(className){
7707             if(!className || !this.dom.className){
7708                 return this;
7709             }
7710             if(className instanceof Array){
7711                 for(var i = 0, len = className.length; i < len; i++) {
7712                     this.removeClass(className[i]);
7713                 }
7714             }else{
7715                 if(this.hasClass(className)){
7716                     var re = this.classReCache[className];
7717                     if (!re) {
7718                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
7719                        this.classReCache[className] = re;
7720                     }
7721                     this.dom.className =
7722                         this.dom.className.replace(re, " ");
7723                 }
7724             }
7725             return this;
7726         },
7727
7728         // private
7729         classReCache: {},
7730
7731         /**
7732          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
7733          * @param {String} className The CSS class to toggle
7734          * @return {Roo.Element} this
7735          */
7736         toggleClass : function(className){
7737             if(this.hasClass(className)){
7738                 this.removeClass(className);
7739             }else{
7740                 this.addClass(className);
7741             }
7742             return this;
7743         },
7744
7745         /**
7746          * Checks if the specified CSS class exists on this element's DOM node.
7747          * @param {String} className The CSS class to check for
7748          * @return {Boolean} True if the class exists, else false
7749          */
7750         hasClass : function(className){
7751             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
7752         },
7753
7754         /**
7755          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
7756          * @param {String} oldClassName The CSS class to replace
7757          * @param {String} newClassName The replacement CSS class
7758          * @return {Roo.Element} this
7759          */
7760         replaceClass : function(oldClassName, newClassName){
7761             this.removeClass(oldClassName);
7762             this.addClass(newClassName);
7763             return this;
7764         },
7765
7766         /**
7767          * Returns an object with properties matching the styles requested.
7768          * For example, el.getStyles('color', 'font-size', 'width') might return
7769          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
7770          * @param {String} style1 A style name
7771          * @param {String} style2 A style name
7772          * @param {String} etc.
7773          * @return {Object} The style object
7774          */
7775         getStyles : function(){
7776             var a = arguments, len = a.length, r = {};
7777             for(var i = 0; i < len; i++){
7778                 r[a[i]] = this.getStyle(a[i]);
7779             }
7780             return r;
7781         },
7782
7783         /**
7784          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
7785          * @param {String} property The style property whose value is returned.
7786          * @return {String} The current value of the style property for this element.
7787          */
7788         getStyle : function(){
7789             return view && view.getComputedStyle ?
7790                 function(prop){
7791                     var el = this.dom, v, cs, camel;
7792                     if(prop == 'float'){
7793                         prop = "cssFloat";
7794                     }
7795                     if(el.style && (v = el.style[prop])){
7796                         return v;
7797                     }
7798                     if(cs = view.getComputedStyle(el, "")){
7799                         if(!(camel = propCache[prop])){
7800                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
7801                         }
7802                         return cs[camel];
7803                     }
7804                     return null;
7805                 } :
7806                 function(prop){
7807                     var el = this.dom, v, cs, camel;
7808                     if(prop == 'opacity'){
7809                         if(typeof el.style.filter == 'string'){
7810                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
7811                             if(m){
7812                                 var fv = parseFloat(m[1]);
7813                                 if(!isNaN(fv)){
7814                                     return fv ? fv / 100 : 0;
7815                                 }
7816                             }
7817                         }
7818                         return 1;
7819                     }else if(prop == 'float'){
7820                         prop = "styleFloat";
7821                     }
7822                     if(!(camel = propCache[prop])){
7823                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
7824                     }
7825                     if(v = el.style[camel]){
7826                         return v;
7827                     }
7828                     if(cs = el.currentStyle){
7829                         return cs[camel];
7830                     }
7831                     return null;
7832                 };
7833         }(),
7834
7835         /**
7836          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
7837          * @param {String/Object} property The style property to be set, or an object of multiple styles.
7838          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
7839          * @return {Roo.Element} this
7840          */
7841         setStyle : function(prop, value){
7842             if(typeof prop == "string"){
7843                 
7844                 if (prop == 'float') {
7845                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
7846                     return this;
7847                 }
7848                 
7849                 var camel;
7850                 if(!(camel = propCache[prop])){
7851                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
7852                 }
7853                 
7854                 if(camel == 'opacity') {
7855                     this.setOpacity(value);
7856                 }else{
7857                     this.dom.style[camel] = value;
7858                 }
7859             }else{
7860                 for(var style in prop){
7861                     if(typeof prop[style] != "function"){
7862                        this.setStyle(style, prop[style]);
7863                     }
7864                 }
7865             }
7866             return this;
7867         },
7868
7869         /**
7870          * More flexible version of {@link #setStyle} for setting style properties.
7871          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
7872          * a function which returns such a specification.
7873          * @return {Roo.Element} this
7874          */
7875         applyStyles : function(style){
7876             Roo.DomHelper.applyStyles(this.dom, style);
7877             return this;
7878         },
7879
7880         /**
7881           * Gets the current X position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7882           * @return {Number} The X position of the element
7883           */
7884         getX : function(){
7885             return D.getX(this.dom);
7886         },
7887
7888         /**
7889           * Gets the current Y position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7890           * @return {Number} The Y position of the element
7891           */
7892         getY : function(){
7893             return D.getY(this.dom);
7894         },
7895
7896         /**
7897           * Gets the current position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7898           * @return {Array} The XY position of the element
7899           */
7900         getXY : function(){
7901             return D.getXY(this.dom);
7902         },
7903
7904         /**
7905          * Sets the X position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7906          * @param {Number} The X position of the element
7907          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7908          * @return {Roo.Element} this
7909          */
7910         setX : function(x, animate){
7911             if(!animate || !A){
7912                 D.setX(this.dom, x);
7913             }else{
7914                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
7915             }
7916             return this;
7917         },
7918
7919         /**
7920          * Sets the Y position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7921          * @param {Number} The Y position of the element
7922          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7923          * @return {Roo.Element} this
7924          */
7925         setY : function(y, animate){
7926             if(!animate || !A){
7927                 D.setY(this.dom, y);
7928             }else{
7929                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
7930             }
7931             return this;
7932         },
7933
7934         /**
7935          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
7936          * @param {String} left The left CSS property value
7937          * @return {Roo.Element} this
7938          */
7939         setLeft : function(left){
7940             this.setStyle("left", this.addUnits(left));
7941             return this;
7942         },
7943
7944         /**
7945          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
7946          * @param {String} top The top CSS property value
7947          * @return {Roo.Element} this
7948          */
7949         setTop : function(top){
7950             this.setStyle("top", this.addUnits(top));
7951             return this;
7952         },
7953
7954         /**
7955          * Sets the element's CSS right style.
7956          * @param {String} right The right CSS property value
7957          * @return {Roo.Element} this
7958          */
7959         setRight : function(right){
7960             this.setStyle("right", this.addUnits(right));
7961             return this;
7962         },
7963
7964         /**
7965          * Sets the element's CSS bottom style.
7966          * @param {String} bottom The bottom CSS property value
7967          * @return {Roo.Element} this
7968          */
7969         setBottom : function(bottom){
7970             this.setStyle("bottom", this.addUnits(bottom));
7971             return this;
7972         },
7973
7974         /**
7975          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7976          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7977          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
7978          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7979          * @return {Roo.Element} this
7980          */
7981         setXY : function(pos, animate){
7982             if(!animate || !A){
7983                 D.setXY(this.dom, pos);
7984             }else{
7985                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
7986             }
7987             return this;
7988         },
7989
7990         /**
7991          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7992          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7993          * @param {Number} x X value for new position (coordinates are page-based)
7994          * @param {Number} y Y value for new position (coordinates are page-based)
7995          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7996          * @return {Roo.Element} this
7997          */
7998         setLocation : function(x, y, animate){
7999             this.setXY([x, y], this.preanim(arguments, 2));
8000             return this;
8001         },
8002
8003         /**
8004          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
8005          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
8006          * @param {Number} x X value for new position (coordinates are page-based)
8007          * @param {Number} y Y value for new position (coordinates are page-based)
8008          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8009          * @return {Roo.Element} this
8010          */
8011         moveTo : function(x, y, animate){
8012             this.setXY([x, y], this.preanim(arguments, 2));
8013             return this;
8014         },
8015
8016         /**
8017          * Returns the region of the given element.
8018          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
8019          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
8020          */
8021         getRegion : function(){
8022             return D.getRegion(this.dom);
8023         },
8024
8025         /**
8026          * Returns the offset height of the element
8027          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
8028          * @return {Number} The element's height
8029          */
8030         getHeight : function(contentHeight){
8031             var h = this.dom.offsetHeight || 0;
8032             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
8033         },
8034
8035         /**
8036          * Returns the offset width of the element
8037          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
8038          * @return {Number} The element's width
8039          */
8040         getWidth : function(contentWidth){
8041             var w = this.dom.offsetWidth || 0;
8042             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
8043         },
8044
8045         /**
8046          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
8047          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
8048          * if a height has not been set using CSS.
8049          * @return {Number}
8050          */
8051         getComputedHeight : function(){
8052             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
8053             if(!h){
8054                 h = parseInt(this.getStyle('height'), 10) || 0;
8055                 if(!this.isBorderBox()){
8056                     h += this.getFrameWidth('tb');
8057                 }
8058             }
8059             return h;
8060         },
8061
8062         /**
8063          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
8064          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
8065          * if a width has not been set using CSS.
8066          * @return {Number}
8067          */
8068         getComputedWidth : function(){
8069             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
8070             if(!w){
8071                 w = parseInt(this.getStyle('width'), 10) || 0;
8072                 if(!this.isBorderBox()){
8073                     w += this.getFrameWidth('lr');
8074                 }
8075             }
8076             return w;
8077         },
8078
8079         /**
8080          * Returns the size of the element.
8081          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
8082          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
8083          */
8084         getSize : function(contentSize){
8085             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
8086         },
8087
8088         /**
8089          * Returns the width and height of the viewport.
8090          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
8091          */
8092         getViewSize : function(){
8093             var d = this.dom, doc = document, aw = 0, ah = 0;
8094             if(d == doc || d == doc.body){
8095                 return {width : D.getViewWidth(), height: D.getViewHeight()};
8096             }else{
8097                 return {
8098                     width : d.clientWidth,
8099                     height: d.clientHeight
8100                 };
8101             }
8102         },
8103
8104         /**
8105          * Returns the value of the "value" attribute
8106          * @param {Boolean} asNumber true to parse the value as a number
8107          * @return {String/Number}
8108          */
8109         getValue : function(asNumber){
8110             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
8111         },
8112
8113         // private
8114         adjustWidth : function(width){
8115             if(typeof width == "number"){
8116                 if(this.autoBoxAdjust && !this.isBorderBox()){
8117                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8118                 }
8119                 if(width < 0){
8120                     width = 0;
8121                 }
8122             }
8123             return width;
8124         },
8125
8126         // private
8127         adjustHeight : function(height){
8128             if(typeof height == "number"){
8129                if(this.autoBoxAdjust && !this.isBorderBox()){
8130                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8131                }
8132                if(height < 0){
8133                    height = 0;
8134                }
8135             }
8136             return height;
8137         },
8138
8139         /**
8140          * Set the width of the element
8141          * @param {Number} width The new width
8142          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8143          * @return {Roo.Element} this
8144          */
8145         setWidth : function(width, animate){
8146             width = this.adjustWidth(width);
8147             if(!animate || !A){
8148                 this.dom.style.width = this.addUnits(width);
8149             }else{
8150                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
8151             }
8152             return this;
8153         },
8154
8155         /**
8156          * Set the height of the element
8157          * @param {Number} height The new height
8158          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8159          * @return {Roo.Element} this
8160          */
8161          setHeight : function(height, animate){
8162             height = this.adjustHeight(height);
8163             if(!animate || !A){
8164                 this.dom.style.height = this.addUnits(height);
8165             }else{
8166                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
8167             }
8168             return this;
8169         },
8170
8171         /**
8172          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
8173          * @param {Number} width The new width
8174          * @param {Number} height The new height
8175          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8176          * @return {Roo.Element} this
8177          */
8178          setSize : function(width, height, animate){
8179             if(typeof width == "object"){ // in case of object from getSize()
8180                 height = width.height; width = width.width;
8181             }
8182             width = this.adjustWidth(width); height = this.adjustHeight(height);
8183             if(!animate || !A){
8184                 this.dom.style.width = this.addUnits(width);
8185                 this.dom.style.height = this.addUnits(height);
8186             }else{
8187                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
8188             }
8189             return this;
8190         },
8191
8192         /**
8193          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
8194          * @param {Number} x X value for new position (coordinates are page-based)
8195          * @param {Number} y Y value for new position (coordinates are page-based)
8196          * @param {Number} width The new width
8197          * @param {Number} height The new height
8198          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8199          * @return {Roo.Element} this
8200          */
8201         setBounds : function(x, y, width, height, animate){
8202             if(!animate || !A){
8203                 this.setSize(width, height);
8204                 this.setLocation(x, y);
8205             }else{
8206                 width = this.adjustWidth(width); height = this.adjustHeight(height);
8207                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
8208                               this.preanim(arguments, 4), 'motion');
8209             }
8210             return this;
8211         },
8212
8213         /**
8214          * Sets the element's position and size the the specified region. If animation is true then width, height, x and y will be animated concurrently.
8215          * @param {Roo.lib.Region} region The region to fill
8216          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8217          * @return {Roo.Element} this
8218          */
8219         setRegion : function(region, animate){
8220             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
8221             return this;
8222         },
8223
8224         /**
8225          * Appends an event handler
8226          *
8227          * @param {String}   eventName     The type of event to append
8228          * @param {Function} fn        The method the event invokes
8229          * @param {Object} scope       (optional) The scope (this object) of the fn
8230          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
8231          */
8232         addListener : function(eventName, fn, scope, options){
8233             if (this.dom) {
8234                 Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
8235             }
8236             if (eventName == 'dblclick') {
8237                 this.addListener('touchstart', this.onTapHandler, this);
8238             }
8239         },
8240         tapedTwice : false,
8241         onTapHandler : function(event)
8242         {
8243             if(!this.tapedTwice) {
8244                 this.tapedTwice = true;
8245                 var s = this;
8246                 setTimeout( function() {
8247                     s.tapedTwice = false;
8248                 }, 300 );
8249                 return;
8250             }
8251             event.preventDefault();
8252             var revent = new MouseEvent('dblclick',  {
8253                 view: window,
8254                 bubbles: true,
8255                 cancelable: true
8256             });
8257              
8258             this.dom.dispatchEvent(revent);
8259             //action on double tap goes below
8260              
8261         }, 
8262
8263         /**
8264          * Removes an event handler from this element
8265          * @param {String} eventName the type of event to remove
8266          * @param {Function} fn the method the event invokes
8267          * @return {Roo.Element} this
8268          */
8269         removeListener : function(eventName, fn){
8270             Roo.EventManager.removeListener(this.dom,  eventName, fn);
8271             return this;
8272         },
8273
8274         /**
8275          * Removes all previous added listeners from this element
8276          * @return {Roo.Element} this
8277          */
8278         removeAllListeners : function(){
8279             E.purgeElement(this.dom);
8280             return this;
8281         },
8282
8283         relayEvent : function(eventName, observable){
8284             this.on(eventName, function(e){
8285                 observable.fireEvent(eventName, e);
8286             });
8287         },
8288
8289         /**
8290          * Set the opacity of the element
8291          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
8292          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8293          * @return {Roo.Element} this
8294          */
8295          setOpacity : function(opacity, animate){
8296             if(!animate || !A){
8297                 var s = this.dom.style;
8298                 if(Roo.isIE){
8299                     s.zoom = 1;
8300                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
8301                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
8302                 }else{
8303                     s.opacity = opacity;
8304                 }
8305             }else{
8306                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
8307             }
8308             return this;
8309         },
8310
8311         /**
8312          * Gets the left X coordinate
8313          * @param {Boolean} local True to get the local css position instead of page coordinate
8314          * @return {Number}
8315          */
8316         getLeft : function(local){
8317             if(!local){
8318                 return this.getX();
8319             }else{
8320                 return parseInt(this.getStyle("left"), 10) || 0;
8321             }
8322         },
8323
8324         /**
8325          * Gets the right X coordinate of the element (element X position + element width)
8326          * @param {Boolean} local True to get the local css position instead of page coordinate
8327          * @return {Number}
8328          */
8329         getRight : function(local){
8330             if(!local){
8331                 return this.getX() + this.getWidth();
8332             }else{
8333                 return (this.getLeft(true) + this.getWidth()) || 0;
8334             }
8335         },
8336
8337         /**
8338          * Gets the top Y coordinate
8339          * @param {Boolean} local True to get the local css position instead of page coordinate
8340          * @return {Number}
8341          */
8342         getTop : function(local) {
8343             if(!local){
8344                 return this.getY();
8345             }else{
8346                 return parseInt(this.getStyle("top"), 10) || 0;
8347             }
8348         },
8349
8350         /**
8351          * Gets the bottom Y coordinate of the element (element Y position + element height)
8352          * @param {Boolean} local True to get the local css position instead of page coordinate
8353          * @return {Number}
8354          */
8355         getBottom : function(local){
8356             if(!local){
8357                 return this.getY() + this.getHeight();
8358             }else{
8359                 return (this.getTop(true) + this.getHeight()) || 0;
8360             }
8361         },
8362
8363         /**
8364         * Initializes positioning on this element. If a desired position is not passed, it will make the
8365         * the element positioned relative IF it is not already positioned.
8366         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
8367         * @param {Number} zIndex (optional) The zIndex to apply
8368         * @param {Number} x (optional) Set the page X position
8369         * @param {Number} y (optional) Set the page Y position
8370         */
8371         position : function(pos, zIndex, x, y){
8372             if(!pos){
8373                if(this.getStyle('position') == 'static'){
8374                    this.setStyle('position', 'relative');
8375                }
8376             }else{
8377                 this.setStyle("position", pos);
8378             }
8379             if(zIndex){
8380                 this.setStyle("z-index", zIndex);
8381             }
8382             if(x !== undefined && y !== undefined){
8383                 this.setXY([x, y]);
8384             }else if(x !== undefined){
8385                 this.setX(x);
8386             }else if(y !== undefined){
8387                 this.setY(y);
8388             }
8389         },
8390
8391         /**
8392         * Clear positioning back to the default when the document was loaded
8393         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
8394         * @return {Roo.Element} this
8395          */
8396         clearPositioning : function(value){
8397             value = value ||'';
8398             this.setStyle({
8399                 "left": value,
8400                 "right": value,
8401                 "top": value,
8402                 "bottom": value,
8403                 "z-index": "",
8404                 "position" : "static"
8405             });
8406             return this;
8407         },
8408
8409         /**
8410         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
8411         * snapshot before performing an update and then restoring the element.
8412         * @return {Object}
8413         */
8414         getPositioning : function(){
8415             var l = this.getStyle("left");
8416             var t = this.getStyle("top");
8417             return {
8418                 "position" : this.getStyle("position"),
8419                 "left" : l,
8420                 "right" : l ? "" : this.getStyle("right"),
8421                 "top" : t,
8422                 "bottom" : t ? "" : this.getStyle("bottom"),
8423                 "z-index" : this.getStyle("z-index")
8424             };
8425         },
8426
8427         /**
8428          * Gets the width of the border(s) for the specified side(s)
8429          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8430          * passing lr would get the border (l)eft width + the border (r)ight width.
8431          * @return {Number} The width of the sides passed added together
8432          */
8433         getBorderWidth : function(side){
8434             return this.addStyles(side, El.borders);
8435         },
8436
8437         /**
8438          * Gets the width of the padding(s) for the specified side(s)
8439          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8440          * passing lr would get the padding (l)eft + the padding (r)ight.
8441          * @return {Number} The padding of the sides passed added together
8442          */
8443         getPadding : function(side){
8444             return this.addStyles(side, El.paddings);
8445         },
8446
8447         /**
8448         * Set positioning with an object returned by getPositioning().
8449         * @param {Object} posCfg
8450         * @return {Roo.Element} this
8451          */
8452         setPositioning : function(pc){
8453             this.applyStyles(pc);
8454             if(pc.right == "auto"){
8455                 this.dom.style.right = "";
8456             }
8457             if(pc.bottom == "auto"){
8458                 this.dom.style.bottom = "";
8459             }
8460             return this;
8461         },
8462
8463         // private
8464         fixDisplay : function(){
8465             if(this.getStyle("display") == "none"){
8466                 this.setStyle("visibility", "hidden");
8467                 this.setStyle("display", this.originalDisplay); // first try reverting to default
8468                 if(this.getStyle("display") == "none"){ // if that fails, default to block
8469                     this.setStyle("display", "block");
8470                 }
8471             }
8472         },
8473
8474         /**
8475          * Quick set left and top adding default units
8476          * @param {String} left The left CSS property value
8477          * @param {String} top The top CSS property value
8478          * @return {Roo.Element} this
8479          */
8480          setLeftTop : function(left, top){
8481             this.dom.style.left = this.addUnits(left);
8482             this.dom.style.top = this.addUnits(top);
8483             return this;
8484         },
8485
8486         /**
8487          * Move this element relative to its current position.
8488          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
8489          * @param {Number} distance How far to move the element in pixels
8490          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8491          * @return {Roo.Element} this
8492          */
8493          move : function(direction, distance, animate){
8494             var xy = this.getXY();
8495             direction = direction.toLowerCase();
8496             switch(direction){
8497                 case "l":
8498                 case "left":
8499                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
8500                     break;
8501                case "r":
8502                case "right":
8503                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
8504                     break;
8505                case "t":
8506                case "top":
8507                case "up":
8508                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
8509                     break;
8510                case "b":
8511                case "bottom":
8512                case "down":
8513                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
8514                     break;
8515             }
8516             return this;
8517         },
8518
8519         /**
8520          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
8521          * @return {Roo.Element} this
8522          */
8523         clip : function(){
8524             if(!this.isClipped){
8525                this.isClipped = true;
8526                this.originalClip = {
8527                    "o": this.getStyle("overflow"),
8528                    "x": this.getStyle("overflow-x"),
8529                    "y": this.getStyle("overflow-y")
8530                };
8531                this.setStyle("overflow", "hidden");
8532                this.setStyle("overflow-x", "hidden");
8533                this.setStyle("overflow-y", "hidden");
8534             }
8535             return this;
8536         },
8537
8538         /**
8539          *  Return clipping (overflow) to original clipping before clip() was called
8540          * @return {Roo.Element} this
8541          */
8542         unclip : function(){
8543             if(this.isClipped){
8544                 this.isClipped = false;
8545                 var o = this.originalClip;
8546                 if(o.o){this.setStyle("overflow", o.o);}
8547                 if(o.x){this.setStyle("overflow-x", o.x);}
8548                 if(o.y){this.setStyle("overflow-y", o.y);}
8549             }
8550             return this;
8551         },
8552
8553
8554         /**
8555          * Gets the x,y coordinates specified by the anchor position on the element.
8556          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
8557          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
8558          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
8559          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
8560          * @return {Array} [x, y] An array containing the element's x and y coordinates
8561          */
8562         getAnchorXY : function(anchor, local, s){
8563             //Passing a different size is useful for pre-calculating anchors,
8564             //especially for anchored animations that change the el size.
8565
8566             var w, h, vp = false;
8567             if(!s){
8568                 var d = this.dom;
8569                 if(d == document.body || d == document){
8570                     vp = true;
8571                     w = D.getViewWidth(); h = D.getViewHeight();
8572                 }else{
8573                     w = this.getWidth(); h = this.getHeight();
8574                 }
8575             }else{
8576                 w = s.width;  h = s.height;
8577             }
8578             var x = 0, y = 0, r = Math.round;
8579             switch((anchor || "tl").toLowerCase()){
8580                 case "c":
8581                     x = r(w*.5);
8582                     y = r(h*.5);
8583                 break;
8584                 case "t":
8585                     x = r(w*.5);
8586                     y = 0;
8587                 break;
8588                 case "l":
8589                     x = 0;
8590                     y = r(h*.5);
8591                 break;
8592                 case "r":
8593                     x = w;
8594                     y = r(h*.5);
8595                 break;
8596                 case "b":
8597                     x = r(w*.5);
8598                     y = h;
8599                 break;
8600                 case "tl":
8601                     x = 0;
8602                     y = 0;
8603                 break;
8604                 case "bl":
8605                     x = 0;
8606                     y = h;
8607                 break;
8608                 case "br":
8609                     x = w;
8610                     y = h;
8611                 break;
8612                 case "tr":
8613                     x = w;
8614                     y = 0;
8615                 break;
8616             }
8617             if(local === true){
8618                 return [x, y];
8619             }
8620             if(vp){
8621                 var sc = this.getScroll();
8622                 return [x + sc.left, y + sc.top];
8623             }
8624             //Add the element's offset xy
8625             var o = this.getXY();
8626             return [x+o[0], y+o[1]];
8627         },
8628
8629         /**
8630          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
8631          * supported position values.
8632          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8633          * @param {String} position The position to align to.
8634          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8635          * @return {Array} [x, y]
8636          */
8637         getAlignToXY : function(el, p, o)
8638         {
8639             el = Roo.get(el);
8640             var d = this.dom;
8641             if(!el.dom){
8642                 throw "Element.alignTo with an element that doesn't exist";
8643             }
8644             var c = false; //constrain to viewport
8645             var p1 = "", p2 = "";
8646             o = o || [0,0];
8647
8648             if(!p){
8649                 p = "tl-bl";
8650             }else if(p == "?"){
8651                 p = "tl-bl?";
8652             }else if(p.indexOf("-") == -1){
8653                 p = "tl-" + p;
8654             }
8655             p = p.toLowerCase();
8656             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
8657             if(!m){
8658                throw "Element.alignTo with an invalid alignment " + p;
8659             }
8660             p1 = m[1]; p2 = m[2]; c = !!m[3];
8661
8662             //Subtract the aligned el's internal xy from the target's offset xy
8663             //plus custom offset to get the aligned el's new offset xy
8664             var a1 = this.getAnchorXY(p1, true);
8665             var a2 = el.getAnchorXY(p2, false);
8666             var x = a2[0] - a1[0] + o[0];
8667             var y = a2[1] - a1[1] + o[1];
8668             if(c){
8669                 //constrain the aligned el to viewport if necessary
8670                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
8671                 // 5px of margin for ie
8672                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
8673
8674                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
8675                 //perpendicular to the vp border, allow the aligned el to slide on that border,
8676                 //otherwise swap the aligned el to the opposite border of the target.
8677                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
8678                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
8679                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t")  );
8680                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
8681
8682                var doc = document;
8683                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
8684                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
8685
8686                if((x+w) > dw + scrollX){
8687                     x = swapX ? r.left-w : dw+scrollX-w;
8688                 }
8689                if(x < scrollX){
8690                    x = swapX ? r.right : scrollX;
8691                }
8692                if((y+h) > dh + scrollY){
8693                     y = swapY ? r.top-h : dh+scrollY-h;
8694                 }
8695                if (y < scrollY){
8696                    y = swapY ? r.bottom : scrollY;
8697                }
8698             }
8699             return [x,y];
8700         },
8701
8702         // private
8703         getConstrainToXY : function(){
8704             var os = {top:0, left:0, bottom:0, right: 0};
8705
8706             return function(el, local, offsets, proposedXY){
8707                 el = Roo.get(el);
8708                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
8709
8710                 var vw, vh, vx = 0, vy = 0;
8711                 if(el.dom == document.body || el.dom == document){
8712                     vw = Roo.lib.Dom.getViewWidth();
8713                     vh = Roo.lib.Dom.getViewHeight();
8714                 }else{
8715                     vw = el.dom.clientWidth;
8716                     vh = el.dom.clientHeight;
8717                     if(!local){
8718                         var vxy = el.getXY();
8719                         vx = vxy[0];
8720                         vy = vxy[1];
8721                     }
8722                 }
8723
8724                 var s = el.getScroll();
8725
8726                 vx += offsets.left + s.left;
8727                 vy += offsets.top + s.top;
8728
8729                 vw -= offsets.right;
8730                 vh -= offsets.bottom;
8731
8732                 var vr = vx+vw;
8733                 var vb = vy+vh;
8734
8735                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
8736                 var x = xy[0], y = xy[1];
8737                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
8738
8739                 // only move it if it needs it
8740                 var moved = false;
8741
8742                 // first validate right/bottom
8743                 if((x + w) > vr){
8744                     x = vr - w;
8745                     moved = true;
8746                 }
8747                 if((y + h) > vb){
8748                     y = vb - h;
8749                     moved = true;
8750                 }
8751                 // then make sure top/left isn't negative
8752                 if(x < vx){
8753                     x = vx;
8754                     moved = true;
8755                 }
8756                 if(y < vy){
8757                     y = vy;
8758                     moved = true;
8759                 }
8760                 return moved ? [x, y] : false;
8761             };
8762         }(),
8763
8764         // private
8765         adjustForConstraints : function(xy, parent, offsets){
8766             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
8767         },
8768
8769         /**
8770          * Aligns this element with another element relative to the specified anchor points. If the other element is the
8771          * document it aligns it to the viewport.
8772          * The position parameter is optional, and can be specified in any one of the following formats:
8773          * <ul>
8774          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
8775          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
8776          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
8777          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
8778          *   <li><b>Two anchors</b>: If two values from the table below are passed separated by a dash, the first value is used as the
8779          *       element's anchor point, and the second value is used as the target's anchor point.</li>
8780          * </ul>
8781          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
8782          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
8783          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
8784          * that specified in order to enforce the viewport constraints.
8785          * Following are all of the supported anchor positions:
8786     <pre>
8787     Value  Description
8788     -----  -----------------------------
8789     tl     The top left corner (default)
8790     t      The center of the top edge
8791     tr     The top right corner
8792     l      The center of the left edge
8793     c      In the center of the element
8794     r      The center of the right edge
8795     bl     The bottom left corner
8796     b      The center of the bottom edge
8797     br     The bottom right corner
8798     </pre>
8799     Example Usage:
8800     <pre><code>
8801     // align el to other-el using the default positioning ("tl-bl", non-constrained)
8802     el.alignTo("other-el");
8803
8804     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
8805     el.alignTo("other-el", "tr?");
8806
8807     // align the bottom right corner of el with the center left edge of other-el
8808     el.alignTo("other-el", "br-l?");
8809
8810     // align the center of el with the bottom left corner of other-el and
8811     // adjust the x position by -6 pixels (and the y position by 0)
8812     el.alignTo("other-el", "c-bl", [-6, 0]);
8813     </code></pre>
8814          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8815          * @param {String} position The position to align to.
8816          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8817          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8818          * @return {Roo.Element} this
8819          */
8820         alignTo : function(element, position, offsets, animate){
8821             var xy = this.getAlignToXY(element, position, offsets);
8822             this.setXY(xy, this.preanim(arguments, 3));
8823             return this;
8824         },
8825
8826         /**
8827          * Anchors an element to another element and realigns it when the window is resized.
8828          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8829          * @param {String} position The position to align to.
8830          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8831          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
8832          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
8833          * is a number, it is used as the buffer delay (defaults to 50ms).
8834          * @param {Function} callback The function to call after the animation finishes
8835          * @return {Roo.Element} this
8836          */
8837         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
8838             var action = function(){
8839                 this.alignTo(el, alignment, offsets, animate);
8840                 Roo.callback(callback, this);
8841             };
8842             Roo.EventManager.onWindowResize(action, this);
8843             var tm = typeof monitorScroll;
8844             if(tm != 'undefined'){
8845                 Roo.EventManager.on(window, 'scroll', action, this,
8846                     {buffer: tm == 'number' ? monitorScroll : 50});
8847             }
8848             action.call(this); // align immediately
8849             return this;
8850         },
8851         /**
8852          * Clears any opacity settings from this element. Required in some cases for IE.
8853          * @return {Roo.Element} this
8854          */
8855         clearOpacity : function(){
8856             if (window.ActiveXObject) {
8857                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
8858                     this.dom.style.filter = "";
8859                 }
8860             } else {
8861                 this.dom.style.opacity = "";
8862                 this.dom.style["-moz-opacity"] = "";
8863                 this.dom.style["-khtml-opacity"] = "";
8864             }
8865             return this;
8866         },
8867
8868         /**
8869          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8870          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8871          * @return {Roo.Element} this
8872          */
8873         hide : function(animate){
8874             this.setVisible(false, this.preanim(arguments, 0));
8875             return this;
8876         },
8877
8878         /**
8879         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8880         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8881          * @return {Roo.Element} this
8882          */
8883         show : function(animate){
8884             this.setVisible(true, this.preanim(arguments, 0));
8885             return this;
8886         },
8887
8888         /**
8889          * @private Test if size has a unit, otherwise appends the default
8890          */
8891         addUnits : function(size){
8892             return Roo.Element.addUnits(size, this.defaultUnit);
8893         },
8894
8895         /**
8896          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
8897          * @return {Roo.Element} this
8898          */
8899         beginMeasure : function(){
8900             var el = this.dom;
8901             if(el.offsetWidth || el.offsetHeight){
8902                 return this; // offsets work already
8903             }
8904             var changed = [];
8905             var p = this.dom, b = document.body; // start with this element
8906             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
8907                 var pe = Roo.get(p);
8908                 if(pe.getStyle('display') == 'none'){
8909                     changed.push({el: p, visibility: pe.getStyle("visibility")});
8910                     p.style.visibility = "hidden";
8911                     p.style.display = "block";
8912                 }
8913                 p = p.parentNode;
8914             }
8915             this._measureChanged = changed;
8916             return this;
8917
8918         },
8919
8920         /**
8921          * Restores displays to before beginMeasure was called
8922          * @return {Roo.Element} this
8923          */
8924         endMeasure : function(){
8925             var changed = this._measureChanged;
8926             if(changed){
8927                 for(var i = 0, len = changed.length; i < len; i++) {
8928                     var r = changed[i];
8929                     r.el.style.visibility = r.visibility;
8930                     r.el.style.display = "none";
8931                 }
8932                 this._measureChanged = null;
8933             }
8934             return this;
8935         },
8936
8937         /**
8938         * Update the innerHTML of this element, optionally searching for and processing scripts
8939         * @param {String} html The new HTML
8940         * @param {Boolean} loadScripts (optional) true to look for and process scripts
8941         * @param {Function} callback For async script loading you can be noticed when the update completes
8942         * @return {Roo.Element} this
8943          */
8944         update : function(html, loadScripts, callback){
8945             if(typeof html == "undefined"){
8946                 html = "";
8947             }
8948             if(loadScripts !== true){
8949                 this.dom.innerHTML = html;
8950                 if(typeof callback == "function"){
8951                     callback();
8952                 }
8953                 return this;
8954             }
8955             var id = Roo.id();
8956             var dom = this.dom;
8957
8958             html += '<span id="' + id + '"></span>';
8959
8960             E.onAvailable(id, function(){
8961                 var hd = document.getElementsByTagName("head")[0];
8962                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
8963                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
8964                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
8965
8966                 var match;
8967                 while(match = re.exec(html)){
8968                     var attrs = match[1];
8969                     var srcMatch = attrs ? attrs.match(srcRe) : false;
8970                     if(srcMatch && srcMatch[2]){
8971                        var s = document.createElement("script");
8972                        s.src = srcMatch[2];
8973                        var typeMatch = attrs.match(typeRe);
8974                        if(typeMatch && typeMatch[2]){
8975                            s.type = typeMatch[2];
8976                        }
8977                        hd.appendChild(s);
8978                     }else if(match[2] && match[2].length > 0){
8979                         if(window.execScript) {
8980                            window.execScript(match[2]);
8981                         } else {
8982                             /**
8983                              * eval:var:id
8984                              * eval:var:dom
8985                              * eval:var:html
8986                              * 
8987                              */
8988                            window.eval(match[2]);
8989                         }
8990                     }
8991                 }
8992                 var el = document.getElementById(id);
8993                 if(el){el.parentNode.removeChild(el);}
8994                 if(typeof callback == "function"){
8995                     callback();
8996                 }
8997             });
8998             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
8999             return this;
9000         },
9001
9002         /**
9003          * Direct access to the UpdateManager update() method (takes the same parameters).
9004          * @param {String/Function} url The url for this request or a function to call to get the url
9005          * @param {String/Object} params (optional) The parameters to pass as either a url encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
9006          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
9007          * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used url. If true, it will not store the url.
9008          * @return {Roo.Element} this
9009          */
9010         load : function(){
9011             var um = this.getUpdateManager();
9012             um.update.apply(um, arguments);
9013             return this;
9014         },
9015
9016         /**
9017         * Gets this element's UpdateManager
9018         * @return {Roo.UpdateManager} The UpdateManager
9019         */
9020         getUpdateManager : function(){
9021             if(!this.updateManager){
9022                 this.updateManager = new Roo.UpdateManager(this);
9023             }
9024             return this.updateManager;
9025         },
9026
9027         /**
9028          * Disables text selection for this element (normalized across browsers)
9029          * @return {Roo.Element} this
9030          */
9031         unselectable : function(){
9032             this.dom.unselectable = "on";
9033             this.swallowEvent("selectstart", true);
9034             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
9035             this.addClass("x-unselectable");
9036             return this;
9037         },
9038
9039         /**
9040         * Calculates the x, y to center this element on the screen
9041         * @return {Array} The x, y values [x, y]
9042         */
9043         getCenterXY : function(){
9044             return this.getAlignToXY(document, 'c-c');
9045         },
9046
9047         /**
9048         * Centers the Element in either the viewport, or another Element.
9049         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
9050         */
9051         center : function(centerIn){
9052             this.alignTo(centerIn || document, 'c-c');
9053             return this;
9054         },
9055
9056         /**
9057          * Tests various css rules/browsers to determine if this element uses a border box
9058          * @return {Boolean}
9059          */
9060         isBorderBox : function(){
9061             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
9062         },
9063
9064         /**
9065          * Return a box {x, y, width, height} that can be used to set another elements
9066          * size/location to match this element.
9067          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
9068          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
9069          * @return {Object} box An object in the format {x, y, width, height}
9070          */
9071         getBox : function(contentBox, local){
9072             var xy;
9073             if(!local){
9074                 xy = this.getXY();
9075             }else{
9076                 var left = parseInt(this.getStyle("left"), 10) || 0;
9077                 var top = parseInt(this.getStyle("top"), 10) || 0;
9078                 xy = [left, top];
9079             }
9080             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
9081             if(!contentBox){
9082                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
9083             }else{
9084                 var l = this.getBorderWidth("l")+this.getPadding("l");
9085                 var r = this.getBorderWidth("r")+this.getPadding("r");
9086                 var t = this.getBorderWidth("t")+this.getPadding("t");
9087                 var b = this.getBorderWidth("b")+this.getPadding("b");
9088                 bx = {x: xy[0]+l, y: xy[1]+t, 0: xy[0]+l, 1: xy[1]+t, width: w-(l+r), height: h-(t+b)};
9089             }
9090             bx.right = bx.x + bx.width;
9091             bx.bottom = bx.y + bx.height;
9092             return bx;
9093         },
9094
9095         /**
9096          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
9097          for more information about the sides.
9098          * @param {String} sides
9099          * @return {Number}
9100          */
9101         getFrameWidth : function(sides, onlyContentBox){
9102             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
9103         },
9104
9105         /**
9106          * Sets the element's box. Use getBox() on another element to get a box obj. If animate is true then width, height, x and y will be animated concurrently.
9107          * @param {Object} box The box to fill {x, y, width, height}
9108          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
9109          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9110          * @return {Roo.Element} this
9111          */
9112         setBox : function(box, adjust, animate){
9113             var w = box.width, h = box.height;
9114             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
9115                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
9116                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
9117             }
9118             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
9119             return this;
9120         },
9121
9122         /**
9123          * Forces the browser to repaint this element
9124          * @return {Roo.Element} this
9125          */
9126          repaint : function(){
9127             var dom = this.dom;
9128             this.addClass("x-repaint");
9129             setTimeout(function(){
9130                 Roo.get(dom).removeClass("x-repaint");
9131             }, 1);
9132             return this;
9133         },
9134
9135         /**
9136          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
9137          * then it returns the calculated width of the sides (see getPadding)
9138          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
9139          * @return {Object/Number}
9140          */
9141         getMargins : function(side){
9142             if(!side){
9143                 return {
9144                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
9145                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
9146                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
9147                     right: parseInt(this.getStyle("margin-right"), 10) || 0
9148                 };
9149             }else{
9150                 return this.addStyles(side, El.margins);
9151              }
9152         },
9153
9154         // private
9155         addStyles : function(sides, styles){
9156             var val = 0, v, w;
9157             for(var i = 0, len = sides.length; i < len; i++){
9158                 v = this.getStyle(styles[sides.charAt(i)]);
9159                 if(v){
9160                      w = parseInt(v, 10);
9161                      if(w){ val += w; }
9162                 }
9163             }
9164             return val;
9165         },
9166
9167         /**
9168          * Creates a proxy element of this element
9169          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
9170          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
9171          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
9172          * @return {Roo.Element} The new proxy element
9173          */
9174         createProxy : function(config, renderTo, matchBox){
9175             if(renderTo){
9176                 renderTo = Roo.getDom(renderTo);
9177             }else{
9178                 renderTo = document.body;
9179             }
9180             config = typeof config == "object" ?
9181                 config : {tag : "div", cls: config};
9182             var proxy = Roo.DomHelper.append(renderTo, config, true);
9183             if(matchBox){
9184                proxy.setBox(this.getBox());
9185             }
9186             return proxy;
9187         },
9188
9189         /**
9190          * Puts a mask over this element to disable user interaction. Requires core.css.
9191          * This method can only be applied to elements which accept child nodes.
9192          * @param {String} msg (optional) A message to display in the mask
9193          * @param {String} msgCls (optional) A css class to apply to the msg element
9194          * @return {Element} The mask  element
9195          */
9196         mask : function(msg, msgCls)
9197         {
9198             if(this.getStyle("position") == "static" && this.dom.tagName !== 'BODY'){
9199                 this.setStyle("position", "relative");
9200             }
9201             if(!this._mask){
9202                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
9203             }
9204             
9205             this.addClass("x-masked");
9206             this._mask.setDisplayed(true);
9207             
9208             // we wander
9209             var z = 0;
9210             var dom = this.dom;
9211             while (dom && dom.style) {
9212                 if (!isNaN(parseInt(dom.style.zIndex))) {
9213                     z = Math.max(z, parseInt(dom.style.zIndex));
9214                 }
9215                 dom = dom.parentNode;
9216             }
9217             // if we are masking the body - then it hides everything..
9218             if (this.dom == document.body) {
9219                 z = 1000000;
9220                 this._mask.setWidth(Roo.lib.Dom.getDocumentWidth());
9221                 this._mask.setHeight(Roo.lib.Dom.getDocumentHeight());
9222             }
9223            
9224             if(typeof msg == 'string'){
9225                 if(!this._maskMsg){
9226                     this._maskMsg = Roo.DomHelper.append(this.dom, {
9227                         cls: "roo-el-mask-msg", 
9228                         cn: [
9229                             {
9230                                 tag: 'i',
9231                                 cls: 'fa fa-spinner fa-spin'
9232                             },
9233                             {
9234                                 tag: 'div'
9235                             }   
9236                         ]
9237                     }, true);
9238                 }
9239                 var mm = this._maskMsg;
9240                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
9241                 if (mm.dom.lastChild) { // weird IE issue?
9242                     mm.dom.lastChild.innerHTML = msg;
9243                 }
9244                 mm.setDisplayed(true);
9245                 mm.center(this);
9246                 mm.setStyle('z-index', z + 102);
9247             }
9248             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
9249                 this._mask.setHeight(this.getHeight());
9250             }
9251             this._mask.setStyle('z-index', z + 100);
9252             
9253             return this._mask;
9254         },
9255
9256         /**
9257          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
9258          * it is cached for reuse.
9259          */
9260         unmask : function(removeEl){
9261             if(this._mask){
9262                 if(removeEl === true){
9263                     this._mask.remove();
9264                     delete this._mask;
9265                     if(this._maskMsg){
9266                         this._maskMsg.remove();
9267                         delete this._maskMsg;
9268                     }
9269                 }else{
9270                     this._mask.setDisplayed(false);
9271                     if(this._maskMsg){
9272                         this._maskMsg.setDisplayed(false);
9273                     }
9274                 }
9275             }
9276             this.removeClass("x-masked");
9277         },
9278
9279         /**
9280          * Returns true if this element is masked
9281          * @return {Boolean}
9282          */
9283         isMasked : function(){
9284             return this._mask && this._mask.isVisible();
9285         },
9286
9287         /**
9288          * Creates an iframe shim for this element to keep selects and other windowed objects from
9289          * showing through.
9290          * @return {Roo.Element} The new shim element
9291          */
9292         createShim : function(){
9293             var el = document.createElement('iframe');
9294             el.frameBorder = 'no';
9295             el.className = 'roo-shim';
9296             if(Roo.isIE && Roo.isSecure){
9297                 el.src = Roo.SSL_SECURE_URL;
9298             }
9299             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
9300             shim.autoBoxAdjust = false;
9301             return shim;
9302         },
9303
9304         /**
9305          * Removes this element from the DOM and deletes it from the cache
9306          */
9307         remove : function(){
9308             if(this.dom.parentNode){
9309                 this.dom.parentNode.removeChild(this.dom);
9310             }
9311             delete El.cache[this.dom.id];
9312         },
9313
9314         /**
9315          * Sets up event handlers to add and remove a css class when the mouse is over this element
9316          * @param {String} className
9317          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
9318          * mouseout events for children elements
9319          * @return {Roo.Element} this
9320          */
9321         addClassOnOver : function(className, preventFlicker){
9322             this.on("mouseover", function(){
9323                 Roo.fly(this, '_internal').addClass(className);
9324             }, this.dom);
9325             var removeFn = function(e){
9326                 if(preventFlicker !== true || !e.within(this, true)){
9327                     Roo.fly(this, '_internal').removeClass(className);
9328                 }
9329             };
9330             this.on("mouseout", removeFn, this.dom);
9331             return this;
9332         },
9333
9334         /**
9335          * Sets up event handlers to add and remove a css class when this element has the focus
9336          * @param {String} className
9337          * @return {Roo.Element} this
9338          */
9339         addClassOnFocus : function(className){
9340             this.on("focus", function(){
9341                 Roo.fly(this, '_internal').addClass(className);
9342             }, this.dom);
9343             this.on("blur", function(){
9344                 Roo.fly(this, '_internal').removeClass(className);
9345             }, this.dom);
9346             return this;
9347         },
9348         /**
9349          * Sets up event handlers to add and remove a css class when the mouse is down and then up on this element (a click effect)
9350          * @param {String} className
9351          * @return {Roo.Element} this
9352          */
9353         addClassOnClick : function(className){
9354             var dom = this.dom;
9355             this.on("mousedown", function(){
9356                 Roo.fly(dom, '_internal').addClass(className);
9357                 var d = Roo.get(document);
9358                 var fn = function(){
9359                     Roo.fly(dom, '_internal').removeClass(className);
9360                     d.removeListener("mouseup", fn);
9361                 };
9362                 d.on("mouseup", fn);
9363             });
9364             return this;
9365         },
9366
9367         /**
9368          * Stops the specified event from bubbling and optionally prevents the default action
9369          * @param {String} eventName
9370          * @param {Boolean} preventDefault (optional) true to prevent the default action too
9371          * @return {Roo.Element} this
9372          */
9373         swallowEvent : function(eventName, preventDefault){
9374             var fn = function(e){
9375                 e.stopPropagation();
9376                 if(preventDefault){
9377                     e.preventDefault();
9378                 }
9379             };
9380             if(eventName instanceof Array){
9381                 for(var i = 0, len = eventName.length; i < len; i++){
9382                      this.on(eventName[i], fn);
9383                 }
9384                 return this;
9385             }
9386             this.on(eventName, fn);
9387             return this;
9388         },
9389
9390         /**
9391          * @private
9392          */
9393         fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
9394
9395         /**
9396          * Sizes this element to its parent element's dimensions performing
9397          * neccessary box adjustments.
9398          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
9399          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
9400          * @return {Roo.Element} this
9401          */
9402         fitToParent : function(monitorResize, targetParent) {
9403           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
9404           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
9405           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
9406             return;
9407           }
9408           var p = Roo.get(targetParent || this.dom.parentNode);
9409           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
9410           if (monitorResize === true) {
9411             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
9412             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
9413           }
9414           return this;
9415         },
9416
9417         /**
9418          * Gets the next sibling, skipping text nodes
9419          * @return {HTMLElement} The next sibling or null
9420          */
9421         getNextSibling : function(){
9422             var n = this.dom.nextSibling;
9423             while(n && n.nodeType != 1){
9424                 n = n.nextSibling;
9425             }
9426             return n;
9427         },
9428
9429         /**
9430          * Gets the previous sibling, skipping text nodes
9431          * @return {HTMLElement} The previous sibling or null
9432          */
9433         getPrevSibling : function(){
9434             var n = this.dom.previousSibling;
9435             while(n && n.nodeType != 1){
9436                 n = n.previousSibling;
9437             }
9438             return n;
9439         },
9440
9441
9442         /**
9443          * Appends the passed element(s) to this element
9444          * @param {String/HTMLElement/Array/Element/CompositeElement} el
9445          * @return {Roo.Element} this
9446          */
9447         appendChild: function(el){
9448             el = Roo.get(el);
9449             el.appendTo(this);
9450             return this;
9451         },
9452
9453         /**
9454          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
9455          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
9456          * automatically generated with the specified attributes.
9457          * @param {HTMLElement} insertBefore (optional) a child element of this element
9458          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
9459          * @return {Roo.Element} The new child element
9460          */
9461         createChild: function(config, insertBefore, returnDom){
9462             config = config || {tag:'div'};
9463             if(insertBefore){
9464                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
9465             }
9466             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
9467         },
9468
9469         /**
9470          * Appends this element to the passed element
9471          * @param {String/HTMLElement/Element} el The new parent element
9472          * @return {Roo.Element} this
9473          */
9474         appendTo: function(el){
9475             el = Roo.getDom(el);
9476             el.appendChild(this.dom);
9477             return this;
9478         },
9479
9480         /**
9481          * Inserts this element before the passed element in the DOM
9482          * @param {String/HTMLElement/Element} el The element to insert before
9483          * @return {Roo.Element} this
9484          */
9485         insertBefore: function(el){
9486             el = Roo.getDom(el);
9487             el.parentNode.insertBefore(this.dom, el);
9488             return this;
9489         },
9490
9491         /**
9492          * Inserts this element after the passed element in the DOM
9493          * @param {String/HTMLElement/Element} el The element to insert after
9494          * @return {Roo.Element} this
9495          */
9496         insertAfter: function(el){
9497             el = Roo.getDom(el);
9498             el.parentNode.insertBefore(this.dom, el.nextSibling);
9499             return this;
9500         },
9501
9502         /**
9503          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
9504          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9505          * @return {Roo.Element} The new child
9506          */
9507         insertFirst: function(el, returnDom){
9508             el = el || {};
9509             if(typeof el == 'object' && !el.nodeType){ // dh config
9510                 return this.createChild(el, this.dom.firstChild, returnDom);
9511             }else{
9512                 el = Roo.getDom(el);
9513                 this.dom.insertBefore(el, this.dom.firstChild);
9514                 return !returnDom ? Roo.get(el) : el;
9515             }
9516         },
9517
9518         /**
9519          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
9520          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9521          * @param {String} where (optional) 'before' or 'after' defaults to before
9522          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9523          * @return {Roo.Element} the inserted Element
9524          */
9525         insertSibling: function(el, where, returnDom){
9526             where = where ? where.toLowerCase() : 'before';
9527             el = el || {};
9528             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
9529
9530             if(typeof el == 'object' && !el.nodeType){ // dh config
9531                 if(where == 'after' && !this.dom.nextSibling){
9532                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
9533                 }else{
9534                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
9535                 }
9536
9537             }else{
9538                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
9539                             where == 'before' ? this.dom : this.dom.nextSibling);
9540                 if(!returnDom){
9541                     rt = Roo.get(rt);
9542                 }
9543             }
9544             return rt;
9545         },
9546
9547         /**
9548          * Creates and wraps this element with another element
9549          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
9550          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9551          * @return {HTMLElement/Element} The newly created wrapper element
9552          */
9553         wrap: function(config, returnDom){
9554             if(!config){
9555                 config = {tag: "div"};
9556             }
9557             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
9558             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
9559             return newEl;
9560         },
9561
9562         /**
9563          * Replaces the passed element with this element
9564          * @param {String/HTMLElement/Element} el The element to replace
9565          * @return {Roo.Element} this
9566          */
9567         replace: function(el){
9568             el = Roo.get(el);
9569             this.insertBefore(el);
9570             el.remove();
9571             return this;
9572         },
9573
9574         /**
9575          * Inserts an html fragment into this element
9576          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
9577          * @param {String} html The HTML fragment
9578          * @param {Boolean} returnEl True to return an Roo.Element
9579          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
9580          */
9581         insertHtml : function(where, html, returnEl){
9582             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
9583             return returnEl ? Roo.get(el) : el;
9584         },
9585
9586         /**
9587          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
9588          * @param {Object} o The object with the attributes
9589          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
9590          * @return {Roo.Element} this
9591          */
9592         set : function(o, useSet){
9593             var el = this.dom;
9594             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
9595             for(var attr in o){
9596                 if(attr == "style" || typeof o[attr] == "function")  { continue; }
9597                 if(attr=="cls"){
9598                     el.className = o["cls"];
9599                 }else{
9600                     if(useSet) {
9601                         el.setAttribute(attr, o[attr]);
9602                     } else {
9603                         el[attr] = o[attr];
9604                     }
9605                 }
9606             }
9607             if(o.style){
9608                 Roo.DomHelper.applyStyles(el, o.style);
9609             }
9610             return this;
9611         },
9612
9613         /**
9614          * Convenience method for constructing a KeyMap
9615          * @param {Number/Array/Object/String} key Either a string with the keys to listen for, the numeric key code, array of key codes or an object with the following options:
9616          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9617          * @param {Function} fn The function to call
9618          * @param {Object} scope (optional) The scope of the function
9619          * @return {Roo.KeyMap} The KeyMap created
9620          */
9621         addKeyListener : function(key, fn, scope){
9622             var config;
9623             if(typeof key != "object" || key instanceof Array){
9624                 config = {
9625                     key: key,
9626                     fn: fn,
9627                     scope: scope
9628                 };
9629             }else{
9630                 config = {
9631                     key : key.key,
9632                     shift : key.shift,
9633                     ctrl : key.ctrl,
9634                     alt : key.alt,
9635                     fn: fn,
9636                     scope: scope
9637                 };
9638             }
9639             return new Roo.KeyMap(this, config);
9640         },
9641
9642         /**
9643          * Creates a KeyMap for this element
9644          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
9645          * @return {Roo.KeyMap} The KeyMap created
9646          */
9647         addKeyMap : function(config){
9648             return new Roo.KeyMap(this, config);
9649         },
9650
9651         /**
9652          * Returns true if this element is scrollable.
9653          * @return {Boolean}
9654          */
9655          isScrollable : function(){
9656             var dom = this.dom;
9657             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
9658         },
9659
9660         /**
9661          * Scrolls this element the specified scroll point. It does NOT do bounds checking so if you scroll to a weird value it will try to do it. For auto bounds checking, use scroll().
9662          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
9663          * @param {Number} value The new scroll value
9664          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9665          * @return {Element} this
9666          */
9667
9668         scrollTo : function(side, value, animate){
9669             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
9670             if(!animate || !A){
9671                 this.dom[prop] = value;
9672             }else{
9673                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
9674                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
9675             }
9676             return this;
9677         },
9678
9679         /**
9680          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
9681          * within this element's scrollable range.
9682          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9683          * @param {Number} distance How far to scroll the element in pixels
9684          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9685          * @return {Boolean} Returns true if a scroll was triggered or false if the element
9686          * was scrolled as far as it could go.
9687          */
9688          scroll : function(direction, distance, animate){
9689              if(!this.isScrollable()){
9690                  return;
9691              }
9692              var el = this.dom;
9693              var l = el.scrollLeft, t = el.scrollTop;
9694              var w = el.scrollWidth, h = el.scrollHeight;
9695              var cw = el.clientWidth, ch = el.clientHeight;
9696              direction = direction.toLowerCase();
9697              var scrolled = false;
9698              var a = this.preanim(arguments, 2);
9699              switch(direction){
9700                  case "l":
9701                  case "left":
9702                      if(w - l > cw){
9703                          var v = Math.min(l + distance, w-cw);
9704                          this.scrollTo("left", v, a);
9705                          scrolled = true;
9706                      }
9707                      break;
9708                 case "r":
9709                 case "right":
9710                      if(l > 0){
9711                          var v = Math.max(l - distance, 0);
9712                          this.scrollTo("left", v, a);
9713                          scrolled = true;
9714                      }
9715                      break;
9716                 case "t":
9717                 case "top":
9718                 case "up":
9719                      if(t > 0){
9720                          var v = Math.max(t - distance, 0);
9721                          this.scrollTo("top", v, a);
9722                          scrolled = true;
9723                      }
9724                      break;
9725                 case "b":
9726                 case "bottom":
9727                 case "down":
9728                      if(h - t > ch){
9729                          var v = Math.min(t + distance, h-ch);
9730                          this.scrollTo("top", v, a);
9731                          scrolled = true;
9732                      }
9733                      break;
9734              }
9735              return scrolled;
9736         },
9737
9738         /**
9739          * Translates the passed page coordinates into left/top css values for this element
9740          * @param {Number/Array} x The page x or an array containing [x, y]
9741          * @param {Number} y The page y
9742          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
9743          */
9744         translatePoints : function(x, y){
9745             if(typeof x == 'object' || x instanceof Array){
9746                 y = x[1]; x = x[0];
9747             }
9748             var p = this.getStyle('position');
9749             var o = this.getXY();
9750
9751             var l = parseInt(this.getStyle('left'), 10);
9752             var t = parseInt(this.getStyle('top'), 10);
9753
9754             if(isNaN(l)){
9755                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
9756             }
9757             if(isNaN(t)){
9758                 t = (p == "relative") ? 0 : this.dom.offsetTop;
9759             }
9760
9761             return {left: (x - o[0] + l), top: (y - o[1] + t)};
9762         },
9763
9764         /**
9765          * Returns the current scroll position of the element.
9766          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
9767          */
9768         getScroll : function(){
9769             var d = this.dom, doc = document;
9770             if(d == doc || d == doc.body){
9771                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
9772                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
9773                 return {left: l, top: t};
9774             }else{
9775                 return {left: d.scrollLeft, top: d.scrollTop};
9776             }
9777         },
9778
9779         /**
9780          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
9781          * are convert to standard 6 digit hex color.
9782          * @param {String} attr The css attribute
9783          * @param {String} defaultValue The default value to use when a valid color isn't found
9784          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
9785          * YUI color anims.
9786          */
9787         getColor : function(attr, defaultValue, prefix){
9788             var v = this.getStyle(attr);
9789             if(!v || v == "transparent" || v == "inherit") {
9790                 return defaultValue;
9791             }
9792             var color = typeof prefix == "undefined" ? "#" : prefix;
9793             if(v.substr(0, 4) == "rgb("){
9794                 var rvs = v.slice(4, v.length -1).split(",");
9795                 for(var i = 0; i < 3; i++){
9796                     var h = parseInt(rvs[i]).toString(16);
9797                     if(h < 16){
9798                         h = "0" + h;
9799                     }
9800                     color += h;
9801                 }
9802             } else {
9803                 if(v.substr(0, 1) == "#"){
9804                     if(v.length == 4) {
9805                         for(var i = 1; i < 4; i++){
9806                             var c = v.charAt(i);
9807                             color +=  c + c;
9808                         }
9809                     }else if(v.length == 7){
9810                         color += v.substr(1);
9811                     }
9812                 }
9813             }
9814             return(color.length > 5 ? color.toLowerCase() : defaultValue);
9815         },
9816
9817         /**
9818          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
9819          * gradient background, rounded corners and a 4-way shadow.
9820          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
9821          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
9822          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
9823          * @return {Roo.Element} this
9824          */
9825         boxWrap : function(cls){
9826             cls = cls || 'x-box';
9827             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
9828             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
9829             return el;
9830         },
9831
9832         /**
9833          * Returns the value of a namespaced attribute from the element's underlying DOM node.
9834          * @param {String} namespace The namespace in which to look for the attribute
9835          * @param {String} name The attribute name
9836          * @return {String} The attribute value
9837          */
9838         getAttributeNS : Roo.isIE ? function(ns, name){
9839             var d = this.dom;
9840             var type = typeof d[ns+":"+name];
9841             if(type != 'undefined' && type != 'unknown'){
9842                 return d[ns+":"+name];
9843             }
9844             return d[name];
9845         } : function(ns, name){
9846             var d = this.dom;
9847             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
9848         },
9849         
9850         
9851         /**
9852          * Sets or Returns the value the dom attribute value
9853          * @param {String|Object} name The attribute name (or object to set multiple attributes)
9854          * @param {String} value (optional) The value to set the attribute to
9855          * @return {String} The attribute value
9856          */
9857         attr : function(name){
9858             if (arguments.length > 1) {
9859                 this.dom.setAttribute(name, arguments[1]);
9860                 return arguments[1];
9861             }
9862             if (typeof(name) == 'object') {
9863                 for(var i in name) {
9864                     this.attr(i, name[i]);
9865                 }
9866                 return name;
9867             }
9868             
9869             
9870             if (!this.dom.hasAttribute(name)) {
9871                 return undefined;
9872             }
9873             return this.dom.getAttribute(name);
9874         }
9875         
9876         
9877         
9878     };
9879
9880     var ep = El.prototype;
9881
9882     /**
9883      * Appends an event handler (Shorthand for addListener)
9884      * @param {String}   eventName     The type of event to append
9885      * @param {Function} fn        The method the event invokes
9886      * @param {Object} scope       (optional) The scope (this object) of the fn
9887      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9888      * @method
9889      */
9890     ep.on = ep.addListener;
9891         // backwards compat
9892     ep.mon = ep.addListener;
9893
9894     /**
9895      * Removes an event handler from this element (shorthand for removeListener)
9896      * @param {String} eventName the type of event to remove
9897      * @param {Function} fn the method the event invokes
9898      * @return {Roo.Element} this
9899      * @method
9900      */
9901     ep.un = ep.removeListener;
9902
9903     /**
9904      * true to automatically adjust width and height settings for box-model issues (default to true)
9905      */
9906     ep.autoBoxAdjust = true;
9907
9908     // private
9909     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
9910
9911     // private
9912     El.addUnits = function(v, defaultUnit){
9913         if(v === "" || v == "auto"){
9914             return v;
9915         }
9916         if(v === undefined){
9917             return '';
9918         }
9919         if(typeof v == "number" || !El.unitPattern.test(v)){
9920             return v + (defaultUnit || 'px');
9921         }
9922         return v;
9923     };
9924
9925     // special markup used throughout Roo when box wrapping elements
9926     El.boxMarkup = '<div class="{0}-tl"><div class="{0}-tr"><div class="{0}-tc"></div></div></div><div class="{0}-ml"><div class="{0}-mr"><div class="{0}-mc"></div></div></div><div class="{0}-bl"><div class="{0}-br"><div class="{0}-bc"></div></div></div>';
9927     /**
9928      * Visibility mode constant - Use visibility to hide element
9929      * @static
9930      * @type Number
9931      */
9932     El.VISIBILITY = 1;
9933     /**
9934      * Visibility mode constant - Use display to hide element
9935      * @static
9936      * @type Number
9937      */
9938     El.DISPLAY = 2;
9939
9940     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
9941     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
9942     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
9943
9944
9945
9946     /**
9947      * @private
9948      */
9949     El.cache = {};
9950
9951     var docEl;
9952
9953     /**
9954      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9955      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9956      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9957      * @return {Element} The Element object
9958      * @static
9959      */
9960     El.get = function(el){
9961         var ex, elm, id;
9962         if(!el){ return null; }
9963         if(typeof el == "string"){ // element id
9964             if(!(elm = document.getElementById(el))){
9965                 return null;
9966             }
9967             if(ex = El.cache[el]){
9968                 ex.dom = elm;
9969             }else{
9970                 ex = El.cache[el] = new El(elm);
9971             }
9972             return ex;
9973         }else if(el.tagName){ // dom element
9974             if(!(id = el.id)){
9975                 id = Roo.id(el);
9976             }
9977             if(ex = El.cache[id]){
9978                 ex.dom = el;
9979             }else{
9980                 ex = El.cache[id] = new El(el);
9981             }
9982             return ex;
9983         }else if(el instanceof El){
9984             if(el != docEl){
9985                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
9986                                                               // catch case where it hasn't been appended
9987                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
9988             }
9989             return el;
9990         }else if(el.isComposite){
9991             return el;
9992         }else if(el instanceof Array){
9993             return El.select(el);
9994         }else if(el == document){
9995             // create a bogus element object representing the document object
9996             if(!docEl){
9997                 var f = function(){};
9998                 f.prototype = El.prototype;
9999                 docEl = new f();
10000                 docEl.dom = document;
10001             }
10002             return docEl;
10003         }
10004         return null;
10005     };
10006
10007     // private
10008     El.uncache = function(el){
10009         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
10010             if(a[i]){
10011                 delete El.cache[a[i].id || a[i]];
10012             }
10013         }
10014     };
10015
10016     // private
10017     // Garbage collection - uncache elements/purge listeners on orphaned elements
10018     // so we don't hold a reference and cause the browser to retain them
10019     El.garbageCollect = function(){
10020         if(!Roo.enableGarbageCollector){
10021             clearInterval(El.collectorThread);
10022             return;
10023         }
10024         for(var eid in El.cache){
10025             var el = El.cache[eid], d = el.dom;
10026             // -------------------------------------------------------
10027             // Determining what is garbage:
10028             // -------------------------------------------------------
10029             // !d
10030             // dom node is null, definitely garbage
10031             // -------------------------------------------------------
10032             // !d.parentNode
10033             // no parentNode == direct orphan, definitely garbage
10034             // -------------------------------------------------------
10035             // !d.offsetParent && !document.getElementById(eid)
10036             // display none elements have no offsetParent so we will
10037             // also try to look it up by it's id. However, check
10038             // offsetParent first so we don't do unneeded lookups.
10039             // This enables collection of elements that are not orphans
10040             // directly, but somewhere up the line they have an orphan
10041             // parent.
10042             // -------------------------------------------------------
10043             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
10044                 delete El.cache[eid];
10045                 if(d && Roo.enableListenerCollection){
10046                     E.purgeElement(d);
10047                 }
10048             }
10049         }
10050     }
10051     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
10052
10053
10054     // dom is optional
10055     El.Flyweight = function(dom){
10056         this.dom = dom;
10057     };
10058     El.Flyweight.prototype = El.prototype;
10059
10060     El._flyweights = {};
10061     /**
10062      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
10063      * the dom node can be overwritten by other code.
10064      * @param {String/HTMLElement} el The dom node or id
10065      * @param {String} named (optional) Allows for creation of named reusable flyweights to
10066      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
10067      * @static
10068      * @return {Element} The shared Element object
10069      */
10070     El.fly = function(el, named){
10071         named = named || '_global';
10072         el = Roo.getDom(el);
10073         if(!el){
10074             return null;
10075         }
10076         if(!El._flyweights[named]){
10077             El._flyweights[named] = new El.Flyweight();
10078         }
10079         El._flyweights[named].dom = el;
10080         return El._flyweights[named];
10081     };
10082
10083     /**
10084      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
10085      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
10086      * Shorthand of {@link Roo.Element#get}
10087      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
10088      * @return {Element} The Element object
10089      * @member Roo
10090      * @method get
10091      */
10092     Roo.get = El.get;
10093     /**
10094      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
10095      * the dom node can be overwritten by other code.
10096      * Shorthand of {@link Roo.Element#fly}
10097      * @param {String/HTMLElement} el The dom node or id
10098      * @param {String} named (optional) Allows for creation of named reusable flyweights to
10099      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
10100      * @static
10101      * @return {Element} The shared Element object
10102      * @member Roo
10103      * @method fly
10104      */
10105     Roo.fly = El.fly;
10106
10107     // speedy lookup for elements never to box adjust
10108     var noBoxAdjust = Roo.isStrict ? {
10109         select:1
10110     } : {
10111         input:1, select:1, textarea:1
10112     };
10113     if(Roo.isIE || Roo.isGecko){
10114         noBoxAdjust['button'] = 1;
10115     }
10116
10117
10118     Roo.EventManager.on(window, 'unload', function(){
10119         delete El.cache;
10120         delete El._flyweights;
10121     });
10122 })();
10123
10124
10125
10126
10127 if(Roo.DomQuery){
10128     Roo.Element.selectorFunction = Roo.DomQuery.select;
10129 }
10130
10131 Roo.Element.select = function(selector, unique, root){
10132     var els;
10133     if(typeof selector == "string"){
10134         els = Roo.Element.selectorFunction(selector, root);
10135     }else if(selector.length !== undefined){
10136         els = selector;
10137     }else{
10138         throw "Invalid selector";
10139     }
10140     if(unique === true){
10141         return new Roo.CompositeElement(els);
10142     }else{
10143         return new Roo.CompositeElementLite(els);
10144     }
10145 };
10146 /**
10147  * Selects elements based on the passed CSS selector to enable working on them as 1.
10148  * @param {String/Array} selector The CSS selector or an array of elements
10149  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
10150  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
10151  * @return {CompositeElementLite/CompositeElement}
10152  * @member Roo
10153  * @method select
10154  */
10155 Roo.select = Roo.Element.select;
10156
10157
10158
10159
10160
10161
10162
10163
10164
10165
10166
10167
10168
10169
10170 /*
10171  * Based on:
10172  * Ext JS Library 1.1.1
10173  * Copyright(c) 2006-2007, Ext JS, LLC.
10174  *
10175  * Originally Released Under LGPL - original licence link has changed is not relivant.
10176  *
10177  * Fork - LGPL
10178  * <script type="text/javascript">
10179  */
10180
10181
10182
10183 //Notifies Element that fx methods are available
10184 Roo.enableFx = true;
10185
10186 /**
10187  * @class Roo.Fx
10188  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
10189  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
10190  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
10191  * Element effects to work.</p><br/>
10192  *
10193  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
10194  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
10195  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
10196  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
10197  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
10198  * expected results and should be done with care.</p><br/>
10199  *
10200  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
10201  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
10202 <pre>
10203 Value  Description
10204 -----  -----------------------------
10205 tl     The top left corner
10206 t      The center of the top edge
10207 tr     The top right corner
10208 l      The center of the left edge
10209 r      The center of the right edge
10210 bl     The bottom left corner
10211 b      The center of the bottom edge
10212 br     The bottom right corner
10213 </pre>
10214  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
10215  * below are common options that can be passed to any Fx method.</b>
10216  * @cfg {Function} callback A function called when the effect is finished
10217  * @cfg {Object} scope The scope of the effect function
10218  * @cfg {String} easing A valid Easing value for the effect
10219  * @cfg {String} afterCls A css class to apply after the effect
10220  * @cfg {Number} duration The length of time (in seconds) that the effect should last
10221  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
10222  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
10223  * effects that end with the element being visually hidden, ignored otherwise)
10224  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
10225  * a function which returns such a specification that will be applied to the Element after the effect finishes
10226  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
10227  * @cfg {Boolean} concurrent Whether to allow subsequently-queued effects to run at the same time as the current effect, or to ensure that they run in sequence
10228  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
10229  */
10230 Roo.Fx = {
10231         /**
10232          * Slides the element into view.  An anchor point can be optionally passed to set the point of
10233          * origin for the slide effect.  This function automatically handles wrapping the element with
10234          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10235          * Usage:
10236          *<pre><code>
10237 // default: slide the element in from the top
10238 el.slideIn();
10239
10240 // custom: slide the element in from the right with a 2-second duration
10241 el.slideIn('r', { duration: 2 });
10242
10243 // common config options shown with default values
10244 el.slideIn('t', {
10245     easing: 'easeOut',
10246     duration: .5
10247 });
10248 </code></pre>
10249          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10250          * @param {Object} options (optional) Object literal with any of the Fx config options
10251          * @return {Roo.Element} The Element
10252          */
10253     slideIn : function(anchor, o){
10254         var el = this.getFxEl();
10255         o = o || {};
10256
10257         el.queueFx(o, function(){
10258
10259             anchor = anchor || "t";
10260
10261             // fix display to visibility
10262             this.fixDisplay();
10263
10264             // restore values after effect
10265             var r = this.getFxRestore();
10266             var b = this.getBox();
10267             // fixed size for slide
10268             this.setSize(b);
10269
10270             // wrap if needed
10271             var wrap = this.fxWrap(r.pos, o, "hidden");
10272
10273             var st = this.dom.style;
10274             st.visibility = "visible";
10275             st.position = "absolute";
10276
10277             // clear out temp styles after slide and unwrap
10278             var after = function(){
10279                 el.fxUnwrap(wrap, r.pos, o);
10280                 st.width = r.width;
10281                 st.height = r.height;
10282                 el.afterFx(o);
10283             };
10284             // time to calc the positions
10285             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
10286
10287             switch(anchor.toLowerCase()){
10288                 case "t":
10289                     wrap.setSize(b.width, 0);
10290                     st.left = st.bottom = "0";
10291                     a = {height: bh};
10292                 break;
10293                 case "l":
10294                     wrap.setSize(0, b.height);
10295                     st.right = st.top = "0";
10296                     a = {width: bw};
10297                 break;
10298                 case "r":
10299                     wrap.setSize(0, b.height);
10300                     wrap.setX(b.right);
10301                     st.left = st.top = "0";
10302                     a = {width: bw, points: pt};
10303                 break;
10304                 case "b":
10305                     wrap.setSize(b.width, 0);
10306                     wrap.setY(b.bottom);
10307                     st.left = st.top = "0";
10308                     a = {height: bh, points: pt};
10309                 break;
10310                 case "tl":
10311                     wrap.setSize(0, 0);
10312                     st.right = st.bottom = "0";
10313                     a = {width: bw, height: bh};
10314                 break;
10315                 case "bl":
10316                     wrap.setSize(0, 0);
10317                     wrap.setY(b.y+b.height);
10318                     st.right = st.top = "0";
10319                     a = {width: bw, height: bh, points: pt};
10320                 break;
10321                 case "br":
10322                     wrap.setSize(0, 0);
10323                     wrap.setXY([b.right, b.bottom]);
10324                     st.left = st.top = "0";
10325                     a = {width: bw, height: bh, points: pt};
10326                 break;
10327                 case "tr":
10328                     wrap.setSize(0, 0);
10329                     wrap.setX(b.x+b.width);
10330                     st.left = st.bottom = "0";
10331                     a = {width: bw, height: bh, points: pt};
10332                 break;
10333             }
10334             this.dom.style.visibility = "visible";
10335             wrap.show();
10336
10337             arguments.callee.anim = wrap.fxanim(a,
10338                 o,
10339                 'motion',
10340                 .5,
10341                 'easeOut', after);
10342         });
10343         return this;
10344     },
10345     
10346         /**
10347          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
10348          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
10349          * 'hidden') but block elements will still take up space in the document.  The element must be removed
10350          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
10351          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10352          * Usage:
10353          *<pre><code>
10354 // default: slide the element out to the top
10355 el.slideOut();
10356
10357 // custom: slide the element out to the right with a 2-second duration
10358 el.slideOut('r', { duration: 2 });
10359
10360 // common config options shown with default values
10361 el.slideOut('t', {
10362     easing: 'easeOut',
10363     duration: .5,
10364     remove: false,
10365     useDisplay: false
10366 });
10367 </code></pre>
10368          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10369          * @param {Object} options (optional) Object literal with any of the Fx config options
10370          * @return {Roo.Element} The Element
10371          */
10372     slideOut : function(anchor, o){
10373         var el = this.getFxEl();
10374         o = o || {};
10375
10376         el.queueFx(o, function(){
10377
10378             anchor = anchor || "t";
10379
10380             // restore values after effect
10381             var r = this.getFxRestore();
10382             
10383             var b = this.getBox();
10384             // fixed size for slide
10385             this.setSize(b);
10386
10387             // wrap if needed
10388             var wrap = this.fxWrap(r.pos, o, "visible");
10389
10390             var st = this.dom.style;
10391             st.visibility = "visible";
10392             st.position = "absolute";
10393
10394             wrap.setSize(b);
10395
10396             var after = function(){
10397                 if(o.useDisplay){
10398                     el.setDisplayed(false);
10399                 }else{
10400                     el.hide();
10401                 }
10402
10403                 el.fxUnwrap(wrap, r.pos, o);
10404
10405                 st.width = r.width;
10406                 st.height = r.height;
10407
10408                 el.afterFx(o);
10409             };
10410
10411             var a, zero = {to: 0};
10412             switch(anchor.toLowerCase()){
10413                 case "t":
10414                     st.left = st.bottom = "0";
10415                     a = {height: zero};
10416                 break;
10417                 case "l":
10418                     st.right = st.top = "0";
10419                     a = {width: zero};
10420                 break;
10421                 case "r":
10422                     st.left = st.top = "0";
10423                     a = {width: zero, points: {to:[b.right, b.y]}};
10424                 break;
10425                 case "b":
10426                     st.left = st.top = "0";
10427                     a = {height: zero, points: {to:[b.x, b.bottom]}};
10428                 break;
10429                 case "tl":
10430                     st.right = st.bottom = "0";
10431                     a = {width: zero, height: zero};
10432                 break;
10433                 case "bl":
10434                     st.right = st.top = "0";
10435                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
10436                 break;
10437                 case "br":
10438                     st.left = st.top = "0";
10439                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
10440                 break;
10441                 case "tr":
10442                     st.left = st.bottom = "0";
10443                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
10444                 break;
10445             }
10446
10447             arguments.callee.anim = wrap.fxanim(a,
10448                 o,
10449                 'motion',
10450                 .5,
10451                 "easeOut", after);
10452         });
10453         return this;
10454     },
10455
10456         /**
10457          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
10458          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
10459          * The element must be removed from the DOM using the 'remove' config option if desired.
10460          * Usage:
10461          *<pre><code>
10462 // default
10463 el.puff();
10464
10465 // common config options shown with default values
10466 el.puff({
10467     easing: 'easeOut',
10468     duration: .5,
10469     remove: false,
10470     useDisplay: false
10471 });
10472 </code></pre>
10473          * @param {Object} options (optional) Object literal with any of the Fx config options
10474          * @return {Roo.Element} The Element
10475          */
10476     puff : function(o){
10477         var el = this.getFxEl();
10478         o = o || {};
10479
10480         el.queueFx(o, function(){
10481             this.clearOpacity();
10482             this.show();
10483
10484             // restore values after effect
10485             var r = this.getFxRestore();
10486             var st = this.dom.style;
10487
10488             var after = function(){
10489                 if(o.useDisplay){
10490                     el.setDisplayed(false);
10491                 }else{
10492                     el.hide();
10493                 }
10494
10495                 el.clearOpacity();
10496
10497                 el.setPositioning(r.pos);
10498                 st.width = r.width;
10499                 st.height = r.height;
10500                 st.fontSize = '';
10501                 el.afterFx(o);
10502             };
10503
10504             var width = this.getWidth();
10505             var height = this.getHeight();
10506
10507             arguments.callee.anim = this.fxanim({
10508                     width : {to: this.adjustWidth(width * 2)},
10509                     height : {to: this.adjustHeight(height * 2)},
10510                     points : {by: [-(width * .5), -(height * .5)]},
10511                     opacity : {to: 0},
10512                     fontSize: {to:200, unit: "%"}
10513                 },
10514                 o,
10515                 'motion',
10516                 .5,
10517                 "easeOut", after);
10518         });
10519         return this;
10520     },
10521
10522         /**
10523          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
10524          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
10525          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
10526          * Usage:
10527          *<pre><code>
10528 // default
10529 el.switchOff();
10530
10531 // all config options shown with default values
10532 el.switchOff({
10533     easing: 'easeIn',
10534     duration: .3,
10535     remove: false,
10536     useDisplay: false
10537 });
10538 </code></pre>
10539          * @param {Object} options (optional) Object literal with any of the Fx config options
10540          * @return {Roo.Element} The Element
10541          */
10542     switchOff : function(o){
10543         var el = this.getFxEl();
10544         o = o || {};
10545
10546         el.queueFx(o, function(){
10547             this.clearOpacity();
10548             this.clip();
10549
10550             // restore values after effect
10551             var r = this.getFxRestore();
10552             var st = this.dom.style;
10553
10554             var after = function(){
10555                 if(o.useDisplay){
10556                     el.setDisplayed(false);
10557                 }else{
10558                     el.hide();
10559                 }
10560
10561                 el.clearOpacity();
10562                 el.setPositioning(r.pos);
10563                 st.width = r.width;
10564                 st.height = r.height;
10565
10566                 el.afterFx(o);
10567             };
10568
10569             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
10570                 this.clearOpacity();
10571                 (function(){
10572                     this.fxanim({
10573                         height:{to:1},
10574                         points:{by:[0, this.getHeight() * .5]}
10575                     }, o, 'motion', 0.3, 'easeIn', after);
10576                 }).defer(100, this);
10577             });
10578         });
10579         return this;
10580     },
10581
10582     /**
10583      * Highlights the Element by setting a color (applies to the background-color by default, but can be
10584      * changed using the "attr" config option) and then fading back to the original color. If no original
10585      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
10586      * Usage:
10587 <pre><code>
10588 // default: highlight background to yellow
10589 el.highlight();
10590
10591 // custom: highlight foreground text to blue for 2 seconds
10592 el.highlight("0000ff", { attr: 'color', duration: 2 });
10593
10594 // common config options shown with default values
10595 el.highlight("ffff9c", {
10596     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
10597     endColor: (current color) or "ffffff",
10598     easing: 'easeIn',
10599     duration: 1
10600 });
10601 </code></pre>
10602      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
10603      * @param {Object} options (optional) Object literal with any of the Fx config options
10604      * @return {Roo.Element} The Element
10605      */ 
10606     highlight : function(color, o){
10607         var el = this.getFxEl();
10608         o = o || {};
10609
10610         el.queueFx(o, function(){
10611             color = color || "ffff9c";
10612             attr = o.attr || "backgroundColor";
10613
10614             this.clearOpacity();
10615             this.show();
10616
10617             var origColor = this.getColor(attr);
10618             var restoreColor = this.dom.style[attr];
10619             endColor = (o.endColor || origColor) || "ffffff";
10620
10621             var after = function(){
10622                 el.dom.style[attr] = restoreColor;
10623                 el.afterFx(o);
10624             };
10625
10626             var a = {};
10627             a[attr] = {from: color, to: endColor};
10628             arguments.callee.anim = this.fxanim(a,
10629                 o,
10630                 'color',
10631                 1,
10632                 'easeIn', after);
10633         });
10634         return this;
10635     },
10636
10637    /**
10638     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
10639     * Usage:
10640 <pre><code>
10641 // default: a single light blue ripple
10642 el.frame();
10643
10644 // custom: 3 red ripples lasting 3 seconds total
10645 el.frame("ff0000", 3, { duration: 3 });
10646
10647 // common config options shown with default values
10648 el.frame("C3DAF9", 1, {
10649     duration: 1 //duration of entire animation (not each individual ripple)
10650     // Note: Easing is not configurable and will be ignored if included
10651 });
10652 </code></pre>
10653     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
10654     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
10655     * @param {Object} options (optional) Object literal with any of the Fx config options
10656     * @return {Roo.Element} The Element
10657     */
10658     frame : function(color, count, o){
10659         var el = this.getFxEl();
10660         o = o || {};
10661
10662         el.queueFx(o, function(){
10663             color = color || "#C3DAF9";
10664             if(color.length == 6){
10665                 color = "#" + color;
10666             }
10667             count = count || 1;
10668             duration = o.duration || 1;
10669             this.show();
10670
10671             var b = this.getBox();
10672             var animFn = function(){
10673                 var proxy = this.createProxy({
10674
10675                      style:{
10676                         visbility:"hidden",
10677                         position:"absolute",
10678                         "z-index":"35000", // yee haw
10679                         border:"0px solid " + color
10680                      }
10681                   });
10682                 var scale = Roo.isBorderBox ? 2 : 1;
10683                 proxy.animate({
10684                     top:{from:b.y, to:b.y - 20},
10685                     left:{from:b.x, to:b.x - 20},
10686                     borderWidth:{from:0, to:10},
10687                     opacity:{from:1, to:0},
10688                     height:{from:b.height, to:(b.height + (20*scale))},
10689                     width:{from:b.width, to:(b.width + (20*scale))}
10690                 }, duration, function(){
10691                     proxy.remove();
10692                 });
10693                 if(--count > 0){
10694                      animFn.defer((duration/2)*1000, this);
10695                 }else{
10696                     el.afterFx(o);
10697                 }
10698             };
10699             animFn.call(this);
10700         });
10701         return this;
10702     },
10703
10704    /**
10705     * Creates a pause before any subsequent queued effects begin.  If there are
10706     * no effects queued after the pause it will have no effect.
10707     * Usage:
10708 <pre><code>
10709 el.pause(1);
10710 </code></pre>
10711     * @param {Number} seconds The length of time to pause (in seconds)
10712     * @return {Roo.Element} The Element
10713     */
10714     pause : function(seconds){
10715         var el = this.getFxEl();
10716         var o = {};
10717
10718         el.queueFx(o, function(){
10719             setTimeout(function(){
10720                 el.afterFx(o);
10721             }, seconds * 1000);
10722         });
10723         return this;
10724     },
10725
10726    /**
10727     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
10728     * using the "endOpacity" config option.
10729     * Usage:
10730 <pre><code>
10731 // default: fade in from opacity 0 to 100%
10732 el.fadeIn();
10733
10734 // custom: fade in from opacity 0 to 75% over 2 seconds
10735 el.fadeIn({ endOpacity: .75, duration: 2});
10736
10737 // common config options shown with default values
10738 el.fadeIn({
10739     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
10740     easing: 'easeOut',
10741     duration: .5
10742 });
10743 </code></pre>
10744     * @param {Object} options (optional) Object literal with any of the Fx config options
10745     * @return {Roo.Element} The Element
10746     */
10747     fadeIn : function(o){
10748         var el = this.getFxEl();
10749         o = o || {};
10750         el.queueFx(o, function(){
10751             this.setOpacity(0);
10752             this.fixDisplay();
10753             this.dom.style.visibility = 'visible';
10754             var to = o.endOpacity || 1;
10755             arguments.callee.anim = this.fxanim({opacity:{to:to}},
10756                 o, null, .5, "easeOut", function(){
10757                 if(to == 1){
10758                     this.clearOpacity();
10759                 }
10760                 el.afterFx(o);
10761             });
10762         });
10763         return this;
10764     },
10765
10766    /**
10767     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
10768     * using the "endOpacity" config option.
10769     * Usage:
10770 <pre><code>
10771 // default: fade out from the element's current opacity to 0
10772 el.fadeOut();
10773
10774 // custom: fade out from the element's current opacity to 25% over 2 seconds
10775 el.fadeOut({ endOpacity: .25, duration: 2});
10776
10777 // common config options shown with default values
10778 el.fadeOut({
10779     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
10780     easing: 'easeOut',
10781     duration: .5
10782     remove: false,
10783     useDisplay: false
10784 });
10785 </code></pre>
10786     * @param {Object} options (optional) Object literal with any of the Fx config options
10787     * @return {Roo.Element} The Element
10788     */
10789     fadeOut : function(o){
10790         var el = this.getFxEl();
10791         o = o || {};
10792         el.queueFx(o, function(){
10793             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
10794                 o, null, .5, "easeOut", function(){
10795                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
10796                      this.dom.style.display = "none";
10797                 }else{
10798                      this.dom.style.visibility = "hidden";
10799                 }
10800                 this.clearOpacity();
10801                 el.afterFx(o);
10802             });
10803         });
10804         return this;
10805     },
10806
10807    /**
10808     * Animates the transition of an element's dimensions from a starting height/width
10809     * to an ending height/width.
10810     * Usage:
10811 <pre><code>
10812 // change height and width to 100x100 pixels
10813 el.scale(100, 100);
10814
10815 // common config options shown with default values.  The height and width will default to
10816 // the element's existing values if passed as null.
10817 el.scale(
10818     [element's width],
10819     [element's height], {
10820     easing: 'easeOut',
10821     duration: .35
10822 });
10823 </code></pre>
10824     * @param {Number} width  The new width (pass undefined to keep the original width)
10825     * @param {Number} height  The new height (pass undefined to keep the original height)
10826     * @param {Object} options (optional) Object literal with any of the Fx config options
10827     * @return {Roo.Element} The Element
10828     */
10829     scale : function(w, h, o){
10830         this.shift(Roo.apply({}, o, {
10831             width: w,
10832             height: h
10833         }));
10834         return this;
10835     },
10836
10837    /**
10838     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
10839     * Any of these properties not specified in the config object will not be changed.  This effect 
10840     * requires that at least one new dimension, position or opacity setting must be passed in on
10841     * the config object in order for the function to have any effect.
10842     * Usage:
10843 <pre><code>
10844 // slide the element horizontally to x position 200 while changing the height and opacity
10845 el.shift({ x: 200, height: 50, opacity: .8 });
10846
10847 // common config options shown with default values.
10848 el.shift({
10849     width: [element's width],
10850     height: [element's height],
10851     x: [element's x position],
10852     y: [element's y position],
10853     opacity: [element's opacity],
10854     easing: 'easeOut',
10855     duration: .35
10856 });
10857 </code></pre>
10858     * @param {Object} options  Object literal with any of the Fx config options
10859     * @return {Roo.Element} The Element
10860     */
10861     shift : function(o){
10862         var el = this.getFxEl();
10863         o = o || {};
10864         el.queueFx(o, function(){
10865             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
10866             if(w !== undefined){
10867                 a.width = {to: this.adjustWidth(w)};
10868             }
10869             if(h !== undefined){
10870                 a.height = {to: this.adjustHeight(h)};
10871             }
10872             if(x !== undefined || y !== undefined){
10873                 a.points = {to: [
10874                     x !== undefined ? x : this.getX(),
10875                     y !== undefined ? y : this.getY()
10876                 ]};
10877             }
10878             if(op !== undefined){
10879                 a.opacity = {to: op};
10880             }
10881             if(o.xy !== undefined){
10882                 a.points = {to: o.xy};
10883             }
10884             arguments.callee.anim = this.fxanim(a,
10885                 o, 'motion', .35, "easeOut", function(){
10886                 el.afterFx(o);
10887             });
10888         });
10889         return this;
10890     },
10891
10892         /**
10893          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
10894          * ending point of the effect.
10895          * Usage:
10896          *<pre><code>
10897 // default: slide the element downward while fading out
10898 el.ghost();
10899
10900 // custom: slide the element out to the right with a 2-second duration
10901 el.ghost('r', { duration: 2 });
10902
10903 // common config options shown with default values
10904 el.ghost('b', {
10905     easing: 'easeOut',
10906     duration: .5
10907     remove: false,
10908     useDisplay: false
10909 });
10910 </code></pre>
10911          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
10912          * @param {Object} options (optional) Object literal with any of the Fx config options
10913          * @return {Roo.Element} The Element
10914          */
10915     ghost : function(anchor, o){
10916         var el = this.getFxEl();
10917         o = o || {};
10918
10919         el.queueFx(o, function(){
10920             anchor = anchor || "b";
10921
10922             // restore values after effect
10923             var r = this.getFxRestore();
10924             var w = this.getWidth(),
10925                 h = this.getHeight();
10926
10927             var st = this.dom.style;
10928
10929             var after = function(){
10930                 if(o.useDisplay){
10931                     el.setDisplayed(false);
10932                 }else{
10933                     el.hide();
10934                 }
10935
10936                 el.clearOpacity();
10937                 el.setPositioning(r.pos);
10938                 st.width = r.width;
10939                 st.height = r.height;
10940
10941                 el.afterFx(o);
10942             };
10943
10944             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
10945             switch(anchor.toLowerCase()){
10946                 case "t":
10947                     pt.by = [0, -h];
10948                 break;
10949                 case "l":
10950                     pt.by = [-w, 0];
10951                 break;
10952                 case "r":
10953                     pt.by = [w, 0];
10954                 break;
10955                 case "b":
10956                     pt.by = [0, h];
10957                 break;
10958                 case "tl":
10959                     pt.by = [-w, -h];
10960                 break;
10961                 case "bl":
10962                     pt.by = [-w, h];
10963                 break;
10964                 case "br":
10965                     pt.by = [w, h];
10966                 break;
10967                 case "tr":
10968                     pt.by = [w, -h];
10969                 break;
10970             }
10971
10972             arguments.callee.anim = this.fxanim(a,
10973                 o,
10974                 'motion',
10975                 .5,
10976                 "easeOut", after);
10977         });
10978         return this;
10979     },
10980
10981         /**
10982          * Ensures that all effects queued after syncFx is called on the element are
10983          * run concurrently.  This is the opposite of {@link #sequenceFx}.
10984          * @return {Roo.Element} The Element
10985          */
10986     syncFx : function(){
10987         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10988             block : false,
10989             concurrent : true,
10990             stopFx : false
10991         });
10992         return this;
10993     },
10994
10995         /**
10996          * Ensures that all effects queued after sequenceFx is called on the element are
10997          * run in sequence.  This is the opposite of {@link #syncFx}.
10998          * @return {Roo.Element} The Element
10999          */
11000     sequenceFx : function(){
11001         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
11002             block : false,
11003             concurrent : false,
11004             stopFx : false
11005         });
11006         return this;
11007     },
11008
11009         /* @private */
11010     nextFx : function(){
11011         var ef = this.fxQueue[0];
11012         if(ef){
11013             ef.call(this);
11014         }
11015     },
11016
11017         /**
11018          * Returns true if the element has any effects actively running or queued, else returns false.
11019          * @return {Boolean} True if element has active effects, else false
11020          */
11021     hasActiveFx : function(){
11022         return this.fxQueue && this.fxQueue[0];
11023     },
11024
11025         /**
11026          * Stops any running effects and clears the element's internal effects queue if it contains
11027          * any additional effects that haven't started yet.
11028          * @return {Roo.Element} The Element
11029          */
11030     stopFx : function(){
11031         if(this.hasActiveFx()){
11032             var cur = this.fxQueue[0];
11033             if(cur && cur.anim && cur.anim.isAnimated()){
11034                 this.fxQueue = [cur]; // clear out others
11035                 cur.anim.stop(true);
11036             }
11037         }
11038         return this;
11039     },
11040
11041         /* @private */
11042     beforeFx : function(o){
11043         if(this.hasActiveFx() && !o.concurrent){
11044            if(o.stopFx){
11045                this.stopFx();
11046                return true;
11047            }
11048            return false;
11049         }
11050         return true;
11051     },
11052
11053         /**
11054          * Returns true if the element is currently blocking so that no other effect can be queued
11055          * until this effect is finished, else returns false if blocking is not set.  This is commonly
11056          * used to ensure that an effect initiated by a user action runs to completion prior to the
11057          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
11058          * @return {Boolean} True if blocking, else false
11059          */
11060     hasFxBlock : function(){
11061         var q = this.fxQueue;
11062         return q && q[0] && q[0].block;
11063     },
11064
11065         /* @private */
11066     queueFx : function(o, fn){
11067         if(!this.fxQueue){
11068             this.fxQueue = [];
11069         }
11070         if(!this.hasFxBlock()){
11071             Roo.applyIf(o, this.fxDefaults);
11072             if(!o.concurrent){
11073                 var run = this.beforeFx(o);
11074                 fn.block = o.block;
11075                 this.fxQueue.push(fn);
11076                 if(run){
11077                     this.nextFx();
11078                 }
11079             }else{
11080                 fn.call(this);
11081             }
11082         }
11083         return this;
11084     },
11085
11086         /* @private */
11087     fxWrap : function(pos, o, vis){
11088         var wrap;
11089         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
11090             var wrapXY;
11091             if(o.fixPosition){
11092                 wrapXY = this.getXY();
11093             }
11094             var div = document.createElement("div");
11095             div.style.visibility = vis;
11096             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
11097             wrap.setPositioning(pos);
11098             if(wrap.getStyle("position") == "static"){
11099                 wrap.position("relative");
11100             }
11101             this.clearPositioning('auto');
11102             wrap.clip();
11103             wrap.dom.appendChild(this.dom);
11104             if(wrapXY){
11105                 wrap.setXY(wrapXY);
11106             }
11107         }
11108         return wrap;
11109     },
11110
11111         /* @private */
11112     fxUnwrap : function(wrap, pos, o){
11113         this.clearPositioning();
11114         this.setPositioning(pos);
11115         if(!o.wrap){
11116             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
11117             wrap.remove();
11118         }
11119     },
11120
11121         /* @private */
11122     getFxRestore : function(){
11123         var st = this.dom.style;
11124         return {pos: this.getPositioning(), width: st.width, height : st.height};
11125     },
11126
11127         /* @private */
11128     afterFx : function(o){
11129         if(o.afterStyle){
11130             this.applyStyles(o.afterStyle);
11131         }
11132         if(o.afterCls){
11133             this.addClass(o.afterCls);
11134         }
11135         if(o.remove === true){
11136             this.remove();
11137         }
11138         Roo.callback(o.callback, o.scope, [this]);
11139         if(!o.concurrent){
11140             this.fxQueue.shift();
11141             this.nextFx();
11142         }
11143     },
11144
11145         /* @private */
11146     getFxEl : function(){ // support for composite element fx
11147         return Roo.get(this.dom);
11148     },
11149
11150         /* @private */
11151     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
11152         animType = animType || 'run';
11153         opt = opt || {};
11154         var anim = Roo.lib.Anim[animType](
11155             this.dom, args,
11156             (opt.duration || defaultDur) || .35,
11157             (opt.easing || defaultEase) || 'easeOut',
11158             function(){
11159                 Roo.callback(cb, this);
11160             },
11161             this
11162         );
11163         opt.anim = anim;
11164         return anim;
11165     }
11166 };
11167
11168 // backwords compat
11169 Roo.Fx.resize = Roo.Fx.scale;
11170
11171 //When included, Roo.Fx is automatically applied to Element so that all basic
11172 //effects are available directly via the Element API
11173 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
11174  * Based on:
11175  * Ext JS Library 1.1.1
11176  * Copyright(c) 2006-2007, Ext JS, LLC.
11177  *
11178  * Originally Released Under LGPL - original licence link has changed is not relivant.
11179  *
11180  * Fork - LGPL
11181  * <script type="text/javascript">
11182  */
11183
11184
11185 /**
11186  * @class Roo.CompositeElement
11187  * Standard composite class. Creates a Roo.Element for every element in the collection.
11188  * <br><br>
11189  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11190  * actions will be performed on all the elements in this collection.</b>
11191  * <br><br>
11192  * All methods return <i>this</i> and can be chained.
11193  <pre><code>
11194  var els = Roo.select("#some-el div.some-class", true);
11195  // or select directly from an existing element
11196  var el = Roo.get('some-el');
11197  el.select('div.some-class', true);
11198
11199  els.setWidth(100); // all elements become 100 width
11200  els.hide(true); // all elements fade out and hide
11201  // or
11202  els.setWidth(100).hide(true);
11203  </code></pre>
11204  */
11205 Roo.CompositeElement = function(els){
11206     this.elements = [];
11207     this.addElements(els);
11208 };
11209 Roo.CompositeElement.prototype = {
11210     isComposite: true,
11211     addElements : function(els){
11212         if(!els) {
11213             return this;
11214         }
11215         if(typeof els == "string"){
11216             els = Roo.Element.selectorFunction(els);
11217         }
11218         var yels = this.elements;
11219         var index = yels.length-1;
11220         for(var i = 0, len = els.length; i < len; i++) {
11221                 yels[++index] = Roo.get(els[i]);
11222         }
11223         return this;
11224     },
11225
11226     /**
11227     * Clears this composite and adds the elements returned by the passed selector.
11228     * @param {String/Array} els A string CSS selector, an array of elements or an element
11229     * @return {CompositeElement} this
11230     */
11231     fill : function(els){
11232         this.elements = [];
11233         this.add(els);
11234         return this;
11235     },
11236
11237     /**
11238     * Filters this composite to only elements that match the passed selector.
11239     * @param {String} selector A string CSS selector
11240     * @param {Boolean} inverse return inverse filter (not matches)
11241     * @return {CompositeElement} this
11242     */
11243     filter : function(selector, inverse){
11244         var els = [];
11245         inverse = inverse || false;
11246         this.each(function(el){
11247             var match = inverse ? !el.is(selector) : el.is(selector);
11248             if(match){
11249                 els[els.length] = el.dom;
11250             }
11251         });
11252         this.fill(els);
11253         return this;
11254     },
11255
11256     invoke : function(fn, args){
11257         var els = this.elements;
11258         for(var i = 0, len = els.length; i < len; i++) {
11259                 Roo.Element.prototype[fn].apply(els[i], args);
11260         }
11261         return this;
11262     },
11263     /**
11264     * Adds elements to this composite.
11265     * @param {String/Array} els A string CSS selector, an array of elements or an element
11266     * @return {CompositeElement} this
11267     */
11268     add : function(els){
11269         if(typeof els == "string"){
11270             this.addElements(Roo.Element.selectorFunction(els));
11271         }else if(els.length !== undefined){
11272             this.addElements(els);
11273         }else{
11274             this.addElements([els]);
11275         }
11276         return this;
11277     },
11278     /**
11279     * Calls the passed function passing (el, this, index) for each element in this composite.
11280     * @param {Function} fn The function to call
11281     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11282     * @return {CompositeElement} this
11283     */
11284     each : function(fn, scope){
11285         var els = this.elements;
11286         for(var i = 0, len = els.length; i < len; i++){
11287             if(fn.call(scope || els[i], els[i], this, i) === false) {
11288                 break;
11289             }
11290         }
11291         return this;
11292     },
11293
11294     /**
11295      * Returns the Element object at the specified index
11296      * @param {Number} index
11297      * @return {Roo.Element}
11298      */
11299     item : function(index){
11300         return this.elements[index] || null;
11301     },
11302
11303     /**
11304      * Returns the first Element
11305      * @return {Roo.Element}
11306      */
11307     first : function(){
11308         return this.item(0);
11309     },
11310
11311     /**
11312      * Returns the last Element
11313      * @return {Roo.Element}
11314      */
11315     last : function(){
11316         return this.item(this.elements.length-1);
11317     },
11318
11319     /**
11320      * Returns the number of elements in this composite
11321      * @return Number
11322      */
11323     getCount : function(){
11324         return this.elements.length;
11325     },
11326
11327     /**
11328      * Returns true if this composite contains the passed element
11329      * @return Boolean
11330      */
11331     contains : function(el){
11332         return this.indexOf(el) !== -1;
11333     },
11334
11335     /**
11336      * Returns true if this composite contains the passed element
11337      * @return Boolean
11338      */
11339     indexOf : function(el){
11340         return this.elements.indexOf(Roo.get(el));
11341     },
11342
11343
11344     /**
11345     * Removes the specified element(s).
11346     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
11347     * or an array of any of those.
11348     * @param {Boolean} removeDom (optional) True to also remove the element from the document
11349     * @return {CompositeElement} this
11350     */
11351     removeElement : function(el, removeDom){
11352         if(el instanceof Array){
11353             for(var i = 0, len = el.length; i < len; i++){
11354                 this.removeElement(el[i]);
11355             }
11356             return this;
11357         }
11358         var index = typeof el == 'number' ? el : this.indexOf(el);
11359         if(index !== -1){
11360             if(removeDom){
11361                 var d = this.elements[index];
11362                 if(d.dom){
11363                     d.remove();
11364                 }else{
11365                     d.parentNode.removeChild(d);
11366                 }
11367             }
11368             this.elements.splice(index, 1);
11369         }
11370         return this;
11371     },
11372
11373     /**
11374     * Replaces the specified element with the passed element.
11375     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
11376     * to replace.
11377     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
11378     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
11379     * @return {CompositeElement} this
11380     */
11381     replaceElement : function(el, replacement, domReplace){
11382         var index = typeof el == 'number' ? el : this.indexOf(el);
11383         if(index !== -1){
11384             if(domReplace){
11385                 this.elements[index].replaceWith(replacement);
11386             }else{
11387                 this.elements.splice(index, 1, Roo.get(replacement))
11388             }
11389         }
11390         return this;
11391     },
11392
11393     /**
11394      * Removes all elements.
11395      */
11396     clear : function(){
11397         this.elements = [];
11398     }
11399 };
11400 (function(){
11401     Roo.CompositeElement.createCall = function(proto, fnName){
11402         if(!proto[fnName]){
11403             proto[fnName] = function(){
11404                 return this.invoke(fnName, arguments);
11405             };
11406         }
11407     };
11408     for(var fnName in Roo.Element.prototype){
11409         if(typeof Roo.Element.prototype[fnName] == "function"){
11410             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
11411         }
11412     };
11413 })();
11414 /*
11415  * Based on:
11416  * Ext JS Library 1.1.1
11417  * Copyright(c) 2006-2007, Ext JS, LLC.
11418  *
11419  * Originally Released Under LGPL - original licence link has changed is not relivant.
11420  *
11421  * Fork - LGPL
11422  * <script type="text/javascript">
11423  */
11424
11425 /**
11426  * @class Roo.CompositeElementLite
11427  * @extends Roo.CompositeElement
11428  * Flyweight composite class. Reuses the same Roo.Element for element operations.
11429  <pre><code>
11430  var els = Roo.select("#some-el div.some-class");
11431  // or select directly from an existing element
11432  var el = Roo.get('some-el');
11433  el.select('div.some-class');
11434
11435  els.setWidth(100); // all elements become 100 width
11436  els.hide(true); // all elements fade out and hide
11437  // or
11438  els.setWidth(100).hide(true);
11439  </code></pre><br><br>
11440  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11441  * actions will be performed on all the elements in this collection.</b>
11442  */
11443 Roo.CompositeElementLite = function(els){
11444     Roo.CompositeElementLite.superclass.constructor.call(this, els);
11445     this.el = new Roo.Element.Flyweight();
11446 };
11447 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
11448     addElements : function(els){
11449         if(els){
11450             if(els instanceof Array){
11451                 this.elements = this.elements.concat(els);
11452             }else{
11453                 var yels = this.elements;
11454                 var index = yels.length-1;
11455                 for(var i = 0, len = els.length; i < len; i++) {
11456                     yels[++index] = els[i];
11457                 }
11458             }
11459         }
11460         return this;
11461     },
11462     invoke : function(fn, args){
11463         var els = this.elements;
11464         var el = this.el;
11465         for(var i = 0, len = els.length; i < len; i++) {
11466             el.dom = els[i];
11467                 Roo.Element.prototype[fn].apply(el, args);
11468         }
11469         return this;
11470     },
11471     /**
11472      * Returns a flyweight Element of the dom element object at the specified index
11473      * @param {Number} index
11474      * @return {Roo.Element}
11475      */
11476     item : function(index){
11477         if(!this.elements[index]){
11478             return null;
11479         }
11480         this.el.dom = this.elements[index];
11481         return this.el;
11482     },
11483
11484     // fixes scope with flyweight
11485     addListener : function(eventName, handler, scope, opt){
11486         var els = this.elements;
11487         for(var i = 0, len = els.length; i < len; i++) {
11488             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
11489         }
11490         return this;
11491     },
11492
11493     /**
11494     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
11495     * passed is the flyweight (shared) Roo.Element instance, so if you require a
11496     * a reference to the dom node, use el.dom.</b>
11497     * @param {Function} fn The function to call
11498     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11499     * @return {CompositeElement} this
11500     */
11501     each : function(fn, scope){
11502         var els = this.elements;
11503         var el = this.el;
11504         for(var i = 0, len = els.length; i < len; i++){
11505             el.dom = els[i];
11506                 if(fn.call(scope || el, el, this, i) === false){
11507                 break;
11508             }
11509         }
11510         return this;
11511     },
11512
11513     indexOf : function(el){
11514         return this.elements.indexOf(Roo.getDom(el));
11515     },
11516
11517     replaceElement : function(el, replacement, domReplace){
11518         var index = typeof el == 'number' ? el : this.indexOf(el);
11519         if(index !== -1){
11520             replacement = Roo.getDom(replacement);
11521             if(domReplace){
11522                 var d = this.elements[index];
11523                 d.parentNode.insertBefore(replacement, d);
11524                 d.parentNode.removeChild(d);
11525             }
11526             this.elements.splice(index, 1, replacement);
11527         }
11528         return this;
11529     }
11530 });
11531 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
11532
11533 /*
11534  * Based on:
11535  * Ext JS Library 1.1.1
11536  * Copyright(c) 2006-2007, Ext JS, LLC.
11537  *
11538  * Originally Released Under LGPL - original licence link has changed is not relivant.
11539  *
11540  * Fork - LGPL
11541  * <script type="text/javascript">
11542  */
11543
11544  
11545
11546 /**
11547  * @class Roo.data.Connection
11548  * @extends Roo.util.Observable
11549  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
11550  * either to a configured URL, or to a URL specified at request time. 
11551  * 
11552  * Requests made by this class are asynchronous, and will return immediately. No data from
11553  * the server will be available to the statement immediately following the {@link #request} call.
11554  * To process returned data, use a callback in the request options object, or an event listener.
11555  * 
11556  * Note: If you are doing a file upload, you will not get a normal response object sent back to
11557  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
11558  * The response object is created using the innerHTML of the IFRAME's document as the responseText
11559  * property and, if present, the IFRAME's XML document as the responseXML property.
11560  * 
11561  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
11562  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
11563  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
11564  * standard DOM methods.
11565  * @constructor
11566  * @param {Object} config a configuration object.
11567  */
11568 Roo.data.Connection = function(config){
11569     Roo.apply(this, config);
11570     this.addEvents({
11571         /**
11572          * @event beforerequest
11573          * Fires before a network request is made to retrieve a data object.
11574          * @param {Connection} conn This Connection object.
11575          * @param {Object} options The options config object passed to the {@link #request} method.
11576          */
11577         "beforerequest" : true,
11578         /**
11579          * @event requestcomplete
11580          * Fires if the request was successfully completed.
11581          * @param {Connection} conn This Connection object.
11582          * @param {Object} response The XHR object containing the response data.
11583          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11584          * @param {Object} options The options config object passed to the {@link #request} method.
11585          */
11586         "requestcomplete" : true,
11587         /**
11588          * @event requestexception
11589          * Fires if an error HTTP status was returned from the server.
11590          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
11591          * @param {Connection} conn This Connection object.
11592          * @param {Object} response The XHR object containing the response data.
11593          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11594          * @param {Object} options The options config object passed to the {@link #request} method.
11595          */
11596         "requestexception" : true
11597     });
11598     Roo.data.Connection.superclass.constructor.call(this);
11599 };
11600
11601 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
11602     /**
11603      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11604      */
11605     /**
11606      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11607      * extra parameters to each request made by this object. (defaults to undefined)
11608      */
11609     /**
11610      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11611      *  to each request made by this object. (defaults to undefined)
11612      */
11613     /**
11614      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11615      */
11616     /**
11617      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11618      */
11619     timeout : 30000,
11620     /**
11621      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11622      * @type Boolean
11623      */
11624     autoAbort:false,
11625
11626     /**
11627      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11628      * @type Boolean
11629      */
11630     disableCaching: true,
11631
11632     /**
11633      * Sends an HTTP request to a remote server.
11634      * @param {Object} options An object which may contain the following properties:<ul>
11635      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
11636      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
11637      * request, a url encoded string or a function to call to get either.</li>
11638      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
11639      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
11640      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
11641      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
11642      * <li>options {Object} The parameter to the request call.</li>
11643      * <li>success {Boolean} True if the request succeeded.</li>
11644      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11645      * </ul></li>
11646      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
11647      * The callback is passed the following parameters:<ul>
11648      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11649      * <li>options {Object} The parameter to the request call.</li>
11650      * </ul></li>
11651      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
11652      * The callback is passed the following parameters:<ul>
11653      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11654      * <li>options {Object} The parameter to the request call.</li>
11655      * </ul></li>
11656      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
11657      * for the callback function. Defaults to the browser window.</li>
11658      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
11659      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
11660      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
11661      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
11662      * params for the post data. Any params will be appended to the URL.</li>
11663      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
11664      * </ul>
11665      * @return {Number} transactionId
11666      */
11667     request : function(o){
11668         if(this.fireEvent("beforerequest", this, o) !== false){
11669             var p = o.params;
11670
11671             if(typeof p == "function"){
11672                 p = p.call(o.scope||window, o);
11673             }
11674             if(typeof p == "object"){
11675                 p = Roo.urlEncode(o.params);
11676             }
11677             if(this.extraParams){
11678                 var extras = Roo.urlEncode(this.extraParams);
11679                 p = p ? (p + '&' + extras) : extras;
11680             }
11681
11682             var url = o.url || this.url;
11683             if(typeof url == 'function'){
11684                 url = url.call(o.scope||window, o);
11685             }
11686
11687             if(o.form){
11688                 var form = Roo.getDom(o.form);
11689                 url = url || form.action;
11690
11691                 var enctype = form.getAttribute("enctype");
11692                 
11693                 if (o.formData) {
11694                     return this.doFormDataUpload(o, url);
11695                 }
11696                 
11697                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
11698                     return this.doFormUpload(o, p, url);
11699                 }
11700                 var f = Roo.lib.Ajax.serializeForm(form);
11701                 p = p ? (p + '&' + f) : f;
11702             }
11703             
11704             if (!o.form && o.formData) {
11705                 o.formData = o.formData === true ? new FormData() : o.formData;
11706                 for (var k in o.params) {
11707                     o.formData.append(k,o.params[k]);
11708                 }
11709                     
11710                 return this.doFormDataUpload(o, url);
11711             }
11712             
11713
11714             var hs = o.headers;
11715             if(this.defaultHeaders){
11716                 hs = Roo.apply(hs || {}, this.defaultHeaders);
11717                 if(!o.headers){
11718                     o.headers = hs;
11719                 }
11720             }
11721
11722             var cb = {
11723                 success: this.handleResponse,
11724                 failure: this.handleFailure,
11725                 scope: this,
11726                 argument: {options: o},
11727                 timeout : o.timeout || this.timeout
11728             };
11729
11730             var method = o.method||this.method||(p ? "POST" : "GET");
11731
11732             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
11733                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
11734             }
11735
11736             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11737                 if(o.autoAbort){
11738                     this.abort();
11739                 }
11740             }else if(this.autoAbort !== false){
11741                 this.abort();
11742             }
11743
11744             if((method == 'GET' && p) || o.xmlData){
11745                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11746                 p = '';
11747             }
11748             Roo.lib.Ajax.useDefaultHeader = typeof(o.headers) == 'undefined' || typeof(o.headers['Content-Type']) == 'undefined';
11749             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
11750             Roo.lib.Ajax.useDefaultHeader == true;
11751             return this.transId;
11752         }else{
11753             Roo.callback(o.callback, o.scope, [o, null, null]);
11754             return null;
11755         }
11756     },
11757
11758     /**
11759      * Determine whether this object has a request outstanding.
11760      * @param {Number} transactionId (Optional) defaults to the last transaction
11761      * @return {Boolean} True if there is an outstanding request.
11762      */
11763     isLoading : function(transId){
11764         if(transId){
11765             return Roo.lib.Ajax.isCallInProgress(transId);
11766         }else{
11767             return this.transId ? true : false;
11768         }
11769     },
11770
11771     /**
11772      * Aborts any outstanding request.
11773      * @param {Number} transactionId (Optional) defaults to the last transaction
11774      */
11775     abort : function(transId){
11776         if(transId || this.isLoading()){
11777             Roo.lib.Ajax.abort(transId || this.transId);
11778         }
11779     },
11780
11781     // private
11782     handleResponse : function(response){
11783         this.transId = false;
11784         var options = response.argument.options;
11785         response.argument = options ? options.argument : null;
11786         this.fireEvent("requestcomplete", this, response, options);
11787         Roo.callback(options.success, options.scope, [response, options]);
11788         Roo.callback(options.callback, options.scope, [options, true, response]);
11789     },
11790
11791     // private
11792     handleFailure : function(response, e){
11793         this.transId = false;
11794         var options = response.argument.options;
11795         response.argument = options ? options.argument : null;
11796         this.fireEvent("requestexception", this, response, options, e);
11797         Roo.callback(options.failure, options.scope, [response, options]);
11798         Roo.callback(options.callback, options.scope, [options, false, response]);
11799     },
11800
11801     // private
11802     doFormUpload : function(o, ps, url){
11803         var id = Roo.id();
11804         var frame = document.createElement('iframe');
11805         frame.id = id;
11806         frame.name = id;
11807         frame.className = 'x-hidden';
11808         if(Roo.isIE){
11809             frame.src = Roo.SSL_SECURE_URL;
11810         }
11811         document.body.appendChild(frame);
11812
11813         if(Roo.isIE){
11814            document.frames[id].name = id;
11815         }
11816
11817         var form = Roo.getDom(o.form);
11818         form.target = id;
11819         form.method = 'POST';
11820         form.enctype = form.encoding = 'multipart/form-data';
11821         if(url){
11822             form.action = url;
11823         }
11824
11825         var hiddens, hd;
11826         if(ps){ // add dynamic params
11827             hiddens = [];
11828             ps = Roo.urlDecode(ps, false);
11829             for(var k in ps){
11830                 if(ps.hasOwnProperty(k)){
11831                     hd = document.createElement('input');
11832                     hd.type = 'hidden';
11833                     hd.name = k;
11834                     hd.value = ps[k];
11835                     form.appendChild(hd);
11836                     hiddens.push(hd);
11837                 }
11838             }
11839         }
11840
11841         function cb(){
11842             var r = {  // bogus response object
11843                 responseText : '',
11844                 responseXML : null
11845             };
11846
11847             r.argument = o ? o.argument : null;
11848
11849             try { //
11850                 var doc;
11851                 if(Roo.isIE){
11852                     doc = frame.contentWindow.document;
11853                 }else {
11854                     doc = (frame.contentDocument || window.frames[id].document);
11855                 }
11856                 if(doc && doc.body){
11857                     r.responseText = doc.body.innerHTML;
11858                 }
11859                 if(doc && doc.XMLDocument){
11860                     r.responseXML = doc.XMLDocument;
11861                 }else {
11862                     r.responseXML = doc;
11863                 }
11864             }
11865             catch(e) {
11866                 // ignore
11867             }
11868
11869             Roo.EventManager.removeListener(frame, 'load', cb, this);
11870
11871             this.fireEvent("requestcomplete", this, r, o);
11872             Roo.callback(o.success, o.scope, [r, o]);
11873             Roo.callback(o.callback, o.scope, [o, true, r]);
11874
11875             setTimeout(function(){document.body.removeChild(frame);}, 100);
11876         }
11877
11878         Roo.EventManager.on(frame, 'load', cb, this);
11879         form.submit();
11880
11881         if(hiddens){ // remove dynamic params
11882             for(var i = 0, len = hiddens.length; i < len; i++){
11883                 form.removeChild(hiddens[i]);
11884             }
11885         }
11886     },
11887     // this is a 'formdata version???'
11888     
11889     
11890     doFormDataUpload : function(o,  url)
11891     {
11892         var formData;
11893         if (o.form) {
11894             var form =  Roo.getDom(o.form);
11895             form.enctype = form.encoding = 'multipart/form-data';
11896             formData = o.formData === true ? new FormData(form) : o.formData;
11897         } else {
11898             formData = o.formData === true ? new FormData() : o.formData;
11899         }
11900         
11901       
11902         var cb = {
11903             success: this.handleResponse,
11904             failure: this.handleFailure,
11905             scope: this,
11906             argument: {options: o},
11907             timeout : o.timeout || this.timeout
11908         };
11909  
11910         if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11911             if(o.autoAbort){
11912                 this.abort();
11913             }
11914         }else if(this.autoAbort !== false){
11915             this.abort();
11916         }
11917
11918         //Roo.lib.Ajax.defaultPostHeader = null;
11919         Roo.lib.Ajax.useDefaultHeader = false;
11920         this.transId = Roo.lib.Ajax.request( "POST", url, cb,  formData, o);
11921         Roo.lib.Ajax.useDefaultHeader = true;
11922  
11923          
11924     }
11925     
11926 });
11927 /*
11928  * Based on:
11929  * Ext JS Library 1.1.1
11930  * Copyright(c) 2006-2007, Ext JS, LLC.
11931  *
11932  * Originally Released Under LGPL - original licence link has changed is not relivant.
11933  *
11934  * Fork - LGPL
11935  * <script type="text/javascript">
11936  */
11937  
11938 /**
11939  * Global Ajax request class.
11940  * 
11941  * @class Roo.Ajax
11942  * @extends Roo.data.Connection
11943  * @static
11944  * 
11945  * @cfg {String} url  The default URL to be used for requests to the server. (defaults to undefined)
11946  * @cfg {Object} extraParams  An object containing properties which are used as extra parameters to each request made by this object. (defaults to undefined)
11947  * @cfg {Object} defaultHeaders  An object containing request headers which are added to each request made by this object. (defaults to undefined)
11948  * @cfg {String} method (Optional)  The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11949  * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11950  * @cfg {Boolean} autoAbort (Optional) Whether a new request should abort any pending requests. (defaults to false)
11951  * @cfg {Boolean} disableCaching (Optional)   True to add a unique cache-buster param to GET requests. (defaults to true)
11952  */
11953 Roo.Ajax = new Roo.data.Connection({
11954     // fix up the docs
11955     /**
11956      * @scope Roo.Ajax
11957      * @type {Boolear} 
11958      */
11959     autoAbort : false,
11960
11961     /**
11962      * Serialize the passed form into a url encoded string
11963      * @scope Roo.Ajax
11964      * @param {String/HTMLElement} form
11965      * @return {String}
11966      */
11967     serializeForm : function(form){
11968         return Roo.lib.Ajax.serializeForm(form);
11969     }
11970 });/*
11971  * Based on:
11972  * Ext JS Library 1.1.1
11973  * Copyright(c) 2006-2007, Ext JS, LLC.
11974  *
11975  * Originally Released Under LGPL - original licence link has changed is not relivant.
11976  *
11977  * Fork - LGPL
11978  * <script type="text/javascript">
11979  */
11980
11981  
11982 /**
11983  * @class Roo.UpdateManager
11984  * @extends Roo.util.Observable
11985  * Provides AJAX-style update for Element object.<br><br>
11986  * Usage:<br>
11987  * <pre><code>
11988  * // Get it from a Roo.Element object
11989  * var el = Roo.get("foo");
11990  * var mgr = el.getUpdateManager();
11991  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
11992  * ...
11993  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
11994  * <br>
11995  * // or directly (returns the same UpdateManager instance)
11996  * var mgr = new Roo.UpdateManager("myElementId");
11997  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
11998  * mgr.on("update", myFcnNeedsToKnow);
11999  * <br>
12000    // short handed call directly from the element object
12001    Roo.get("foo").load({
12002         url: "bar.php",
12003         scripts:true,
12004         params: "for=bar",
12005         text: "Loading Foo..."
12006    });
12007  * </code></pre>
12008  * @constructor
12009  * Create new UpdateManager directly.
12010  * @param {String/HTMLElement/Roo.Element} el The element to update
12011  * @param {Boolean} forceNew (optional) By default the constructor checks to see if the passed element already has an UpdateManager and if it does it returns the same instance. This will skip that check (useful for extending this class).
12012  */
12013 Roo.UpdateManager = function(el, forceNew){
12014     el = Roo.get(el);
12015     if(!forceNew && el.updateManager){
12016         return el.updateManager;
12017     }
12018     /**
12019      * The Element object
12020      * @type Roo.Element
12021      */
12022     this.el = el;
12023     /**
12024      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
12025      * @type String
12026      */
12027     this.defaultUrl = null;
12028
12029     this.addEvents({
12030         /**
12031          * @event beforeupdate
12032          * Fired before an update is made, return false from your handler and the update is cancelled.
12033          * @param {Roo.Element} el
12034          * @param {String/Object/Function} url
12035          * @param {String/Object} params
12036          */
12037         "beforeupdate": true,
12038         /**
12039          * @event update
12040          * Fired after successful update is made.
12041          * @param {Roo.Element} el
12042          * @param {Object} oResponseObject The response Object
12043          */
12044         "update": true,
12045         /**
12046          * @event failure
12047          * Fired on update failure.
12048          * @param {Roo.Element} el
12049          * @param {Object} oResponseObject The response Object
12050          */
12051         "failure": true
12052     });
12053     var d = Roo.UpdateManager.defaults;
12054     /**
12055      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
12056      * @type String
12057      */
12058     this.sslBlankUrl = d.sslBlankUrl;
12059     /**
12060      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
12061      * @type Boolean
12062      */
12063     this.disableCaching = d.disableCaching;
12064     /**
12065      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12066      * @type String
12067      */
12068     this.indicatorText = d.indicatorText;
12069     /**
12070      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
12071      * @type String
12072      */
12073     this.showLoadIndicator = d.showLoadIndicator;
12074     /**
12075      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
12076      * @type Number
12077      */
12078     this.timeout = d.timeout;
12079
12080     /**
12081      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
12082      * @type Boolean
12083      */
12084     this.loadScripts = d.loadScripts;
12085
12086     /**
12087      * Transaction object of current executing transaction
12088      */
12089     this.transaction = null;
12090
12091     /**
12092      * @private
12093      */
12094     this.autoRefreshProcId = null;
12095     /**
12096      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
12097      * @type Function
12098      */
12099     this.refreshDelegate = this.refresh.createDelegate(this);
12100     /**
12101      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
12102      * @type Function
12103      */
12104     this.updateDelegate = this.update.createDelegate(this);
12105     /**
12106      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
12107      * @type Function
12108      */
12109     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
12110     /**
12111      * @private
12112      */
12113     this.successDelegate = this.processSuccess.createDelegate(this);
12114     /**
12115      * @private
12116      */
12117     this.failureDelegate = this.processFailure.createDelegate(this);
12118
12119     if(!this.renderer){
12120      /**
12121       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
12122       */
12123     this.renderer = new Roo.UpdateManager.BasicRenderer();
12124     }
12125     
12126     Roo.UpdateManager.superclass.constructor.call(this);
12127 };
12128
12129 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
12130     /**
12131      * Get the Element this UpdateManager is bound to
12132      * @return {Roo.Element} The element
12133      */
12134     getEl : function(){
12135         return this.el;
12136     },
12137     /**
12138      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
12139      * @param {Object/String/Function} url The url for this request or a function to call to get the url or a config object containing any of the following options:
12140 <pre><code>
12141 um.update({<br/>
12142     url: "your-url.php",<br/>
12143     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
12144     callback: yourFunction,<br/>
12145     scope: yourObject, //(optional scope)  <br/>
12146     discardUrl: false, <br/>
12147     nocache: false,<br/>
12148     text: "Loading...",<br/>
12149     timeout: 30,<br/>
12150     scripts: false<br/>
12151 });
12152 </code></pre>
12153      * The only required property is url. The optional properties nocache, text and scripts
12154      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
12155      * @param {String/Object} params (optional) The parameters to pass as either a url encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
12156      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
12157      * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used url. If true, it will not store the url.
12158      */
12159     update : function(url, params, callback, discardUrl){
12160         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
12161             var method = this.method,
12162                 cfg;
12163             if(typeof url == "object"){ // must be config object
12164                 cfg = url;
12165                 url = cfg.url;
12166                 params = params || cfg.params;
12167                 callback = callback || cfg.callback;
12168                 discardUrl = discardUrl || cfg.discardUrl;
12169                 if(callback && cfg.scope){
12170                     callback = callback.createDelegate(cfg.scope);
12171                 }
12172                 if(typeof cfg.method != "undefined"){method = cfg.method;};
12173                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
12174                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
12175                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
12176                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
12177             }
12178             this.showLoading();
12179             if(!discardUrl){
12180                 this.defaultUrl = url;
12181             }
12182             if(typeof url == "function"){
12183                 url = url.call(this);
12184             }
12185
12186             method = method || (params ? "POST" : "GET");
12187             if(method == "GET"){
12188                 url = this.prepareUrl(url);
12189             }
12190
12191             var o = Roo.apply(cfg ||{}, {
12192                 url : url,
12193                 params: params,
12194                 success: this.successDelegate,
12195                 failure: this.failureDelegate,
12196                 callback: undefined,
12197                 timeout: (this.timeout*1000),
12198                 argument: {"url": url, "form": null, "callback": callback, "params": params}
12199             });
12200             Roo.log("updated manager called with timeout of " + o.timeout);
12201             this.transaction = Roo.Ajax.request(o);
12202         }
12203     },
12204
12205     /**
12206      * Performs an async form post, updating this element with the response. If the form has the attribute enctype="multipart/form-data", it assumes it's a file upload.
12207      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
12208      * @param {String/HTMLElement} form The form Id or form element
12209      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
12210      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
12211      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
12212      */
12213     formUpdate : function(form, url, reset, callback){
12214         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
12215             if(typeof url == "function"){
12216                 url = url.call(this);
12217             }
12218             form = Roo.getDom(form);
12219             this.transaction = Roo.Ajax.request({
12220                 form: form,
12221                 url:url,
12222                 success: this.successDelegate,
12223                 failure: this.failureDelegate,
12224                 timeout: (this.timeout*1000),
12225                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
12226             });
12227             this.showLoading.defer(1, this);
12228         }
12229     },
12230
12231     /**
12232      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
12233      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
12234      */
12235     refresh : function(callback){
12236         if(this.defaultUrl == null){
12237             return;
12238         }
12239         this.update(this.defaultUrl, null, callback, true);
12240     },
12241
12242     /**
12243      * Set this element to auto refresh.
12244      * @param {Number} interval How often to update (in seconds).
12245      * @param {String/Function} url (optional) The url for this request or a function to call to get the url (Defaults to the last used url)
12246      * @param {String/Object} params (optional) The parameters to pass as either a url encoded string "&param1=1&param2=2" or as an object {param1: 1, param2: 2}
12247      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
12248      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
12249      */
12250     startAutoRefresh : function(interval, url, params, callback, refreshNow){
12251         if(refreshNow){
12252             this.update(url || this.defaultUrl, params, callback, true);
12253         }
12254         if(this.autoRefreshProcId){
12255             clearInterval(this.autoRefreshProcId);
12256         }
12257         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
12258     },
12259
12260     /**
12261      * Stop auto refresh on this element.
12262      */
12263      stopAutoRefresh : function(){
12264         if(this.autoRefreshProcId){
12265             clearInterval(this.autoRefreshProcId);
12266             delete this.autoRefreshProcId;
12267         }
12268     },
12269
12270     isAutoRefreshing : function(){
12271        return this.autoRefreshProcId ? true : false;
12272     },
12273     /**
12274      * Called to update the element to "Loading" state. Override to perform custom action.
12275      */
12276     showLoading : function(){
12277         if(this.showLoadIndicator){
12278             this.el.update(this.indicatorText);
12279         }
12280     },
12281
12282     /**
12283      * Adds unique parameter to query string if disableCaching = true
12284      * @private
12285      */
12286     prepareUrl : function(url){
12287         if(this.disableCaching){
12288             var append = "_dc=" + (new Date().getTime());
12289             if(url.indexOf("?") !== -1){
12290                 url += "&" + append;
12291             }else{
12292                 url += "?" + append;
12293             }
12294         }
12295         return url;
12296     },
12297
12298     /**
12299      * @private
12300      */
12301     processSuccess : function(response){
12302         this.transaction = null;
12303         if(response.argument.form && response.argument.reset){
12304             try{ // put in try/catch since some older FF releases had problems with this
12305                 response.argument.form.reset();
12306             }catch(e){}
12307         }
12308         if(this.loadScripts){
12309             this.renderer.render(this.el, response, this,
12310                 this.updateComplete.createDelegate(this, [response]));
12311         }else{
12312             this.renderer.render(this.el, response, this);
12313             this.updateComplete(response);
12314         }
12315     },
12316
12317     updateComplete : function(response){
12318         this.fireEvent("update", this.el, response);
12319         if(typeof response.argument.callback == "function"){
12320             response.argument.callback(this.el, true, response);
12321         }
12322     },
12323
12324     /**
12325      * @private
12326      */
12327     processFailure : function(response){
12328         this.transaction = null;
12329         this.fireEvent("failure", this.el, response);
12330         if(typeof response.argument.callback == "function"){
12331             response.argument.callback(this.el, false, response);
12332         }
12333     },
12334
12335     /**
12336      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
12337      * @param {Object} renderer The object implementing the render() method
12338      */
12339     setRenderer : function(renderer){
12340         this.renderer = renderer;
12341     },
12342
12343     getRenderer : function(){
12344        return this.renderer;
12345     },
12346
12347     /**
12348      * Set the defaultUrl used for updates
12349      * @param {String/Function} defaultUrl The url or a function to call to get the url
12350      */
12351     setDefaultUrl : function(defaultUrl){
12352         this.defaultUrl = defaultUrl;
12353     },
12354
12355     /**
12356      * Aborts the executing transaction
12357      */
12358     abort : function(){
12359         if(this.transaction){
12360             Roo.Ajax.abort(this.transaction);
12361         }
12362     },
12363
12364     /**
12365      * Returns true if an update is in progress
12366      * @return {Boolean}
12367      */
12368     isUpdating : function(){
12369         if(this.transaction){
12370             return Roo.Ajax.isLoading(this.transaction);
12371         }
12372         return false;
12373     }
12374 });
12375
12376 /**
12377  * @class Roo.UpdateManager.defaults
12378  * @static (not really - but it helps the doc tool)
12379  * The defaults collection enables customizing the default properties of UpdateManager
12380  */
12381    Roo.UpdateManager.defaults = {
12382        /**
12383          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
12384          * @type Number
12385          */
12386          timeout : 30,
12387
12388          /**
12389          * True to process scripts by default (Defaults to false).
12390          * @type Boolean
12391          */
12392         loadScripts : false,
12393
12394         /**
12395         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
12396         * @type String
12397         */
12398         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
12399         /**
12400          * Whether to append unique parameter on get request to disable caching (Defaults to false).
12401          * @type Boolean
12402          */
12403         disableCaching : false,
12404         /**
12405          * Whether to show indicatorText when loading (Defaults to true).
12406          * @type Boolean
12407          */
12408         showLoadIndicator : true,
12409         /**
12410          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12411          * @type String
12412          */
12413         indicatorText : '<div class="loading-indicator">Loading...</div>'
12414    };
12415
12416 /**
12417  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
12418  *Usage:
12419  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
12420  * @param {String/HTMLElement/Roo.Element} el The element to update
12421  * @param {String} url The url
12422  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
12423  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
12424  * @static
12425  * @deprecated
12426  * @member Roo.UpdateManager
12427  */
12428 Roo.UpdateManager.updateElement = function(el, url, params, options){
12429     var um = Roo.get(el, true).getUpdateManager();
12430     Roo.apply(um, options);
12431     um.update(url, params, options ? options.callback : null);
12432 };
12433 // alias for backwards compat
12434 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
12435 /**
12436  * @class Roo.UpdateManager.BasicRenderer
12437  * Default Content renderer. Updates the elements innerHTML with the responseText.
12438  */
12439 Roo.UpdateManager.BasicRenderer = function(){};
12440
12441 Roo.UpdateManager.BasicRenderer.prototype = {
12442     /**
12443      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
12444      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
12445      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
12446      * @param {Roo.Element} el The element being rendered
12447      * @param {Object} response The YUI Connect response object
12448      * @param {UpdateManager} updateManager The calling update manager
12449      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
12450      */
12451      render : function(el, response, updateManager, callback){
12452         el.update(response.responseText, updateManager.loadScripts, callback);
12453     }
12454 };
12455 /*
12456  * Based on:
12457  * Roo JS
12458  * (c)) Alan Knowles
12459  * Licence : LGPL
12460  */
12461
12462
12463 /**
12464  * @class Roo.DomTemplate
12465  * @extends Roo.Template
12466  * An effort at a dom based template engine..
12467  *
12468  * Similar to XTemplate, except it uses dom parsing to create the template..
12469  *
12470  * Supported features:
12471  *
12472  *  Tags:
12473
12474 <pre><code>
12475       {a_variable} - output encoded.
12476       {a_variable.format:("Y-m-d")} - call a method on the variable
12477       {a_variable:raw} - unencoded output
12478       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
12479       {a_variable:this.method_on_template(...)} - call a method on the template object.
12480  
12481 </code></pre>
12482  *  The tpl tag:
12483 <pre><code>
12484         &lt;div roo-for="a_variable or condition.."&gt;&lt;/div&gt;
12485         &lt;div roo-if="a_variable or condition"&gt;&lt;/div&gt;
12486         &lt;div roo-exec="some javascript"&gt;&lt;/div&gt;
12487         &lt;div roo-name="named_template"&gt;&lt;/div&gt; 
12488   
12489 </code></pre>
12490  *      
12491  */
12492 Roo.DomTemplate = function()
12493 {
12494      Roo.DomTemplate.superclass.constructor.apply(this, arguments);
12495      if (this.html) {
12496         this.compile();
12497      }
12498 };
12499
12500
12501 Roo.extend(Roo.DomTemplate, Roo.Template, {
12502     /**
12503      * id counter for sub templates.
12504      */
12505     id : 0,
12506     /**
12507      * flag to indicate if dom parser is inside a pre,
12508      * it will strip whitespace if not.
12509      */
12510     inPre : false,
12511     
12512     /**
12513      * The various sub templates
12514      */
12515     tpls : false,
12516     
12517     
12518     
12519     /**
12520      *
12521      * basic tag replacing syntax
12522      * WORD:WORD()
12523      *
12524      * // you can fake an object call by doing this
12525      *  x.t:(test,tesT) 
12526      * 
12527      */
12528     re : /(\{|\%7B)([\w-\.]+)(?:\:([\w\.]*)(?:\(([^)]*?)?\))?)?(\}|\%7D)/g,
12529     //re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
12530     
12531     iterChild : function (node, method) {
12532         
12533         var oldPre = this.inPre;
12534         if (node.tagName == 'PRE') {
12535             this.inPre = true;
12536         }
12537         for( var i = 0; i < node.childNodes.length; i++) {
12538             method.call(this, node.childNodes[i]);
12539         }
12540         this.inPre = oldPre;
12541     },
12542     
12543     
12544     
12545     /**
12546      * compile the template
12547      *
12548      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
12549      *
12550      */
12551     compile: function()
12552     {
12553         var s = this.html;
12554         
12555         // covert the html into DOM...
12556         var doc = false;
12557         var div =false;
12558         try {
12559             doc = document.implementation.createHTMLDocument("");
12560             doc.documentElement.innerHTML =   this.html  ;
12561             div = doc.documentElement;
12562         } catch (e) {
12563             // old IE... - nasty -- it causes all sorts of issues.. with
12564             // images getting pulled from server..
12565             div = document.createElement('div');
12566             div.innerHTML = this.html;
12567         }
12568         //doc.documentElement.innerHTML = htmlBody
12569          
12570         
12571         
12572         this.tpls = [];
12573         var _t = this;
12574         this.iterChild(div, function(n) {_t.compileNode(n, true); });
12575         
12576         var tpls = this.tpls;
12577         
12578         // create a top level template from the snippet..
12579         
12580         //Roo.log(div.innerHTML);
12581         
12582         var tpl = {
12583             uid : 'master',
12584             id : this.id++,
12585             attr : false,
12586             value : false,
12587             body : div.innerHTML,
12588             
12589             forCall : false,
12590             execCall : false,
12591             dom : div,
12592             isTop : true
12593             
12594         };
12595         tpls.unshift(tpl);
12596         
12597         
12598         // compile them...
12599         this.tpls = [];
12600         Roo.each(tpls, function(tp){
12601             this.compileTpl(tp);
12602             this.tpls[tp.id] = tp;
12603         }, this);
12604         
12605         this.master = tpls[0];
12606         return this;
12607         
12608         
12609     },
12610     
12611     compileNode : function(node, istop) {
12612         // test for
12613         //Roo.log(node);
12614         
12615         
12616         // skip anything not a tag..
12617         if (node.nodeType != 1) {
12618             if (node.nodeType == 3 && !this.inPre) {
12619                 // reduce white space..
12620                 node.nodeValue = node.nodeValue.replace(/\s+/g, ' '); 
12621                 
12622             }
12623             return;
12624         }
12625         
12626         var tpl = {
12627             uid : false,
12628             id : false,
12629             attr : false,
12630             value : false,
12631             body : '',
12632             
12633             forCall : false,
12634             execCall : false,
12635             dom : false,
12636             isTop : istop
12637             
12638             
12639         };
12640         
12641         
12642         switch(true) {
12643             case (node.hasAttribute('roo-for')): tpl.attr = 'for'; break;
12644             case (node.hasAttribute('roo-if')): tpl.attr = 'if'; break;
12645             case (node.hasAttribute('roo-name')): tpl.attr = 'name'; break;
12646             case (node.hasAttribute('roo-exec')): tpl.attr = 'exec'; break;
12647             // no default..
12648         }
12649         
12650         
12651         if (!tpl.attr) {
12652             // just itterate children..
12653             this.iterChild(node,this.compileNode);
12654             return;
12655         }
12656         tpl.uid = this.id++;
12657         tpl.value = node.getAttribute('roo-' +  tpl.attr);
12658         node.removeAttribute('roo-'+ tpl.attr);
12659         if (tpl.attr != 'name') {
12660             var placeholder = document.createTextNode('{domtpl' + tpl.uid + '}');
12661             node.parentNode.replaceChild(placeholder,  node);
12662         } else {
12663             
12664             var placeholder =  document.createElement('span');
12665             placeholder.className = 'roo-tpl-' + tpl.value;
12666             node.parentNode.replaceChild(placeholder,  node);
12667         }
12668         
12669         // parent now sees '{domtplXXXX}
12670         this.iterChild(node,this.compileNode);
12671         
12672         // we should now have node body...
12673         var div = document.createElement('div');
12674         div.appendChild(node);
12675         tpl.dom = node;
12676         // this has the unfortunate side effect of converting tagged attributes
12677         // eg. href="{...}" into %7C...%7D
12678         // this has been fixed by searching for those combo's although it's a bit hacky..
12679         
12680         
12681         tpl.body = div.innerHTML;
12682         
12683         
12684          
12685         tpl.id = tpl.uid;
12686         switch(tpl.attr) {
12687             case 'for' :
12688                 switch (tpl.value) {
12689                     case '.':  tpl.forCall = new Function('values', 'parent', 'with(values){ return values; }'); break;
12690                     case '..': tpl.forCall= new Function('values', 'parent', 'with(values){ return parent; }'); break;
12691                     default:   tpl.forCall= new Function('values', 'parent', 'with(values){ return '+tpl.value+'; }');
12692                 }
12693                 break;
12694             
12695             case 'exec':
12696                 tpl.execCall = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12697                 break;
12698             
12699             case 'if':     
12700                 tpl.ifCall = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12701                 break;
12702             
12703             case 'name':
12704                 tpl.id  = tpl.value; // replace non characters???
12705                 break;
12706             
12707         }
12708         
12709         
12710         this.tpls.push(tpl);
12711         
12712         
12713         
12714     },
12715     
12716     
12717     
12718     
12719     /**
12720      * Compile a segment of the template into a 'sub-template'
12721      *
12722      * 
12723      * 
12724      *
12725      */
12726     compileTpl : function(tpl)
12727     {
12728         var fm = Roo.util.Format;
12729         var useF = this.disableFormats !== true;
12730         
12731         var sep = Roo.isGecko ? "+\n" : ",\n";
12732         
12733         var undef = function(str) {
12734             Roo.debug && Roo.log("Property not found :"  + str);
12735             return '';
12736         };
12737           
12738         //Roo.log(tpl.body);
12739         
12740         
12741         
12742         var fn = function(m, lbrace, name, format, args)
12743         {
12744             //Roo.log("ARGS");
12745             //Roo.log(arguments);
12746             args = args ? args.replace(/\\'/g,"'") : args;
12747             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
12748             if (typeof(format) == 'undefined') {
12749                 format =  'htmlEncode'; 
12750             }
12751             if (format == 'raw' ) {
12752                 format = false;
12753             }
12754             
12755             if(name.substr(0, 6) == 'domtpl'){
12756                 return "'"+ sep +'this.applySubTemplate('+name.substr(6)+', values, parent)'+sep+"'";
12757             }
12758             
12759             // build an array of options to determine if value is undefined..
12760             
12761             // basically get 'xxxx.yyyy' then do
12762             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
12763             //    (function () { Roo.log("Property not found"); return ''; })() :
12764             //    ......
12765             
12766             var udef_ar = [];
12767             var lookfor = '';
12768             Roo.each(name.split('.'), function(st) {
12769                 lookfor += (lookfor.length ? '.': '') + st;
12770                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
12771             });
12772             
12773             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
12774             
12775             
12776             if(format && useF){
12777                 
12778                 args = args ? ',' + args : "";
12779                  
12780                 if(format.substr(0, 5) != "this."){
12781                     format = "fm." + format + '(';
12782                 }else{
12783                     format = 'this.call("'+ format.substr(5) + '", ';
12784                     args = ", values";
12785                 }
12786                 
12787                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
12788             }
12789              
12790             if (args && args.length) {
12791                 // called with xxyx.yuu:(test,test)
12792                 // change to ()
12793                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
12794             }
12795             // raw.. - :raw modifier..
12796             return "'"+ sep + udef_st  + name + ")"+sep+"'";
12797             
12798         };
12799         var body;
12800         // branched to use + in gecko and [].join() in others
12801         if(Roo.isGecko){
12802             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
12803                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
12804                     "';};};";
12805         }else{
12806             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
12807             body.push(tpl.body.replace(/(\r\n|\n)/g,
12808                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
12809             body.push("'].join('');};};");
12810             body = body.join('');
12811         }
12812         
12813         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
12814        
12815         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
12816         eval(body);
12817         
12818         return this;
12819     },
12820      
12821     /**
12822      * same as applyTemplate, except it's done to one of the subTemplates
12823      * when using named templates, you can do:
12824      *
12825      * var str = pl.applySubTemplate('your-name', values);
12826      *
12827      * 
12828      * @param {Number} id of the template
12829      * @param {Object} values to apply to template
12830      * @param {Object} parent (normaly the instance of this object)
12831      */
12832     applySubTemplate : function(id, values, parent)
12833     {
12834         
12835         
12836         var t = this.tpls[id];
12837         
12838         
12839         try { 
12840             if(t.ifCall && !t.ifCall.call(this, values, parent)){
12841                 Roo.debug && Roo.log('if call on ' + t.value + ' return false');
12842                 return '';
12843             }
12844         } catch(e) {
12845             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-if="' + t.value + '" - ' + e.toString());
12846             Roo.log(values);
12847           
12848             return '';
12849         }
12850         try { 
12851             
12852             if(t.execCall && t.execCall.call(this, values, parent)){
12853                 return '';
12854             }
12855         } catch(e) {
12856             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12857             Roo.log(values);
12858             return '';
12859         }
12860         
12861         try {
12862             var vs = t.forCall ? t.forCall.call(this, values, parent) : values;
12863             parent = t.target ? values : parent;
12864             if(t.forCall && vs instanceof Array){
12865                 var buf = [];
12866                 for(var i = 0, len = vs.length; i < len; i++){
12867                     try {
12868                         buf[buf.length] = t.compiled.call(this, vs[i], parent);
12869                     } catch (e) {
12870                         Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12871                         Roo.log(e.body);
12872                         //Roo.log(t.compiled);
12873                         Roo.log(vs[i]);
12874                     }   
12875                 }
12876                 return buf.join('');
12877             }
12878         } catch (e) {
12879             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12880             Roo.log(values);
12881             return '';
12882         }
12883         try {
12884             return t.compiled.call(this, vs, parent);
12885         } catch (e) {
12886             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12887             Roo.log(e.body);
12888             //Roo.log(t.compiled);
12889             Roo.log(values);
12890             return '';
12891         }
12892     },
12893
12894    
12895
12896     applyTemplate : function(values){
12897         return this.master.compiled.call(this, values, {});
12898         //var s = this.subs;
12899     },
12900
12901     apply : function(){
12902         return this.applyTemplate.apply(this, arguments);
12903     }
12904
12905  });
12906
12907 Roo.DomTemplate.from = function(el){
12908     el = Roo.getDom(el);
12909     return new Roo.Domtemplate(el.value || el.innerHTML);
12910 };/*
12911  * Based on:
12912  * Ext JS Library 1.1.1
12913  * Copyright(c) 2006-2007, Ext JS, LLC.
12914  *
12915  * Originally Released Under LGPL - original licence link has changed is not relivant.
12916  *
12917  * Fork - LGPL
12918  * <script type="text/javascript">
12919  */
12920
12921 /**
12922  * @class Roo.util.DelayedTask
12923  * Provides a convenient method of performing setTimeout where a new
12924  * timeout cancels the old timeout. An example would be performing validation on a keypress.
12925  * You can use this class to buffer
12926  * the keypress events for a certain number of milliseconds, and perform only if they stop
12927  * for that amount of time.
12928  * @constructor The parameters to this constructor serve as defaults and are not required.
12929  * @param {Function} fn (optional) The default function to timeout
12930  * @param {Object} scope (optional) The default scope of that timeout
12931  * @param {Array} args (optional) The default Array of arguments
12932  */
12933 Roo.util.DelayedTask = function(fn, scope, args){
12934     var id = null, d, t;
12935
12936     var call = function(){
12937         var now = new Date().getTime();
12938         if(now - t >= d){
12939             clearInterval(id);
12940             id = null;
12941             fn.apply(scope, args || []);
12942         }
12943     };
12944     /**
12945      * Cancels any pending timeout and queues a new one
12946      * @param {Number} delay The milliseconds to delay
12947      * @param {Function} newFn (optional) Overrides function passed to constructor
12948      * @param {Object} newScope (optional) Overrides scope passed to constructor
12949      * @param {Array} newArgs (optional) Overrides args passed to constructor
12950      */
12951     this.delay = function(delay, newFn, newScope, newArgs){
12952         if(id && delay != d){
12953             this.cancel();
12954         }
12955         d = delay;
12956         t = new Date().getTime();
12957         fn = newFn || fn;
12958         scope = newScope || scope;
12959         args = newArgs || args;
12960         if(!id){
12961             id = setInterval(call, d);
12962         }
12963     };
12964
12965     /**
12966      * Cancel the last queued timeout
12967      */
12968     this.cancel = function(){
12969         if(id){
12970             clearInterval(id);
12971             id = null;
12972         }
12973     };
12974 };/*
12975  * Based on:
12976  * Ext JS Library 1.1.1
12977  * Copyright(c) 2006-2007, Ext JS, LLC.
12978  *
12979  * Originally Released Under LGPL - original licence link has changed is not relivant.
12980  *
12981  * Fork - LGPL
12982  * <script type="text/javascript">
12983  */
12984  
12985  
12986 Roo.util.TaskRunner = function(interval){
12987     interval = interval || 10;
12988     var tasks = [], removeQueue = [];
12989     var id = 0;
12990     var running = false;
12991
12992     var stopThread = function(){
12993         running = false;
12994         clearInterval(id);
12995         id = 0;
12996     };
12997
12998     var startThread = function(){
12999         if(!running){
13000             running = true;
13001             id = setInterval(runTasks, interval);
13002         }
13003     };
13004
13005     var removeTask = function(task){
13006         removeQueue.push(task);
13007         if(task.onStop){
13008             task.onStop();
13009         }
13010     };
13011
13012     var runTasks = function(){
13013         if(removeQueue.length > 0){
13014             for(var i = 0, len = removeQueue.length; i < len; i++){
13015                 tasks.remove(removeQueue[i]);
13016             }
13017             removeQueue = [];
13018             if(tasks.length < 1){
13019                 stopThread();
13020                 return;
13021             }
13022         }
13023         var now = new Date().getTime();
13024         for(var i = 0, len = tasks.length; i < len; ++i){
13025             var t = tasks[i];
13026             var itime = now - t.taskRunTime;
13027             if(t.interval <= itime){
13028                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
13029                 t.taskRunTime = now;
13030                 if(rt === false || t.taskRunCount === t.repeat){
13031                     removeTask(t);
13032                     return;
13033                 }
13034             }
13035             if(t.duration && t.duration <= (now - t.taskStartTime)){
13036                 removeTask(t);
13037             }
13038         }
13039     };
13040
13041     /**
13042      * Queues a new task.
13043      * @param {Object} task
13044      */
13045     this.start = function(task){
13046         tasks.push(task);
13047         task.taskStartTime = new Date().getTime();
13048         task.taskRunTime = 0;
13049         task.taskRunCount = 0;
13050         startThread();
13051         return task;
13052     };
13053
13054     this.stop = function(task){
13055         removeTask(task);
13056         return task;
13057     };
13058
13059     this.stopAll = function(){
13060         stopThread();
13061         for(var i = 0, len = tasks.length; i < len; i++){
13062             if(tasks[i].onStop){
13063                 tasks[i].onStop();
13064             }
13065         }
13066         tasks = [];
13067         removeQueue = [];
13068     };
13069 };
13070
13071 Roo.TaskMgr = new Roo.util.TaskRunner();/*
13072  * Based on:
13073  * Ext JS Library 1.1.1
13074  * Copyright(c) 2006-2007, Ext JS, LLC.
13075  *
13076  * Originally Released Under LGPL - original licence link has changed is not relivant.
13077  *
13078  * Fork - LGPL
13079  * <script type="text/javascript">
13080  */
13081
13082  
13083 /**
13084  * @class Roo.util.MixedCollection
13085  * @extends Roo.util.Observable
13086  * A Collection class that maintains both numeric indexes and keys and exposes events.
13087  * @constructor
13088  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
13089  * collection (defaults to false)
13090  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
13091  * and return the key value for that item.  This is used when available to look up the key on items that
13092  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
13093  * equivalent to providing an implementation for the {@link #getKey} method.
13094  */
13095 Roo.util.MixedCollection = function(allowFunctions, keyFn){
13096     this.items = [];
13097     this.map = {};
13098     this.keys = [];
13099     this.length = 0;
13100     this.addEvents({
13101         /**
13102          * @event clear
13103          * Fires when the collection is cleared.
13104          */
13105         "clear" : true,
13106         /**
13107          * @event add
13108          * Fires when an item is added to the collection.
13109          * @param {Number} index The index at which the item was added.
13110          * @param {Object} o The item added.
13111          * @param {String} key The key associated with the added item.
13112          */
13113         "add" : true,
13114         /**
13115          * @event replace
13116          * Fires when an item is replaced in the collection.
13117          * @param {String} key he key associated with the new added.
13118          * @param {Object} old The item being replaced.
13119          * @param {Object} new The new item.
13120          */
13121         "replace" : true,
13122         /**
13123          * @event remove
13124          * Fires when an item is removed from the collection.
13125          * @param {Object} o The item being removed.
13126          * @param {String} key (optional) The key associated with the removed item.
13127          */
13128         "remove" : true,
13129         "sort" : true
13130     });
13131     this.allowFunctions = allowFunctions === true;
13132     if(keyFn){
13133         this.getKey = keyFn;
13134     }
13135     Roo.util.MixedCollection.superclass.constructor.call(this);
13136 };
13137
13138 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
13139     allowFunctions : false,
13140     
13141 /**
13142  * Adds an item to the collection.
13143  * @param {String} key The key to associate with the item
13144  * @param {Object} o The item to add.
13145  * @return {Object} The item added.
13146  */
13147     add : function(key, o){
13148         if(arguments.length == 1){
13149             o = arguments[0];
13150             key = this.getKey(o);
13151         }
13152         if(typeof key == "undefined" || key === null){
13153             this.length++;
13154             this.items.push(o);
13155             this.keys.push(null);
13156         }else{
13157             var old = this.map[key];
13158             if(old){
13159                 return this.replace(key, o);
13160             }
13161             this.length++;
13162             this.items.push(o);
13163             this.map[key] = o;
13164             this.keys.push(key);
13165         }
13166         this.fireEvent("add", this.length-1, o, key);
13167         return o;
13168     },
13169        
13170 /**
13171   * MixedCollection has a generic way to fetch keys if you implement getKey.
13172 <pre><code>
13173 // normal way
13174 var mc = new Roo.util.MixedCollection();
13175 mc.add(someEl.dom.id, someEl);
13176 mc.add(otherEl.dom.id, otherEl);
13177 //and so on
13178
13179 // using getKey
13180 var mc = new Roo.util.MixedCollection();
13181 mc.getKey = function(el){
13182    return el.dom.id;
13183 };
13184 mc.add(someEl);
13185 mc.add(otherEl);
13186
13187 // or via the constructor
13188 var mc = new Roo.util.MixedCollection(false, function(el){
13189    return el.dom.id;
13190 });
13191 mc.add(someEl);
13192 mc.add(otherEl);
13193 </code></pre>
13194  * @param o {Object} The item for which to find the key.
13195  * @return {Object} The key for the passed item.
13196  */
13197     getKey : function(o){
13198          return o.id; 
13199     },
13200    
13201 /**
13202  * Replaces an item in the collection.
13203  * @param {String} key The key associated with the item to replace, or the item to replace.
13204  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
13205  * @return {Object}  The new item.
13206  */
13207     replace : function(key, o){
13208         if(arguments.length == 1){
13209             o = arguments[0];
13210             key = this.getKey(o);
13211         }
13212         var old = this.item(key);
13213         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
13214              return this.add(key, o);
13215         }
13216         var index = this.indexOfKey(key);
13217         this.items[index] = o;
13218         this.map[key] = o;
13219         this.fireEvent("replace", key, old, o);
13220         return o;
13221     },
13222    
13223 /**
13224  * Adds all elements of an Array or an Object to the collection.
13225  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
13226  * an Array of values, each of which are added to the collection.
13227  */
13228     addAll : function(objs){
13229         if(arguments.length > 1 || objs instanceof Array){
13230             var args = arguments.length > 1 ? arguments : objs;
13231             for(var i = 0, len = args.length; i < len; i++){
13232                 this.add(args[i]);
13233             }
13234         }else{
13235             for(var key in objs){
13236                 if(this.allowFunctions || typeof objs[key] != "function"){
13237                     this.add(key, objs[key]);
13238                 }
13239             }
13240         }
13241     },
13242    
13243 /**
13244  * Executes the specified function once for every item in the collection, passing each
13245  * item as the first and only parameter. returning false from the function will stop the iteration.
13246  * @param {Function} fn The function to execute for each item.
13247  * @param {Object} scope (optional) The scope in which to execute the function.
13248  */
13249     each : function(fn, scope){
13250         var items = [].concat(this.items); // each safe for removal
13251         for(var i = 0, len = items.length; i < len; i++){
13252             if(fn.call(scope || items[i], items[i], i, len) === false){
13253                 break;
13254             }
13255         }
13256     },
13257    
13258 /**
13259  * Executes the specified function once for every key in the collection, passing each
13260  * key, and its associated item as the first two parameters.
13261  * @param {Function} fn The function to execute for each item.
13262  * @param {Object} scope (optional) The scope in which to execute the function.
13263  */
13264     eachKey : function(fn, scope){
13265         for(var i = 0, len = this.keys.length; i < len; i++){
13266             fn.call(scope || window, this.keys[i], this.items[i], i, len);
13267         }
13268     },
13269    
13270 /**
13271  * Returns the first item in the collection which elicits a true return value from the
13272  * passed selection function.
13273  * @param {Function} fn The selection function to execute for each item.
13274  * @param {Object} scope (optional) The scope in which to execute the function.
13275  * @return {Object} The first item in the collection which returned true from the selection function.
13276  */
13277     find : function(fn, scope){
13278         for(var i = 0, len = this.items.length; i < len; i++){
13279             if(fn.call(scope || window, this.items[i], this.keys[i])){
13280                 return this.items[i];
13281             }
13282         }
13283         return null;
13284     },
13285    
13286 /**
13287  * Inserts an item at the specified index in the collection.
13288  * @param {Number} index The index to insert the item at.
13289  * @param {String} key The key to associate with the new item, or the item itself.
13290  * @param {Object} o  (optional) If the second parameter was a key, the new item.
13291  * @return {Object} The item inserted.
13292  */
13293     insert : function(index, key, o){
13294         if(arguments.length == 2){
13295             o = arguments[1];
13296             key = this.getKey(o);
13297         }
13298         if(index >= this.length){
13299             return this.add(key, o);
13300         }
13301         this.length++;
13302         this.items.splice(index, 0, o);
13303         if(typeof key != "undefined" && key != null){
13304             this.map[key] = o;
13305         }
13306         this.keys.splice(index, 0, key);
13307         this.fireEvent("add", index, o, key);
13308         return o;
13309     },
13310    
13311 /**
13312  * Removed an item from the collection.
13313  * @param {Object} o The item to remove.
13314  * @return {Object} The item removed.
13315  */
13316     remove : function(o){
13317         return this.removeAt(this.indexOf(o));
13318     },
13319    
13320 /**
13321  * Remove an item from a specified index in the collection.
13322  * @param {Number} index The index within the collection of the item to remove.
13323  */
13324     removeAt : function(index){
13325         if(index < this.length && index >= 0){
13326             this.length--;
13327             var o = this.items[index];
13328             this.items.splice(index, 1);
13329             var key = this.keys[index];
13330             if(typeof key != "undefined"){
13331                 delete this.map[key];
13332             }
13333             this.keys.splice(index, 1);
13334             this.fireEvent("remove", o, key);
13335         }
13336     },
13337    
13338 /**
13339  * Removed an item associated with the passed key fom the collection.
13340  * @param {String} key The key of the item to remove.
13341  */
13342     removeKey : function(key){
13343         return this.removeAt(this.indexOfKey(key));
13344     },
13345    
13346 /**
13347  * Returns the number of items in the collection.
13348  * @return {Number} the number of items in the collection.
13349  */
13350     getCount : function(){
13351         return this.length; 
13352     },
13353    
13354 /**
13355  * Returns index within the collection of the passed Object.
13356  * @param {Object} o The item to find the index of.
13357  * @return {Number} index of the item.
13358  */
13359     indexOf : function(o){
13360         if(!this.items.indexOf){
13361             for(var i = 0, len = this.items.length; i < len; i++){
13362                 if(this.items[i] == o) {
13363                     return i;
13364                 }
13365             }
13366             return -1;
13367         }else{
13368             return this.items.indexOf(o);
13369         }
13370     },
13371    
13372 /**
13373  * Returns index within the collection of the passed key.
13374  * @param {String} key The key to find the index of.
13375  * @return {Number} index of the key.
13376  */
13377     indexOfKey : function(key){
13378         if(!this.keys.indexOf){
13379             for(var i = 0, len = this.keys.length; i < len; i++){
13380                 if(this.keys[i] == key) {
13381                     return i;
13382                 }
13383             }
13384             return -1;
13385         }else{
13386             return this.keys.indexOf(key);
13387         }
13388     },
13389    
13390 /**
13391  * Returns the item associated with the passed key OR index. Key has priority over index.
13392  * @param {String/Number} key The key or index of the item.
13393  * @return {Object} The item associated with the passed key.
13394  */
13395     item : function(key){
13396         if (key === 'length') {
13397             return null;
13398         }
13399         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
13400         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
13401     },
13402     
13403 /**
13404  * Returns the item at the specified index.
13405  * @param {Number} index The index of the item.
13406  * @return {Object}
13407  */
13408     itemAt : function(index){
13409         return this.items[index];
13410     },
13411     
13412 /**
13413  * Returns the item associated with the passed key.
13414  * @param {String/Number} key The key of the item.
13415  * @return {Object} The item associated with the passed key.
13416  */
13417     key : function(key){
13418         return this.map[key];
13419     },
13420    
13421 /**
13422  * Returns true if the collection contains the passed Object as an item.
13423  * @param {Object} o  The Object to look for in the collection.
13424  * @return {Boolean} True if the collection contains the Object as an item.
13425  */
13426     contains : function(o){
13427         return this.indexOf(o) != -1;
13428     },
13429    
13430 /**
13431  * Returns true if the collection contains the passed Object as a key.
13432  * @param {String} key The key to look for in the collection.
13433  * @return {Boolean} True if the collection contains the Object as a key.
13434  */
13435     containsKey : function(key){
13436         return typeof this.map[key] != "undefined";
13437     },
13438    
13439 /**
13440  * Removes all items from the collection.
13441  */
13442     clear : function(){
13443         this.length = 0;
13444         this.items = [];
13445         this.keys = [];
13446         this.map = {};
13447         this.fireEvent("clear");
13448     },
13449    
13450 /**
13451  * Returns the first item in the collection.
13452  * @return {Object} the first item in the collection..
13453  */
13454     first : function(){
13455         return this.items[0]; 
13456     },
13457    
13458 /**
13459  * Returns the last item in the collection.
13460  * @return {Object} the last item in the collection..
13461  */
13462     last : function(){
13463         return this.items[this.length-1];   
13464     },
13465     
13466     _sort : function(property, dir, fn){
13467         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
13468         fn = fn || function(a, b){
13469             return a-b;
13470         };
13471         var c = [], k = this.keys, items = this.items;
13472         for(var i = 0, len = items.length; i < len; i++){
13473             c[c.length] = {key: k[i], value: items[i], index: i};
13474         }
13475         c.sort(function(a, b){
13476             var v = fn(a[property], b[property]) * dsc;
13477             if(v == 0){
13478                 v = (a.index < b.index ? -1 : 1);
13479             }
13480             return v;
13481         });
13482         for(var i = 0, len = c.length; i < len; i++){
13483             items[i] = c[i].value;
13484             k[i] = c[i].key;
13485         }
13486         this.fireEvent("sort", this);
13487     },
13488     
13489     /**
13490      * Sorts this collection with the passed comparison function
13491      * @param {String} direction (optional) "ASC" or "DESC"
13492      * @param {Function} fn (optional) comparison function
13493      */
13494     sort : function(dir, fn){
13495         this._sort("value", dir, fn);
13496     },
13497     
13498     /**
13499      * Sorts this collection by keys
13500      * @param {String} direction (optional) "ASC" or "DESC"
13501      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
13502      */
13503     keySort : function(dir, fn){
13504         this._sort("key", dir, fn || function(a, b){
13505             return String(a).toUpperCase()-String(b).toUpperCase();
13506         });
13507     },
13508     
13509     /**
13510      * Returns a range of items in this collection
13511      * @param {Number} startIndex (optional) defaults to 0
13512      * @param {Number} endIndex (optional) default to the last item
13513      * @return {Array} An array of items
13514      */
13515     getRange : function(start, end){
13516         var items = this.items;
13517         if(items.length < 1){
13518             return [];
13519         }
13520         start = start || 0;
13521         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
13522         var r = [];
13523         if(start <= end){
13524             for(var i = start; i <= end; i++) {
13525                     r[r.length] = items[i];
13526             }
13527         }else{
13528             for(var i = start; i >= end; i--) {
13529                     r[r.length] = items[i];
13530             }
13531         }
13532         return r;
13533     },
13534         
13535     /**
13536      * Filter the <i>objects</i> in this collection by a specific property. 
13537      * Returns a new collection that has been filtered.
13538      * @param {String} property A property on your objects
13539      * @param {String/RegExp} value Either string that the property values 
13540      * should start with or a RegExp to test against the property
13541      * @return {MixedCollection} The new filtered collection
13542      */
13543     filter : function(property, value){
13544         if(!value.exec){ // not a regex
13545             value = String(value);
13546             if(value.length == 0){
13547                 return this.clone();
13548             }
13549             value = new RegExp("^" + Roo.escapeRe(value), "i");
13550         }
13551         return this.filterBy(function(o){
13552             return o && value.test(o[property]);
13553         });
13554         },
13555     
13556     /**
13557      * Filter by a function. * Returns a new collection that has been filtered.
13558      * The passed function will be called with each 
13559      * object in the collection. If the function returns true, the value is included 
13560      * otherwise it is filtered.
13561      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
13562      * @param {Object} scope (optional) The scope of the function (defaults to this) 
13563      * @return {MixedCollection} The new filtered collection
13564      */
13565     filterBy : function(fn, scope){
13566         var r = new Roo.util.MixedCollection();
13567         r.getKey = this.getKey;
13568         var k = this.keys, it = this.items;
13569         for(var i = 0, len = it.length; i < len; i++){
13570             if(fn.call(scope||this, it[i], k[i])){
13571                                 r.add(k[i], it[i]);
13572                         }
13573         }
13574         return r;
13575     },
13576     
13577     /**
13578      * Creates a duplicate of this collection
13579      * @return {MixedCollection}
13580      */
13581     clone : function(){
13582         var r = new Roo.util.MixedCollection();
13583         var k = this.keys, it = this.items;
13584         for(var i = 0, len = it.length; i < len; i++){
13585             r.add(k[i], it[i]);
13586         }
13587         r.getKey = this.getKey;
13588         return r;
13589     }
13590 });
13591 /**
13592  * Returns the item associated with the passed key or index.
13593  * @method
13594  * @param {String/Number} key The key or index of the item.
13595  * @return {Object} The item associated with the passed key.
13596  */
13597 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
13598  * Based on:
13599  * Ext JS Library 1.1.1
13600  * Copyright(c) 2006-2007, Ext JS, LLC.
13601  *
13602  * Originally Released Under LGPL - original licence link has changed is not relivant.
13603  *
13604  * Fork - LGPL
13605  * <script type="text/javascript">
13606  */
13607 /**
13608  * @class Roo.util.JSON
13609  * Modified version of Douglas Crockford"s json.js that doesn"t
13610  * mess with the Object prototype 
13611  * http://www.json.org/js.html
13612  * @singleton
13613  */
13614 Roo.util.JSON = new (function(){
13615     var useHasOwn = {}.hasOwnProperty ? true : false;
13616     
13617     // crashes Safari in some instances
13618     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
13619     
13620     var pad = function(n) {
13621         return n < 10 ? "0" + n : n;
13622     };
13623     
13624     var m = {
13625         "\b": '\\b',
13626         "\t": '\\t',
13627         "\n": '\\n',
13628         "\f": '\\f',
13629         "\r": '\\r',
13630         '"' : '\\"',
13631         "\\": '\\\\'
13632     };
13633
13634     var encodeString = function(s){
13635         if (/["\\\x00-\x1f]/.test(s)) {
13636             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
13637                 var c = m[b];
13638                 if(c){
13639                     return c;
13640                 }
13641                 c = b.charCodeAt();
13642                 return "\\u00" +
13643                     Math.floor(c / 16).toString(16) +
13644                     (c % 16).toString(16);
13645             }) + '"';
13646         }
13647         return '"' + s + '"';
13648     };
13649     
13650     var encodeArray = function(o){
13651         var a = ["["], b, i, l = o.length, v;
13652             for (i = 0; i < l; i += 1) {
13653                 v = o[i];
13654                 switch (typeof v) {
13655                     case "undefined":
13656                     case "function":
13657                     case "unknown":
13658                         break;
13659                     default:
13660                         if (b) {
13661                             a.push(',');
13662                         }
13663                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
13664                         b = true;
13665                 }
13666             }
13667             a.push("]");
13668             return a.join("");
13669     };
13670     
13671     var encodeDate = function(o){
13672         return '"' + o.getFullYear() + "-" +
13673                 pad(o.getMonth() + 1) + "-" +
13674                 pad(o.getDate()) + "T" +
13675                 pad(o.getHours()) + ":" +
13676                 pad(o.getMinutes()) + ":" +
13677                 pad(o.getSeconds()) + '"';
13678     };
13679     
13680     /**
13681      * Encodes an Object, Array or other value
13682      * @param {Mixed} o The variable to encode
13683      * @return {String} The JSON string
13684      */
13685     this.encode = function(o)
13686     {
13687         // should this be extended to fully wrap stringify..
13688         
13689         if(typeof o == "undefined" || o === null){
13690             return "null";
13691         }else if(o instanceof Array){
13692             return encodeArray(o);
13693         }else if(o instanceof Date){
13694             return encodeDate(o);
13695         }else if(typeof o == "string"){
13696             return encodeString(o);
13697         }else if(typeof o == "number"){
13698             return isFinite(o) ? String(o) : "null";
13699         }else if(typeof o == "boolean"){
13700             return String(o);
13701         }else {
13702             var a = ["{"], b, i, v;
13703             for (i in o) {
13704                 if(!useHasOwn || o.hasOwnProperty(i)) {
13705                     v = o[i];
13706                     switch (typeof v) {
13707                     case "undefined":
13708                     case "function":
13709                     case "unknown":
13710                         break;
13711                     default:
13712                         if(b){
13713                             a.push(',');
13714                         }
13715                         a.push(this.encode(i), ":",
13716                                 v === null ? "null" : this.encode(v));
13717                         b = true;
13718                     }
13719                 }
13720             }
13721             a.push("}");
13722             return a.join("");
13723         }
13724     };
13725     
13726     /**
13727      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
13728      * @param {String} json The JSON string
13729      * @return {Object} The resulting object
13730      */
13731     this.decode = function(json){
13732         
13733         return  /** eval:var:json */ eval("(" + json + ')');
13734     };
13735 })();
13736 /** 
13737  * Shorthand for {@link Roo.util.JSON#encode}
13738  * @member Roo encode 
13739  * @method */
13740 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
13741 /** 
13742  * Shorthand for {@link Roo.util.JSON#decode}
13743  * @member Roo decode 
13744  * @method */
13745 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
13746 /*
13747  * Based on:
13748  * Ext JS Library 1.1.1
13749  * Copyright(c) 2006-2007, Ext JS, LLC.
13750  *
13751  * Originally Released Under LGPL - original licence link has changed is not relivant.
13752  *
13753  * Fork - LGPL
13754  * <script type="text/javascript">
13755  */
13756  
13757 /**
13758  * @class Roo.util.Format
13759  * Reusable data formatting functions
13760  * @singleton
13761  */
13762 Roo.util.Format = function(){
13763     var trimRe = /^\s+|\s+$/g;
13764     return {
13765         /**
13766          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
13767          * @param {String} value The string to truncate
13768          * @param {Number} length The maximum length to allow before truncating
13769          * @return {String} The converted text
13770          */
13771         ellipsis : function(value, len){
13772             if(value && value.length > len){
13773                 return value.substr(0, len-3)+"...";
13774             }
13775             return value;
13776         },
13777
13778         /**
13779          * Checks a reference and converts it to empty string if it is undefined
13780          * @param {Mixed} value Reference to check
13781          * @return {Mixed} Empty string if converted, otherwise the original value
13782          */
13783         undef : function(value){
13784             return typeof value != "undefined" ? value : "";
13785         },
13786
13787         /**
13788          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
13789          * @param {String} value The string to encode
13790          * @return {String} The encoded text
13791          */
13792         htmlEncode : function(value){
13793             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
13794         },
13795
13796         /**
13797          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
13798          * @param {String} value The string to decode
13799          * @return {String} The decoded text
13800          */
13801         htmlDecode : function(value){
13802             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
13803         },
13804
13805         /**
13806          * Trims any whitespace from either side of a string
13807          * @param {String} value The text to trim
13808          * @return {String} The trimmed text
13809          */
13810         trim : function(value){
13811             return String(value).replace(trimRe, "");
13812         },
13813
13814         /**
13815          * Returns a substring from within an original string
13816          * @param {String} value The original text
13817          * @param {Number} start The start index of the substring
13818          * @param {Number} length The length of the substring
13819          * @return {String} The substring
13820          */
13821         substr : function(value, start, length){
13822             return String(value).substr(start, length);
13823         },
13824
13825         /**
13826          * Converts a string to all lower case letters
13827          * @param {String} value The text to convert
13828          * @return {String} The converted text
13829          */
13830         lowercase : function(value){
13831             return String(value).toLowerCase();
13832         },
13833
13834         /**
13835          * Converts a string to all upper case letters
13836          * @param {String} value The text to convert
13837          * @return {String} The converted text
13838          */
13839         uppercase : function(value){
13840             return String(value).toUpperCase();
13841         },
13842
13843         /**
13844          * Converts the first character only of a string to upper case
13845          * @param {String} value The text to convert
13846          * @return {String} The converted text
13847          */
13848         capitalize : function(value){
13849             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
13850         },
13851
13852         // private
13853         call : function(value, fn){
13854             if(arguments.length > 2){
13855                 var args = Array.prototype.slice.call(arguments, 2);
13856                 args.unshift(value);
13857                  
13858                 return /** eval:var:value */  eval(fn).apply(window, args);
13859             }else{
13860                 /** eval:var:value */
13861                 return /** eval:var:value */ eval(fn).call(window, value);
13862             }
13863         },
13864
13865        
13866         /**
13867          * safer version of Math.toFixed..??/
13868          * @param {Number/String} value The numeric value to format
13869          * @param {Number/String} value Decimal places 
13870          * @return {String} The formatted currency string
13871          */
13872         toFixed : function(v, n)
13873         {
13874             // why not use to fixed - precision is buggered???
13875             if (!n) {
13876                 return Math.round(v-0);
13877             }
13878             var fact = Math.pow(10,n+1);
13879             v = (Math.round((v-0)*fact))/fact;
13880             var z = (''+fact).substring(2);
13881             if (v == Math.floor(v)) {
13882                 return Math.floor(v) + '.' + z;
13883             }
13884             
13885             // now just padd decimals..
13886             var ps = String(v).split('.');
13887             var fd = (ps[1] + z);
13888             var r = fd.substring(0,n); 
13889             var rm = fd.substring(n); 
13890             if (rm < 5) {
13891                 return ps[0] + '.' + r;
13892             }
13893             r*=1; // turn it into a number;
13894             r++;
13895             if (String(r).length != n) {
13896                 ps[0]*=1;
13897                 ps[0]++;
13898                 r = String(r).substring(1); // chop the end off.
13899             }
13900             
13901             return ps[0] + '.' + r;
13902              
13903         },
13904         
13905         /**
13906          * Format a number as US currency
13907          * @param {Number/String} value The numeric value to format
13908          * @return {String} The formatted currency string
13909          */
13910         usMoney : function(v){
13911             return '$' + Roo.util.Format.number(v);
13912         },
13913         
13914         /**
13915          * Format a number
13916          * eventually this should probably emulate php's number_format
13917          * @param {Number/String} value The numeric value to format
13918          * @param {Number} decimals number of decimal places
13919          * @param {String} delimiter for thousands (default comma)
13920          * @return {String} The formatted currency string
13921          */
13922         number : function(v, decimals, thousandsDelimiter)
13923         {
13924             // multiply and round.
13925             decimals = typeof(decimals) == 'undefined' ? 2 : decimals;
13926             thousandsDelimiter = typeof(thousandsDelimiter) == 'undefined' ? ',' : thousandsDelimiter;
13927             
13928             var mul = Math.pow(10, decimals);
13929             var zero = String(mul).substring(1);
13930             v = (Math.round((v-0)*mul))/mul;
13931             
13932             // if it's '0' number.. then
13933             
13934             //v = (v == Math.floor(v)) ? v + "." + zero : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
13935             v = String(v);
13936             var ps = v.split('.');
13937             var whole = ps[0];
13938             
13939             var r = /(\d+)(\d{3})/;
13940             // add comma's
13941             
13942             if(thousandsDelimiter.length != 0) {
13943                 whole = whole.replace(/\B(?=(\d{3})+(?!\d))/g, thousandsDelimiter );
13944             } 
13945             
13946             var sub = ps[1] ?
13947                     // has decimals..
13948                     (decimals ?  ('.'+ ps[1] + zero.substring(ps[1].length)) : '') :
13949                     // does not have decimals
13950                     (decimals ? ('.' + zero) : '');
13951             
13952             
13953             return whole + sub ;
13954         },
13955         
13956         /**
13957          * Parse a value into a formatted date using the specified format pattern.
13958          * @param {Mixed} value The value to format
13959          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
13960          * @return {String} The formatted date string
13961          */
13962         date : function(v, format){
13963             if(!v){
13964                 return "";
13965             }
13966             if(!(v instanceof Date)){
13967                 v = new Date(Date.parse(v));
13968             }
13969             return v.dateFormat(format || Roo.util.Format.defaults.date);
13970         },
13971
13972         /**
13973          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
13974          * @param {String} format Any valid date format string
13975          * @return {Function} The date formatting function
13976          */
13977         dateRenderer : function(format){
13978             return function(v){
13979                 return Roo.util.Format.date(v, format);  
13980             };
13981         },
13982
13983         // private
13984         stripTagsRE : /<\/?[^>]+>/gi,
13985         
13986         /**
13987          * Strips all HTML tags
13988          * @param {Mixed} value The text from which to strip tags
13989          * @return {String} The stripped text
13990          */
13991         stripTags : function(v){
13992             return !v ? v : String(v).replace(this.stripTagsRE, "");
13993         }
13994     };
13995 }();
13996 Roo.util.Format.defaults = {
13997     date : 'd/M/Y'
13998 };/*
13999  * Based on:
14000  * Ext JS Library 1.1.1
14001  * Copyright(c) 2006-2007, Ext JS, LLC.
14002  *
14003  * Originally Released Under LGPL - original licence link has changed is not relivant.
14004  *
14005  * Fork - LGPL
14006  * <script type="text/javascript">
14007  */
14008
14009
14010  
14011
14012 /**
14013  * @class Roo.MasterTemplate
14014  * @extends Roo.Template
14015  * Provides a template that can have child templates. The syntax is:
14016 <pre><code>
14017 var t = new Roo.MasterTemplate(
14018         '&lt;select name="{name}"&gt;',
14019                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
14020         '&lt;/select&gt;'
14021 );
14022 t.add('options', {value: 'foo', text: 'bar'});
14023 // or you can add multiple child elements in one shot
14024 t.addAll('options', [
14025     {value: 'foo', text: 'bar'},
14026     {value: 'foo2', text: 'bar2'},
14027     {value: 'foo3', text: 'bar3'}
14028 ]);
14029 // then append, applying the master template values
14030 t.append('my-form', {name: 'my-select'});
14031 </code></pre>
14032 * A name attribute for the child template is not required if you have only one child
14033 * template or you want to refer to them by index.
14034  */
14035 Roo.MasterTemplate = function(){
14036     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
14037     this.originalHtml = this.html;
14038     var st = {};
14039     var m, re = this.subTemplateRe;
14040     re.lastIndex = 0;
14041     var subIndex = 0;
14042     while(m = re.exec(this.html)){
14043         var name = m[1], content = m[2];
14044         st[subIndex] = {
14045             name: name,
14046             index: subIndex,
14047             buffer: [],
14048             tpl : new Roo.Template(content)
14049         };
14050         if(name){
14051             st[name] = st[subIndex];
14052         }
14053         st[subIndex].tpl.compile();
14054         st[subIndex].tpl.call = this.call.createDelegate(this);
14055         subIndex++;
14056     }
14057     this.subCount = subIndex;
14058     this.subs = st;
14059 };
14060 Roo.extend(Roo.MasterTemplate, Roo.Template, {
14061     /**
14062     * The regular expression used to match sub templates
14063     * @type RegExp
14064     * @property
14065     */
14066     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
14067
14068     /**
14069      * Applies the passed values to a child template.
14070      * @param {String/Number} name (optional) The name or index of the child template
14071      * @param {Array/Object} values The values to be applied to the template
14072      * @return {MasterTemplate} this
14073      */
14074      add : function(name, values){
14075         if(arguments.length == 1){
14076             values = arguments[0];
14077             name = 0;
14078         }
14079         var s = this.subs[name];
14080         s.buffer[s.buffer.length] = s.tpl.apply(values);
14081         return this;
14082     },
14083
14084     /**
14085      * Applies all the passed values to a child template.
14086      * @param {String/Number} name (optional) The name or index of the child template
14087      * @param {Array} values The values to be applied to the template, this should be an array of objects.
14088      * @param {Boolean} reset (optional) True to reset the template first
14089      * @return {MasterTemplate} this
14090      */
14091     fill : function(name, values, reset){
14092         var a = arguments;
14093         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
14094             values = a[0];
14095             name = 0;
14096             reset = a[1];
14097         }
14098         if(reset){
14099             this.reset();
14100         }
14101         for(var i = 0, len = values.length; i < len; i++){
14102             this.add(name, values[i]);
14103         }
14104         return this;
14105     },
14106
14107     /**
14108      * Resets the template for reuse
14109      * @return {MasterTemplate} this
14110      */
14111      reset : function(){
14112         var s = this.subs;
14113         for(var i = 0; i < this.subCount; i++){
14114             s[i].buffer = [];
14115         }
14116         return this;
14117     },
14118
14119     applyTemplate : function(values){
14120         var s = this.subs;
14121         var replaceIndex = -1;
14122         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
14123             return s[++replaceIndex].buffer.join("");
14124         });
14125         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
14126     },
14127
14128     apply : function(){
14129         return this.applyTemplate.apply(this, arguments);
14130     },
14131
14132     compile : function(){return this;}
14133 });
14134
14135 /**
14136  * Alias for fill().
14137  * @method
14138  */
14139 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
14140  /**
14141  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
14142  * var tpl = Roo.MasterTemplate.from('element-id');
14143  * @param {String/HTMLElement} el
14144  * @param {Object} config
14145  * @static
14146  */
14147 Roo.MasterTemplate.from = function(el, config){
14148     el = Roo.getDom(el);
14149     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
14150 };/*
14151  * Based on:
14152  * Ext JS Library 1.1.1
14153  * Copyright(c) 2006-2007, Ext JS, LLC.
14154  *
14155  * Originally Released Under LGPL - original licence link has changed is not relivant.
14156  *
14157  * Fork - LGPL
14158  * <script type="text/javascript">
14159  */
14160
14161  
14162 /**
14163  * @class Roo.util.CSS
14164  * Utility class for manipulating CSS rules
14165  * @singleton
14166  */
14167 Roo.util.CSS = function(){
14168         var rules = null;
14169         var doc = document;
14170
14171     var camelRe = /(-[a-z])/gi;
14172     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
14173
14174    return {
14175    /**
14176     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
14177     * tag and appended to the HEAD of the document.
14178     * @param {String|Object} cssText The text containing the css rules
14179     * @param {String} id An id to add to the stylesheet for later removal
14180     * @return {StyleSheet}
14181     */
14182     createStyleSheet : function(cssText, id){
14183         var ss;
14184         var head = doc.getElementsByTagName("head")[0];
14185         var nrules = doc.createElement("style");
14186         nrules.setAttribute("type", "text/css");
14187         if(id){
14188             nrules.setAttribute("id", id);
14189         }
14190         if (typeof(cssText) != 'string') {
14191             // support object maps..
14192             // not sure if this a good idea.. 
14193             // perhaps it should be merged with the general css handling
14194             // and handle js style props.
14195             var cssTextNew = [];
14196             for(var n in cssText) {
14197                 var citems = [];
14198                 for(var k in cssText[n]) {
14199                     citems.push( k + ' : ' +cssText[n][k] + ';' );
14200                 }
14201                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
14202                 
14203             }
14204             cssText = cssTextNew.join("\n");
14205             
14206         }
14207        
14208        
14209        if(Roo.isIE){
14210            head.appendChild(nrules);
14211            ss = nrules.styleSheet;
14212            ss.cssText = cssText;
14213        }else{
14214            try{
14215                 nrules.appendChild(doc.createTextNode(cssText));
14216            }catch(e){
14217                nrules.cssText = cssText; 
14218            }
14219            head.appendChild(nrules);
14220            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
14221        }
14222        this.cacheStyleSheet(ss);
14223        return ss;
14224    },
14225
14226    /**
14227     * Removes a style or link tag by id
14228     * @param {String} id The id of the tag
14229     */
14230    removeStyleSheet : function(id){
14231        var existing = doc.getElementById(id);
14232        if(existing){
14233            existing.parentNode.removeChild(existing);
14234        }
14235    },
14236
14237    /**
14238     * Dynamically swaps an existing stylesheet reference for a new one
14239     * @param {String} id The id of an existing link tag to remove
14240     * @param {String} url The href of the new stylesheet to include
14241     */
14242    swapStyleSheet : function(id, url){
14243        this.removeStyleSheet(id);
14244        var ss = doc.createElement("link");
14245        ss.setAttribute("rel", "stylesheet");
14246        ss.setAttribute("type", "text/css");
14247        ss.setAttribute("id", id);
14248        ss.setAttribute("href", url);
14249        doc.getElementsByTagName("head")[0].appendChild(ss);
14250    },
14251    
14252    /**
14253     * Refresh the rule cache if you have dynamically added stylesheets
14254     * @return {Object} An object (hash) of rules indexed by selector
14255     */
14256    refreshCache : function(){
14257        return this.getRules(true);
14258    },
14259
14260    // private
14261    cacheStyleSheet : function(stylesheet){
14262        if(!rules){
14263            rules = {};
14264        }
14265        try{// try catch for cross domain access issue
14266            var ssRules = stylesheet.cssRules || stylesheet.rules;
14267            for(var j = ssRules.length-1; j >= 0; --j){
14268                rules[ssRules[j].selectorText] = ssRules[j];
14269            }
14270        }catch(e){}
14271    },
14272    
14273    /**
14274     * Gets all css rules for the document
14275     * @param {Boolean} refreshCache true to refresh the internal cache
14276     * @return {Object} An object (hash) of rules indexed by selector
14277     */
14278    getRules : function(refreshCache){
14279                 if(rules == null || refreshCache){
14280                         rules = {};
14281                         var ds = doc.styleSheets;
14282                         for(var i =0, len = ds.length; i < len; i++){
14283                             try{
14284                         this.cacheStyleSheet(ds[i]);
14285                     }catch(e){} 
14286                 }
14287                 }
14288                 return rules;
14289         },
14290         
14291         /**
14292     * Gets an an individual CSS rule by selector(s)
14293     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
14294     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
14295     * @return {CSSRule} The CSS rule or null if one is not found
14296     */
14297    getRule : function(selector, refreshCache){
14298                 var rs = this.getRules(refreshCache);
14299                 if(!(selector instanceof Array)){
14300                     return rs[selector];
14301                 }
14302                 for(var i = 0; i < selector.length; i++){
14303                         if(rs[selector[i]]){
14304                                 return rs[selector[i]];
14305                         }
14306                 }
14307                 return null;
14308         },
14309         
14310         
14311         /**
14312     * Updates a rule property
14313     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
14314     * @param {String} property The css property
14315     * @param {String} value The new value for the property
14316     * @return {Boolean} true If a rule was found and updated
14317     */
14318    updateRule : function(selector, property, value){
14319                 if(!(selector instanceof Array)){
14320                         var rule = this.getRule(selector);
14321                         if(rule){
14322                                 rule.style[property.replace(camelRe, camelFn)] = value;
14323                                 return true;
14324                         }
14325                 }else{
14326                         for(var i = 0; i < selector.length; i++){
14327                                 if(this.updateRule(selector[i], property, value)){
14328                                         return true;
14329                                 }
14330                         }
14331                 }
14332                 return false;
14333         }
14334    };   
14335 }();/*
14336  * Based on:
14337  * Ext JS Library 1.1.1
14338  * Copyright(c) 2006-2007, Ext JS, LLC.
14339  *
14340  * Originally Released Under LGPL - original licence link has changed is not relivant.
14341  *
14342  * Fork - LGPL
14343  * <script type="text/javascript">
14344  */
14345
14346  
14347
14348 /**
14349  * @class Roo.util.ClickRepeater
14350  * @extends Roo.util.Observable
14351  * 
14352  * A wrapper class which can be applied to any element. Fires a "click" event while the
14353  * mouse is pressed. The interval between firings may be specified in the config but
14354  * defaults to 10 milliseconds.
14355  * 
14356  * Optionally, a CSS class may be applied to the element during the time it is pressed.
14357  * 
14358  * @cfg {String/HTMLElement/Element} el The element to act as a button.
14359  * @cfg {Number} delay The initial delay before the repeating event begins firing.
14360  * Similar to an autorepeat key delay.
14361  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
14362  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
14363  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
14364  *           "interval" and "delay" are ignored. "immediate" is honored.
14365  * @cfg {Boolean} preventDefault True to prevent the default click event
14366  * @cfg {Boolean} stopDefault True to stop the default click event
14367  * 
14368  * @history
14369  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
14370  *     2007-02-02 jvs Renamed to ClickRepeater
14371  *   2007-02-03 jvs Modifications for FF Mac and Safari 
14372  *
14373  *  @constructor
14374  * @param {String/HTMLElement/Element} el The element to listen on
14375  * @param {Object} config
14376  **/
14377 Roo.util.ClickRepeater = function(el, config)
14378 {
14379     this.el = Roo.get(el);
14380     this.el.unselectable();
14381
14382     Roo.apply(this, config);
14383
14384     this.addEvents({
14385     /**
14386      * @event mousedown
14387      * Fires when the mouse button is depressed.
14388      * @param {Roo.util.ClickRepeater} this
14389      */
14390         "mousedown" : true,
14391     /**
14392      * @event click
14393      * Fires on a specified interval during the time the element is pressed.
14394      * @param {Roo.util.ClickRepeater} this
14395      */
14396         "click" : true,
14397     /**
14398      * @event mouseup
14399      * Fires when the mouse key is released.
14400      * @param {Roo.util.ClickRepeater} this
14401      */
14402         "mouseup" : true
14403     });
14404
14405     this.el.on("mousedown", this.handleMouseDown, this);
14406     if(this.preventDefault || this.stopDefault){
14407         this.el.on("click", function(e){
14408             if(this.preventDefault){
14409                 e.preventDefault();
14410             }
14411             if(this.stopDefault){
14412                 e.stopEvent();
14413             }
14414         }, this);
14415     }
14416
14417     // allow inline handler
14418     if(this.handler){
14419         this.on("click", this.handler,  this.scope || this);
14420     }
14421
14422     Roo.util.ClickRepeater.superclass.constructor.call(this);
14423 };
14424
14425 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
14426     interval : 20,
14427     delay: 250,
14428     preventDefault : true,
14429     stopDefault : false,
14430     timer : 0,
14431
14432     // private
14433     handleMouseDown : function(){
14434         clearTimeout(this.timer);
14435         this.el.blur();
14436         if(this.pressClass){
14437             this.el.addClass(this.pressClass);
14438         }
14439         this.mousedownTime = new Date();
14440
14441         Roo.get(document).on("mouseup", this.handleMouseUp, this);
14442         this.el.on("mouseout", this.handleMouseOut, this);
14443
14444         this.fireEvent("mousedown", this);
14445         this.fireEvent("click", this);
14446         
14447         this.timer = this.click.defer(this.delay || this.interval, this);
14448     },
14449
14450     // private
14451     click : function(){
14452         this.fireEvent("click", this);
14453         this.timer = this.click.defer(this.getInterval(), this);
14454     },
14455
14456     // private
14457     getInterval: function(){
14458         if(!this.accelerate){
14459             return this.interval;
14460         }
14461         var pressTime = this.mousedownTime.getElapsed();
14462         if(pressTime < 500){
14463             return 400;
14464         }else if(pressTime < 1700){
14465             return 320;
14466         }else if(pressTime < 2600){
14467             return 250;
14468         }else if(pressTime < 3500){
14469             return 180;
14470         }else if(pressTime < 4400){
14471             return 140;
14472         }else if(pressTime < 5300){
14473             return 80;
14474         }else if(pressTime < 6200){
14475             return 50;
14476         }else{
14477             return 10;
14478         }
14479     },
14480
14481     // private
14482     handleMouseOut : function(){
14483         clearTimeout(this.timer);
14484         if(this.pressClass){
14485             this.el.removeClass(this.pressClass);
14486         }
14487         this.el.on("mouseover", this.handleMouseReturn, this);
14488     },
14489
14490     // private
14491     handleMouseReturn : function(){
14492         this.el.un("mouseover", this.handleMouseReturn);
14493         if(this.pressClass){
14494             this.el.addClass(this.pressClass);
14495         }
14496         this.click();
14497     },
14498
14499     // private
14500     handleMouseUp : function(){
14501         clearTimeout(this.timer);
14502         this.el.un("mouseover", this.handleMouseReturn);
14503         this.el.un("mouseout", this.handleMouseOut);
14504         Roo.get(document).un("mouseup", this.handleMouseUp);
14505         this.el.removeClass(this.pressClass);
14506         this.fireEvent("mouseup", this);
14507     }
14508 });/*
14509  * Based on:
14510  * Ext JS Library 1.1.1
14511  * Copyright(c) 2006-2007, Ext JS, LLC.
14512  *
14513  * Originally Released Under LGPL - original licence link has changed is not relivant.
14514  *
14515  * Fork - LGPL
14516  * <script type="text/javascript">
14517  */
14518
14519  
14520 /**
14521  * @class Roo.KeyNav
14522  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
14523  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
14524  * way to implement custom navigation schemes for any UI component.</p>
14525  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
14526  * pageUp, pageDown, del, home, end.  Usage:</p>
14527  <pre><code>
14528 var nav = new Roo.KeyNav("my-element", {
14529     "left" : function(e){
14530         this.moveLeft(e.ctrlKey);
14531     },
14532     "right" : function(e){
14533         this.moveRight(e.ctrlKey);
14534     },
14535     "enter" : function(e){
14536         this.save();
14537     },
14538     scope : this
14539 });
14540 </code></pre>
14541  * @constructor
14542  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14543  * @param {Object} config The config
14544  */
14545 Roo.KeyNav = function(el, config){
14546     this.el = Roo.get(el);
14547     Roo.apply(this, config);
14548     if(!this.disabled){
14549         this.disabled = true;
14550         this.enable();
14551     }
14552 };
14553
14554 Roo.KeyNav.prototype = {
14555     /**
14556      * @cfg {Boolean} disabled
14557      * True to disable this KeyNav instance (defaults to false)
14558      */
14559     disabled : false,
14560     /**
14561      * @cfg {String} defaultEventAction
14562      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
14563      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
14564      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
14565      */
14566     defaultEventAction: "stopEvent",
14567     /**
14568      * @cfg {Boolean} forceKeyDown
14569      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
14570      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
14571      * handle keydown instead of keypress.
14572      */
14573     forceKeyDown : false,
14574
14575     // private
14576     prepareEvent : function(e){
14577         var k = e.getKey();
14578         var h = this.keyToHandler[k];
14579         //if(h && this[h]){
14580         //    e.stopPropagation();
14581         //}
14582         if(Roo.isSafari && h && k >= 37 && k <= 40){
14583             e.stopEvent();
14584         }
14585     },
14586
14587     // private
14588     relay : function(e){
14589         var k = e.getKey();
14590         var h = this.keyToHandler[k];
14591         if(h && this[h]){
14592             if(this.doRelay(e, this[h], h) !== true){
14593                 e[this.defaultEventAction]();
14594             }
14595         }
14596     },
14597
14598     // private
14599     doRelay : function(e, h, hname){
14600         return h.call(this.scope || this, e);
14601     },
14602
14603     // possible handlers
14604     enter : false,
14605     left : false,
14606     right : false,
14607     up : false,
14608     down : false,
14609     tab : false,
14610     esc : false,
14611     pageUp : false,
14612     pageDown : false,
14613     del : false,
14614     home : false,
14615     end : false,
14616
14617     // quick lookup hash
14618     keyToHandler : {
14619         37 : "left",
14620         39 : "right",
14621         38 : "up",
14622         40 : "down",
14623         33 : "pageUp",
14624         34 : "pageDown",
14625         46 : "del",
14626         36 : "home",
14627         35 : "end",
14628         13 : "enter",
14629         27 : "esc",
14630         9  : "tab"
14631     },
14632
14633         /**
14634          * Enable this KeyNav
14635          */
14636         enable: function(){
14637                 if(this.disabled){
14638             // ie won't do special keys on keypress, no one else will repeat keys with keydown
14639             // the EventObject will normalize Safari automatically
14640             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14641                 this.el.on("keydown", this.relay,  this);
14642             }else{
14643                 this.el.on("keydown", this.prepareEvent,  this);
14644                 this.el.on("keypress", this.relay,  this);
14645             }
14646                     this.disabled = false;
14647                 }
14648         },
14649
14650         /**
14651          * Disable this KeyNav
14652          */
14653         disable: function(){
14654                 if(!this.disabled){
14655                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14656                 this.el.un("keydown", this.relay);
14657             }else{
14658                 this.el.un("keydown", this.prepareEvent);
14659                 this.el.un("keypress", this.relay);
14660             }
14661                     this.disabled = true;
14662                 }
14663         }
14664 };/*
14665  * Based on:
14666  * Ext JS Library 1.1.1
14667  * Copyright(c) 2006-2007, Ext JS, LLC.
14668  *
14669  * Originally Released Under LGPL - original licence link has changed is not relivant.
14670  *
14671  * Fork - LGPL
14672  * <script type="text/javascript">
14673  */
14674
14675  
14676 /**
14677  * @class Roo.KeyMap
14678  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
14679  * The constructor accepts the same config object as defined by {@link #addBinding}.
14680  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
14681  * combination it will call the function with this signature (if the match is a multi-key
14682  * combination the callback will still be called only once): (String key, Roo.EventObject e)
14683  * A KeyMap can also handle a string representation of keys.<br />
14684  * Usage:
14685  <pre><code>
14686 // map one key by key code
14687 var map = new Roo.KeyMap("my-element", {
14688     key: 13, // or Roo.EventObject.ENTER
14689     fn: myHandler,
14690     scope: myObject
14691 });
14692
14693 // map multiple keys to one action by string
14694 var map = new Roo.KeyMap("my-element", {
14695     key: "a\r\n\t",
14696     fn: myHandler,
14697     scope: myObject
14698 });
14699
14700 // map multiple keys to multiple actions by strings and array of codes
14701 var map = new Roo.KeyMap("my-element", [
14702     {
14703         key: [10,13],
14704         fn: function(){ alert("Return was pressed"); }
14705     }, {
14706         key: "abc",
14707         fn: function(){ alert('a, b or c was pressed'); }
14708     }, {
14709         key: "\t",
14710         ctrl:true,
14711         shift:true,
14712         fn: function(){ alert('Control + shift + tab was pressed.'); }
14713     }
14714 ]);
14715 </code></pre>
14716  * <b>Note: A KeyMap starts enabled</b>
14717  * @constructor
14718  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14719  * @param {Object} config The config (see {@link #addBinding})
14720  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
14721  */
14722 Roo.KeyMap = function(el, config, eventName){
14723     this.el  = Roo.get(el);
14724     this.eventName = eventName || "keydown";
14725     this.bindings = [];
14726     if(config){
14727         this.addBinding(config);
14728     }
14729     this.enable();
14730 };
14731
14732 Roo.KeyMap.prototype = {
14733     /**
14734      * True to stop the event from bubbling and prevent the default browser action if the
14735      * key was handled by the KeyMap (defaults to false)
14736      * @type Boolean
14737      */
14738     stopEvent : false,
14739
14740     /**
14741      * Add a new binding to this KeyMap. The following config object properties are supported:
14742      * <pre>
14743 Property    Type             Description
14744 ----------  ---------------  ----------------------------------------------------------------------
14745 key         String/Array     A single keycode or an array of keycodes to handle
14746 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
14747 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
14748 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
14749 fn          Function         The function to call when KeyMap finds the expected key combination
14750 scope       Object           The scope of the callback function
14751 </pre>
14752      *
14753      * Usage:
14754      * <pre><code>
14755 // Create a KeyMap
14756 var map = new Roo.KeyMap(document, {
14757     key: Roo.EventObject.ENTER,
14758     fn: handleKey,
14759     scope: this
14760 });
14761
14762 //Add a new binding to the existing KeyMap later
14763 map.addBinding({
14764     key: 'abc',
14765     shift: true,
14766     fn: handleKey,
14767     scope: this
14768 });
14769 </code></pre>
14770      * @param {Object/Array} config A single KeyMap config or an array of configs
14771      */
14772         addBinding : function(config){
14773         if(config instanceof Array){
14774             for(var i = 0, len = config.length; i < len; i++){
14775                 this.addBinding(config[i]);
14776             }
14777             return;
14778         }
14779         var keyCode = config.key,
14780             shift = config.shift, 
14781             ctrl = config.ctrl, 
14782             alt = config.alt,
14783             fn = config.fn,
14784             scope = config.scope;
14785         if(typeof keyCode == "string"){
14786             var ks = [];
14787             var keyString = keyCode.toUpperCase();
14788             for(var j = 0, len = keyString.length; j < len; j++){
14789                 ks.push(keyString.charCodeAt(j));
14790             }
14791             keyCode = ks;
14792         }
14793         var keyArray = keyCode instanceof Array;
14794         var handler = function(e){
14795             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14796                 var k = e.getKey();
14797                 if(keyArray){
14798                     for(var i = 0, len = keyCode.length; i < len; i++){
14799                         if(keyCode[i] == k){
14800                           if(this.stopEvent){
14801                               e.stopEvent();
14802                           }
14803                           fn.call(scope || window, k, e);
14804                           return;
14805                         }
14806                     }
14807                 }else{
14808                     if(k == keyCode){
14809                         if(this.stopEvent){
14810                            e.stopEvent();
14811                         }
14812                         fn.call(scope || window, k, e);
14813                     }
14814                 }
14815             }
14816         };
14817         this.bindings.push(handler);  
14818         },
14819
14820     /**
14821      * Shorthand for adding a single key listener
14822      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
14823      * following options:
14824      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14825      * @param {Function} fn The function to call
14826      * @param {Object} scope (optional) The scope of the function
14827      */
14828     on : function(key, fn, scope){
14829         var keyCode, shift, ctrl, alt;
14830         if(typeof key == "object" && !(key instanceof Array)){
14831             keyCode = key.key;
14832             shift = key.shift;
14833             ctrl = key.ctrl;
14834             alt = key.alt;
14835         }else{
14836             keyCode = key;
14837         }
14838         this.addBinding({
14839             key: keyCode,
14840             shift: shift,
14841             ctrl: ctrl,
14842             alt: alt,
14843             fn: fn,
14844             scope: scope
14845         })
14846     },
14847
14848     // private
14849     handleKeyDown : function(e){
14850             if(this.enabled){ //just in case
14851             var b = this.bindings;
14852             for(var i = 0, len = b.length; i < len; i++){
14853                 b[i].call(this, e);
14854             }
14855             }
14856         },
14857         
14858         /**
14859          * Returns true if this KeyMap is enabled
14860          * @return {Boolean} 
14861          */
14862         isEnabled : function(){
14863             return this.enabled;  
14864         },
14865         
14866         /**
14867          * Enables this KeyMap
14868          */
14869         enable: function(){
14870                 if(!this.enabled){
14871                     this.el.on(this.eventName, this.handleKeyDown, this);
14872                     this.enabled = true;
14873                 }
14874         },
14875
14876         /**
14877          * Disable this KeyMap
14878          */
14879         disable: function(){
14880                 if(this.enabled){
14881                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
14882                     this.enabled = false;
14883                 }
14884         }
14885 };/*
14886  * Based on:
14887  * Ext JS Library 1.1.1
14888  * Copyright(c) 2006-2007, Ext JS, LLC.
14889  *
14890  * Originally Released Under LGPL - original licence link has changed is not relivant.
14891  *
14892  * Fork - LGPL
14893  * <script type="text/javascript">
14894  */
14895
14896  
14897 /**
14898  * @class Roo.util.TextMetrics
14899  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
14900  * wide, in pixels, a given block of text will be.
14901  * @singleton
14902  */
14903 Roo.util.TextMetrics = function(){
14904     var shared;
14905     return {
14906         /**
14907          * Measures the size of the specified text
14908          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
14909          * that can affect the size of the rendered text
14910          * @param {String} text The text to measure
14911          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14912          * in order to accurately measure the text height
14913          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14914          */
14915         measure : function(el, text, fixedWidth){
14916             if(!shared){
14917                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
14918             }
14919             shared.bind(el);
14920             shared.setFixedWidth(fixedWidth || 'auto');
14921             return shared.getSize(text);
14922         },
14923
14924         /**
14925          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
14926          * the overhead of multiple calls to initialize the style properties on each measurement.
14927          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
14928          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14929          * in order to accurately measure the text height
14930          * @return {Roo.util.TextMetrics.Instance} instance The new instance
14931          */
14932         createInstance : function(el, fixedWidth){
14933             return Roo.util.TextMetrics.Instance(el, fixedWidth);
14934         }
14935     };
14936 }();
14937
14938  
14939
14940 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
14941     var ml = new Roo.Element(document.createElement('div'));
14942     document.body.appendChild(ml.dom);
14943     ml.position('absolute');
14944     ml.setLeftTop(-1000, -1000);
14945     ml.hide();
14946
14947     if(fixedWidth){
14948         ml.setWidth(fixedWidth);
14949     }
14950      
14951     var instance = {
14952         /**
14953          * Returns the size of the specified text based on the internal element's style and width properties
14954          * @memberOf Roo.util.TextMetrics.Instance#
14955          * @param {String} text The text to measure
14956          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14957          */
14958         getSize : function(text){
14959             ml.update(text);
14960             var s = ml.getSize();
14961             ml.update('');
14962             return s;
14963         },
14964
14965         /**
14966          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
14967          * that can affect the size of the rendered text
14968          * @memberOf Roo.util.TextMetrics.Instance#
14969          * @param {String/HTMLElement} el The element, dom node or id
14970          */
14971         bind : function(el){
14972             ml.setStyle(
14973                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
14974             );
14975         },
14976
14977         /**
14978          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
14979          * to set a fixed width in order to accurately measure the text height.
14980          * @memberOf Roo.util.TextMetrics.Instance#
14981          * @param {Number} width The width to set on the element
14982          */
14983         setFixedWidth : function(width){
14984             ml.setWidth(width);
14985         },
14986
14987         /**
14988          * Returns the measured width of the specified text
14989          * @memberOf Roo.util.TextMetrics.Instance#
14990          * @param {String} text The text to measure
14991          * @return {Number} width The width in pixels
14992          */
14993         getWidth : function(text){
14994             ml.dom.style.width = 'auto';
14995             return this.getSize(text).width;
14996         },
14997
14998         /**
14999          * Returns the measured height of the specified text.  For multiline text, be sure to call
15000          * {@link #setFixedWidth} if necessary.
15001          * @memberOf Roo.util.TextMetrics.Instance#
15002          * @param {String} text The text to measure
15003          * @return {Number} height The height in pixels
15004          */
15005         getHeight : function(text){
15006             return this.getSize(text).height;
15007         }
15008     };
15009
15010     instance.bind(bindTo);
15011
15012     return instance;
15013 };
15014
15015 // backwards compat
15016 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
15017  * Based on:
15018  * Ext JS Library 1.1.1
15019  * Copyright(c) 2006-2007, Ext JS, LLC.
15020  *
15021  * Originally Released Under LGPL - original licence link has changed is not relivant.
15022  *
15023  * Fork - LGPL
15024  * <script type="text/javascript">
15025  */
15026
15027 /**
15028  * @class Roo.state.Provider
15029  * Abstract base class for state provider implementations. This class provides methods
15030  * for encoding and decoding <b>typed</b> variables including dates and defines the 
15031  * Provider interface.
15032  */
15033 Roo.state.Provider = function(){
15034     /**
15035      * @event statechange
15036      * Fires when a state change occurs.
15037      * @param {Provider} this This state provider
15038      * @param {String} key The state key which was changed
15039      * @param {String} value The encoded value for the state
15040      */
15041     this.addEvents({
15042         "statechange": true
15043     });
15044     this.state = {};
15045     Roo.state.Provider.superclass.constructor.call(this);
15046 };
15047 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
15048     /**
15049      * Returns the current value for a key
15050      * @param {String} name The key name
15051      * @param {Mixed} defaultValue A default value to return if the key's value is not found
15052      * @return {Mixed} The state data
15053      */
15054     get : function(name, defaultValue){
15055         return typeof this.state[name] == "undefined" ?
15056             defaultValue : this.state[name];
15057     },
15058     
15059     /**
15060      * Clears a value from the state
15061      * @param {String} name The key name
15062      */
15063     clear : function(name){
15064         delete this.state[name];
15065         this.fireEvent("statechange", this, name, null);
15066     },
15067     
15068     /**
15069      * Sets the value for a key
15070      * @param {String} name The key name
15071      * @param {Mixed} value The value to set
15072      */
15073     set : function(name, value){
15074         this.state[name] = value;
15075         this.fireEvent("statechange", this, name, value);
15076     },
15077     
15078     /**
15079      * Decodes a string previously encoded with {@link #encodeValue}.
15080      * @param {String} value The value to decode
15081      * @return {Mixed} The decoded value
15082      */
15083     decodeValue : function(cookie){
15084         var re = /^(a|n|d|b|s|o)\:(.*)$/;
15085         var matches = re.exec(unescape(cookie));
15086         if(!matches || !matches[1]) {
15087             return; // non state cookie
15088         }
15089         var type = matches[1];
15090         var v = matches[2];
15091         switch(type){
15092             case "n":
15093                 return parseFloat(v);
15094             case "d":
15095                 return new Date(Date.parse(v));
15096             case "b":
15097                 return (v == "1");
15098             case "a":
15099                 var all = [];
15100                 var values = v.split("^");
15101                 for(var i = 0, len = values.length; i < len; i++){
15102                     all.push(this.decodeValue(values[i]));
15103                 }
15104                 return all;
15105            case "o":
15106                 var all = {};
15107                 var values = v.split("^");
15108                 for(var i = 0, len = values.length; i < len; i++){
15109                     var kv = values[i].split("=");
15110                     all[kv[0]] = this.decodeValue(kv[1]);
15111                 }
15112                 return all;
15113            default:
15114                 return v;
15115         }
15116     },
15117     
15118     /**
15119      * Encodes a value including type information.  Decode with {@link #decodeValue}.
15120      * @param {Mixed} value The value to encode
15121      * @return {String} The encoded value
15122      */
15123     encodeValue : function(v){
15124         var enc;
15125         if(typeof v == "number"){
15126             enc = "n:" + v;
15127         }else if(typeof v == "boolean"){
15128             enc = "b:" + (v ? "1" : "0");
15129         }else if(v instanceof Date){
15130             enc = "d:" + v.toGMTString();
15131         }else if(v instanceof Array){
15132             var flat = "";
15133             for(var i = 0, len = v.length; i < len; i++){
15134                 flat += this.encodeValue(v[i]);
15135                 if(i != len-1) {
15136                     flat += "^";
15137                 }
15138             }
15139             enc = "a:" + flat;
15140         }else if(typeof v == "object"){
15141             var flat = "";
15142             for(var key in v){
15143                 if(typeof v[key] != "function"){
15144                     flat += key + "=" + this.encodeValue(v[key]) + "^";
15145                 }
15146             }
15147             enc = "o:" + flat.substring(0, flat.length-1);
15148         }else{
15149             enc = "s:" + v;
15150         }
15151         return escape(enc);        
15152     }
15153 });
15154
15155 /*
15156  * Based on:
15157  * Ext JS Library 1.1.1
15158  * Copyright(c) 2006-2007, Ext JS, LLC.
15159  *
15160  * Originally Released Under LGPL - original licence link has changed is not relivant.
15161  *
15162  * Fork - LGPL
15163  * <script type="text/javascript">
15164  */
15165 /**
15166  * @class Roo.state.Manager
15167  * This is the global state manager. By default all components that are "state aware" check this class
15168  * for state information if you don't pass them a custom state provider. In order for this class
15169  * to be useful, it must be initialized with a provider when your application initializes.
15170  <pre><code>
15171 // in your initialization function
15172 init : function(){
15173    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
15174    ...
15175    // supposed you have a {@link Roo.BorderLayout}
15176    var layout = new Roo.BorderLayout(...);
15177    layout.restoreState();
15178    // or a {Roo.BasicDialog}
15179    var dialog = new Roo.BasicDialog(...);
15180    dialog.restoreState();
15181  </code></pre>
15182  * @singleton
15183  */
15184 Roo.state.Manager = function(){
15185     var provider = new Roo.state.Provider();
15186     
15187     return {
15188         /**
15189          * Configures the default state provider for your application
15190          * @param {Provider} stateProvider The state provider to set
15191          */
15192         setProvider : function(stateProvider){
15193             provider = stateProvider;
15194         },
15195         
15196         /**
15197          * Returns the current value for a key
15198          * @param {String} name The key name
15199          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
15200          * @return {Mixed} The state data
15201          */
15202         get : function(key, defaultValue){
15203             return provider.get(key, defaultValue);
15204         },
15205         
15206         /**
15207          * Sets the value for a key
15208          * @param {String} name The key name
15209          * @param {Mixed} value The state data
15210          */
15211          set : function(key, value){
15212             provider.set(key, value);
15213         },
15214         
15215         /**
15216          * Clears a value from the state
15217          * @param {String} name The key name
15218          */
15219         clear : function(key){
15220             provider.clear(key);
15221         },
15222         
15223         /**
15224          * Gets the currently configured state provider
15225          * @return {Provider} The state provider
15226          */
15227         getProvider : function(){
15228             return provider;
15229         }
15230     };
15231 }();
15232 /*
15233  * Based on:
15234  * Ext JS Library 1.1.1
15235  * Copyright(c) 2006-2007, Ext JS, LLC.
15236  *
15237  * Originally Released Under LGPL - original licence link has changed is not relivant.
15238  *
15239  * Fork - LGPL
15240  * <script type="text/javascript">
15241  */
15242 /**
15243  * @class Roo.state.CookieProvider
15244  * @extends Roo.state.Provider
15245  * The default Provider implementation which saves state via cookies.
15246  * <br />Usage:
15247  <pre><code>
15248    var cp = new Roo.state.CookieProvider({
15249        path: "/cgi-bin/",
15250        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
15251        domain: "roojs.com"
15252    })
15253    Roo.state.Manager.setProvider(cp);
15254  </code></pre>
15255  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
15256  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
15257  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
15258  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
15259  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
15260  * domain the page is running on including the 'www' like 'www.roojs.com')
15261  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
15262  * @constructor
15263  * Create a new CookieProvider
15264  * @param {Object} config The configuration object
15265  */
15266 Roo.state.CookieProvider = function(config){
15267     Roo.state.CookieProvider.superclass.constructor.call(this);
15268     this.path = "/";
15269     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
15270     this.domain = null;
15271     this.secure = false;
15272     Roo.apply(this, config);
15273     this.state = this.readCookies();
15274 };
15275
15276 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
15277     // private
15278     set : function(name, value){
15279         if(typeof value == "undefined" || value === null){
15280             this.clear(name);
15281             return;
15282         }
15283         this.setCookie(name, value);
15284         Roo.state.CookieProvider.superclass.set.call(this, name, value);
15285     },
15286
15287     // private
15288     clear : function(name){
15289         this.clearCookie(name);
15290         Roo.state.CookieProvider.superclass.clear.call(this, name);
15291     },
15292
15293     // private
15294     readCookies : function(){
15295         var cookies = {};
15296         var c = document.cookie + ";";
15297         var re = /\s?(.*?)=(.*?);/g;
15298         var matches;
15299         while((matches = re.exec(c)) != null){
15300             var name = matches[1];
15301             var value = matches[2];
15302             if(name && name.substring(0,3) == "ys-"){
15303                 cookies[name.substr(3)] = this.decodeValue(value);
15304             }
15305         }
15306         return cookies;
15307     },
15308
15309     // private
15310     setCookie : function(name, value){
15311         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
15312            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
15313            ((this.path == null) ? "" : ("; path=" + this.path)) +
15314            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
15315            ((this.secure == true) ? "; secure" : "");
15316     },
15317
15318     // private
15319     clearCookie : function(name){
15320         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
15321            ((this.path == null) ? "" : ("; path=" + this.path)) +
15322            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
15323            ((this.secure == true) ? "; secure" : "");
15324     }
15325 });/*
15326  * Based on:
15327  * Ext JS Library 1.1.1
15328  * Copyright(c) 2006-2007, Ext JS, LLC.
15329  *
15330  * Originally Released Under LGPL - original licence link has changed is not relivant.
15331  *
15332  * Fork - LGPL
15333  * <script type="text/javascript">
15334  */
15335  
15336
15337 /**
15338  * @class Roo.ComponentMgr
15339  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
15340  * @singleton
15341  */
15342 Roo.ComponentMgr = function(){
15343     var all = new Roo.util.MixedCollection();
15344
15345     return {
15346         /**
15347          * Registers a component.
15348          * @param {Roo.Component} c The component
15349          */
15350         register : function(c){
15351             all.add(c);
15352         },
15353
15354         /**
15355          * Unregisters a component.
15356          * @param {Roo.Component} c The component
15357          */
15358         unregister : function(c){
15359             all.remove(c);
15360         },
15361
15362         /**
15363          * Returns a component by id
15364          * @param {String} id The component id
15365          */
15366         get : function(id){
15367             return all.get(id);
15368         },
15369
15370         /**
15371          * Registers a function that will be called when a specified component is added to ComponentMgr
15372          * @param {String} id The component id
15373          * @param {Funtction} fn The callback function
15374          * @param {Object} scope The scope of the callback
15375          */
15376         onAvailable : function(id, fn, scope){
15377             all.on("add", function(index, o){
15378                 if(o.id == id){
15379                     fn.call(scope || o, o);
15380                     all.un("add", fn, scope);
15381                 }
15382             });
15383         }
15384     };
15385 }();/*
15386  * Based on:
15387  * Ext JS Library 1.1.1
15388  * Copyright(c) 2006-2007, Ext JS, LLC.
15389  *
15390  * Originally Released Under LGPL - original licence link has changed is not relivant.
15391  *
15392  * Fork - LGPL
15393  * <script type="text/javascript">
15394  */
15395  
15396 /**
15397  * @class Roo.Component
15398  * @extends Roo.util.Observable
15399  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
15400  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
15401  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
15402  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
15403  * All visual components (widgets) that require rendering into a layout should subclass Component.
15404  * @constructor
15405  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
15406  * 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
15407  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
15408  */
15409 Roo.Component = function(config){
15410     config = config || {};
15411     if(config.tagName || config.dom || typeof config == "string"){ // element object
15412         config = {el: config, id: config.id || config};
15413     }
15414     this.initialConfig = config;
15415
15416     Roo.apply(this, config);
15417     this.addEvents({
15418         /**
15419          * @event disable
15420          * Fires after the component is disabled.
15421              * @param {Roo.Component} this
15422              */
15423         disable : true,
15424         /**
15425          * @event enable
15426          * Fires after the component is enabled.
15427              * @param {Roo.Component} this
15428              */
15429         enable : true,
15430         /**
15431          * @event beforeshow
15432          * Fires before the component is shown.  Return false to stop the show.
15433              * @param {Roo.Component} this
15434              */
15435         beforeshow : true,
15436         /**
15437          * @event show
15438          * Fires after the component is shown.
15439              * @param {Roo.Component} this
15440              */
15441         show : true,
15442         /**
15443          * @event beforehide
15444          * Fires before the component is hidden. Return false to stop the hide.
15445              * @param {Roo.Component} this
15446              */
15447         beforehide : true,
15448         /**
15449          * @event hide
15450          * Fires after the component is hidden.
15451              * @param {Roo.Component} this
15452              */
15453         hide : true,
15454         /**
15455          * @event beforerender
15456          * Fires before the component is rendered. Return false to stop the render.
15457              * @param {Roo.Component} this
15458              */
15459         beforerender : true,
15460         /**
15461          * @event render
15462          * Fires after the component is rendered.
15463              * @param {Roo.Component} this
15464              */
15465         render : true,
15466         /**
15467          * @event beforedestroy
15468          * Fires before the component is destroyed. Return false to stop the destroy.
15469              * @param {Roo.Component} this
15470              */
15471         beforedestroy : true,
15472         /**
15473          * @event destroy
15474          * Fires after the component is destroyed.
15475              * @param {Roo.Component} this
15476              */
15477         destroy : true
15478     });
15479     if(!this.id){
15480         this.id = "roo-comp-" + (++Roo.Component.AUTO_ID);
15481     }
15482     Roo.ComponentMgr.register(this);
15483     Roo.Component.superclass.constructor.call(this);
15484     this.initComponent();
15485     if(this.renderTo){ // not supported by all components yet. use at your own risk!
15486         this.render(this.renderTo);
15487         delete this.renderTo;
15488     }
15489 };
15490
15491 /** @private */
15492 Roo.Component.AUTO_ID = 1000;
15493
15494 Roo.extend(Roo.Component, Roo.util.Observable, {
15495     /**
15496      * @scope Roo.Component.prototype
15497      * @type {Boolean}
15498      * true if this component is hidden. Read-only.
15499      */
15500     hidden : false,
15501     /**
15502      * @type {Boolean}
15503      * true if this component is disabled. Read-only.
15504      */
15505     disabled : false,
15506     /**
15507      * @type {Boolean}
15508      * true if this component has been rendered. Read-only.
15509      */
15510     rendered : false,
15511     
15512     /** @cfg {String} disableClass
15513      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
15514      */
15515     disabledClass : "x-item-disabled",
15516         /** @cfg {Boolean} allowDomMove
15517          * Whether the component can move the Dom node when rendering (defaults to true).
15518          */
15519     allowDomMove : true,
15520     /** @cfg {String} hideMode (display|visibility)
15521      * How this component should hidden. Supported values are
15522      * "visibility" (css visibility), "offsets" (negative offset position) and
15523      * "display" (css display) - defaults to "display".
15524      */
15525     hideMode: 'display',
15526
15527     /** @private */
15528     ctype : "Roo.Component",
15529
15530     /**
15531      * @cfg {String} actionMode 
15532      * which property holds the element that used for  hide() / show() / disable() / enable()
15533      * default is 'el' for forms you probably want to set this to fieldEl 
15534      */
15535     actionMode : "el",
15536
15537     /** @private */
15538     getActionEl : function(){
15539         return this[this.actionMode];
15540     },
15541
15542     initComponent : Roo.emptyFn,
15543     /**
15544      * If this is a lazy rendering component, render it to its container element.
15545      * @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.
15546      */
15547     render : function(container, position){
15548         
15549         if(this.rendered){
15550             return this;
15551         }
15552         
15553         if(this.fireEvent("beforerender", this) === false){
15554             return false;
15555         }
15556         
15557         if(!container && this.el){
15558             this.el = Roo.get(this.el);
15559             container = this.el.dom.parentNode;
15560             this.allowDomMove = false;
15561         }
15562         this.container = Roo.get(container);
15563         this.rendered = true;
15564         if(position !== undefined){
15565             if(typeof position == 'number'){
15566                 position = this.container.dom.childNodes[position];
15567             }else{
15568                 position = Roo.getDom(position);
15569             }
15570         }
15571         this.onRender(this.container, position || null);
15572         if(this.cls){
15573             this.el.addClass(this.cls);
15574             delete this.cls;
15575         }
15576         if(this.style){
15577             this.el.applyStyles(this.style);
15578             delete this.style;
15579         }
15580         this.fireEvent("render", this);
15581         this.afterRender(this.container);
15582         if(this.hidden){
15583             this.hide();
15584         }
15585         if(this.disabled){
15586             this.disable();
15587         }
15588
15589         return this;
15590         
15591     },
15592
15593     /** @private */
15594     // default function is not really useful
15595     onRender : function(ct, position){
15596         if(this.el){
15597             this.el = Roo.get(this.el);
15598             if(this.allowDomMove !== false){
15599                 ct.dom.insertBefore(this.el.dom, position);
15600             }
15601         }
15602     },
15603
15604     /** @private */
15605     getAutoCreate : function(){
15606         var cfg = typeof this.autoCreate == "object" ?
15607                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
15608         if(this.id && !cfg.id){
15609             cfg.id = this.id;
15610         }
15611         return cfg;
15612     },
15613
15614     /** @private */
15615     afterRender : Roo.emptyFn,
15616
15617     /**
15618      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
15619      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
15620      */
15621     destroy : function(){
15622         if(this.fireEvent("beforedestroy", this) !== false){
15623             this.purgeListeners();
15624             this.beforeDestroy();
15625             if(this.rendered){
15626                 this.el.removeAllListeners();
15627                 this.el.remove();
15628                 if(this.actionMode == "container"){
15629                     this.container.remove();
15630                 }
15631             }
15632             this.onDestroy();
15633             Roo.ComponentMgr.unregister(this);
15634             this.fireEvent("destroy", this);
15635         }
15636     },
15637
15638         /** @private */
15639     beforeDestroy : function(){
15640
15641     },
15642
15643         /** @private */
15644         onDestroy : function(){
15645
15646     },
15647
15648     /**
15649      * Returns the underlying {@link Roo.Element}.
15650      * @return {Roo.Element} The element
15651      */
15652     getEl : function(){
15653         return this.el;
15654     },
15655
15656     /**
15657      * Returns the id of this component.
15658      * @return {String}
15659      */
15660     getId : function(){
15661         return this.id;
15662     },
15663
15664     /**
15665      * Try to focus this component.
15666      * @param {Boolean} selectText True to also select the text in this component (if applicable)
15667      * @return {Roo.Component} this
15668      */
15669     focus : function(selectText){
15670         if(this.rendered){
15671             this.el.focus();
15672             if(selectText === true){
15673                 this.el.dom.select();
15674             }
15675         }
15676         return this;
15677     },
15678
15679     /** @private */
15680     blur : function(){
15681         if(this.rendered){
15682             this.el.blur();
15683         }
15684         return this;
15685     },
15686
15687     /**
15688      * Disable this component.
15689      * @return {Roo.Component} this
15690      */
15691     disable : function(){
15692         if(this.rendered){
15693             this.onDisable();
15694         }
15695         this.disabled = true;
15696         this.fireEvent("disable", this);
15697         return this;
15698     },
15699
15700         // private
15701     onDisable : function(){
15702         this.getActionEl().addClass(this.disabledClass);
15703         this.el.dom.disabled = true;
15704     },
15705
15706     /**
15707      * Enable this component.
15708      * @return {Roo.Component} this
15709      */
15710     enable : function(){
15711         if(this.rendered){
15712             this.onEnable();
15713         }
15714         this.disabled = false;
15715         this.fireEvent("enable", this);
15716         return this;
15717     },
15718
15719         // private
15720     onEnable : function(){
15721         this.getActionEl().removeClass(this.disabledClass);
15722         this.el.dom.disabled = false;
15723     },
15724
15725     /**
15726      * Convenience function for setting disabled/enabled by boolean.
15727      * @param {Boolean} disabled
15728      */
15729     setDisabled : function(disabled){
15730         this[disabled ? "disable" : "enable"]();
15731     },
15732
15733     /**
15734      * Show this component.
15735      * @return {Roo.Component} this
15736      */
15737     show: function(){
15738         if(this.fireEvent("beforeshow", this) !== false){
15739             this.hidden = false;
15740             if(this.rendered){
15741                 this.onShow();
15742             }
15743             this.fireEvent("show", this);
15744         }
15745         return this;
15746     },
15747
15748     // private
15749     onShow : function(){
15750         var ae = this.getActionEl();
15751         if(this.hideMode == 'visibility'){
15752             ae.dom.style.visibility = "visible";
15753         }else if(this.hideMode == 'offsets'){
15754             ae.removeClass('x-hidden');
15755         }else{
15756             ae.dom.style.display = "";
15757         }
15758     },
15759
15760     /**
15761      * Hide this component.
15762      * @return {Roo.Component} this
15763      */
15764     hide: function(){
15765         if(this.fireEvent("beforehide", this) !== false){
15766             this.hidden = true;
15767             if(this.rendered){
15768                 this.onHide();
15769             }
15770             this.fireEvent("hide", this);
15771         }
15772         return this;
15773     },
15774
15775     // private
15776     onHide : function(){
15777         var ae = this.getActionEl();
15778         if(this.hideMode == 'visibility'){
15779             ae.dom.style.visibility = "hidden";
15780         }else if(this.hideMode == 'offsets'){
15781             ae.addClass('x-hidden');
15782         }else{
15783             ae.dom.style.display = "none";
15784         }
15785     },
15786
15787     /**
15788      * Convenience function to hide or show this component by boolean.
15789      * @param {Boolean} visible True to show, false to hide
15790      * @return {Roo.Component} this
15791      */
15792     setVisible: function(visible){
15793         if(visible) {
15794             this.show();
15795         }else{
15796             this.hide();
15797         }
15798         return this;
15799     },
15800
15801     /**
15802      * Returns true if this component is visible.
15803      */
15804     isVisible : function(){
15805         return this.getActionEl().isVisible();
15806     },
15807
15808     cloneConfig : function(overrides){
15809         overrides = overrides || {};
15810         var id = overrides.id || Roo.id();
15811         var cfg = Roo.applyIf(overrides, this.initialConfig);
15812         cfg.id = id; // prevent dup id
15813         return new this.constructor(cfg);
15814     }
15815 });/*
15816  * Based on:
15817  * Ext JS Library 1.1.1
15818  * Copyright(c) 2006-2007, Ext JS, LLC.
15819  *
15820  * Originally Released Under LGPL - original licence link has changed is not relivant.
15821  *
15822  * Fork - LGPL
15823  * <script type="text/javascript">
15824  */
15825
15826 /**
15827  * @class Roo.BoxComponent
15828  * @extends Roo.Component
15829  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
15830  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
15831  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
15832  * layout containers.
15833  * @constructor
15834  * @param {Roo.Element/String/Object} config The configuration options.
15835  */
15836 Roo.BoxComponent = function(config){
15837     Roo.Component.call(this, config);
15838     this.addEvents({
15839         /**
15840          * @event resize
15841          * Fires after the component is resized.
15842              * @param {Roo.Component} this
15843              * @param {Number} adjWidth The box-adjusted width that was set
15844              * @param {Number} adjHeight The box-adjusted height that was set
15845              * @param {Number} rawWidth The width that was originally specified
15846              * @param {Number} rawHeight The height that was originally specified
15847              */
15848         resize : true,
15849         /**
15850          * @event move
15851          * Fires after the component is moved.
15852              * @param {Roo.Component} this
15853              * @param {Number} x The new x position
15854              * @param {Number} y The new y position
15855              */
15856         move : true
15857     });
15858 };
15859
15860 Roo.extend(Roo.BoxComponent, Roo.Component, {
15861     // private, set in afterRender to signify that the component has been rendered
15862     boxReady : false,
15863     // private, used to defer height settings to subclasses
15864     deferHeight: false,
15865     /** @cfg {Number} width
15866      * width (optional) size of component
15867      */
15868      /** @cfg {Number} height
15869      * height (optional) size of component
15870      */
15871      
15872     /**
15873      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
15874      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
15875      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
15876      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
15877      * @return {Roo.BoxComponent} this
15878      */
15879     setSize : function(w, h){
15880         // support for standard size objects
15881         if(typeof w == 'object'){
15882             h = w.height;
15883             w = w.width;
15884         }
15885         // not rendered
15886         if(!this.boxReady){
15887             this.width = w;
15888             this.height = h;
15889             return this;
15890         }
15891
15892         // prevent recalcs when not needed
15893         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
15894             return this;
15895         }
15896         this.lastSize = {width: w, height: h};
15897
15898         var adj = this.adjustSize(w, h);
15899         var aw = adj.width, ah = adj.height;
15900         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
15901             var rz = this.getResizeEl();
15902             if(!this.deferHeight && aw !== undefined && ah !== undefined){
15903                 rz.setSize(aw, ah);
15904             }else if(!this.deferHeight && ah !== undefined){
15905                 rz.setHeight(ah);
15906             }else if(aw !== undefined){
15907                 rz.setWidth(aw);
15908             }
15909             this.onResize(aw, ah, w, h);
15910             this.fireEvent('resize', this, aw, ah, w, h);
15911         }
15912         return this;
15913     },
15914
15915     /**
15916      * Gets the current size of the component's underlying element.
15917      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
15918      */
15919     getSize : function(){
15920         return this.el.getSize();
15921     },
15922
15923     /**
15924      * Gets the current XY position of the component's underlying element.
15925      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15926      * @return {Array} The XY position of the element (e.g., [100, 200])
15927      */
15928     getPosition : function(local){
15929         if(local === true){
15930             return [this.el.getLeft(true), this.el.getTop(true)];
15931         }
15932         return this.xy || this.el.getXY();
15933     },
15934
15935     /**
15936      * Gets the current box measurements of the component's underlying element.
15937      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15938      * @returns {Object} box An object in the format {x, y, width, height}
15939      */
15940     getBox : function(local){
15941         var s = this.el.getSize();
15942         if(local){
15943             s.x = this.el.getLeft(true);
15944             s.y = this.el.getTop(true);
15945         }else{
15946             var xy = this.xy || this.el.getXY();
15947             s.x = xy[0];
15948             s.y = xy[1];
15949         }
15950         return s;
15951     },
15952
15953     /**
15954      * Sets the current box measurements of the component's underlying element.
15955      * @param {Object} box An object in the format {x, y, width, height}
15956      * @returns {Roo.BoxComponent} this
15957      */
15958     updateBox : function(box){
15959         this.setSize(box.width, box.height);
15960         this.setPagePosition(box.x, box.y);
15961         return this;
15962     },
15963
15964     // protected
15965     getResizeEl : function(){
15966         return this.resizeEl || this.el;
15967     },
15968
15969     // protected
15970     getPositionEl : function(){
15971         return this.positionEl || this.el;
15972     },
15973
15974     /**
15975      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
15976      * This method fires the move event.
15977      * @param {Number} left The new left
15978      * @param {Number} top The new top
15979      * @returns {Roo.BoxComponent} this
15980      */
15981     setPosition : function(x, y){
15982         this.x = x;
15983         this.y = y;
15984         if(!this.boxReady){
15985             return this;
15986         }
15987         var adj = this.adjustPosition(x, y);
15988         var ax = adj.x, ay = adj.y;
15989
15990         var el = this.getPositionEl();
15991         if(ax !== undefined || ay !== undefined){
15992             if(ax !== undefined && ay !== undefined){
15993                 el.setLeftTop(ax, ay);
15994             }else if(ax !== undefined){
15995                 el.setLeft(ax);
15996             }else if(ay !== undefined){
15997                 el.setTop(ay);
15998             }
15999             this.onPosition(ax, ay);
16000             this.fireEvent('move', this, ax, ay);
16001         }
16002         return this;
16003     },
16004
16005     /**
16006      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
16007      * This method fires the move event.
16008      * @param {Number} x The new x position
16009      * @param {Number} y The new y position
16010      * @returns {Roo.BoxComponent} this
16011      */
16012     setPagePosition : function(x, y){
16013         this.pageX = x;
16014         this.pageY = y;
16015         if(!this.boxReady){
16016             return;
16017         }
16018         if(x === undefined || y === undefined){ // cannot translate undefined points
16019             return;
16020         }
16021         var p = this.el.translatePoints(x, y);
16022         this.setPosition(p.left, p.top);
16023         return this;
16024     },
16025
16026     // private
16027     onRender : function(ct, position){
16028         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
16029         if(this.resizeEl){
16030             this.resizeEl = Roo.get(this.resizeEl);
16031         }
16032         if(this.positionEl){
16033             this.positionEl = Roo.get(this.positionEl);
16034         }
16035     },
16036
16037     // private
16038     afterRender : function(){
16039         Roo.BoxComponent.superclass.afterRender.call(this);
16040         this.boxReady = true;
16041         this.setSize(this.width, this.height);
16042         if(this.x || this.y){
16043             this.setPosition(this.x, this.y);
16044         }
16045         if(this.pageX || this.pageY){
16046             this.setPagePosition(this.pageX, this.pageY);
16047         }
16048     },
16049
16050     /**
16051      * Force the component's size to recalculate based on the underlying element's current height and width.
16052      * @returns {Roo.BoxComponent} this
16053      */
16054     syncSize : function(){
16055         delete this.lastSize;
16056         this.setSize(this.el.getWidth(), this.el.getHeight());
16057         return this;
16058     },
16059
16060     /**
16061      * Called after the component is resized, this method is empty by default but can be implemented by any
16062      * subclass that needs to perform custom logic after a resize occurs.
16063      * @param {Number} adjWidth The box-adjusted width that was set
16064      * @param {Number} adjHeight The box-adjusted height that was set
16065      * @param {Number} rawWidth The width that was originally specified
16066      * @param {Number} rawHeight The height that was originally specified
16067      */
16068     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
16069
16070     },
16071
16072     /**
16073      * Called after the component is moved, this method is empty by default but can be implemented by any
16074      * subclass that needs to perform custom logic after a move occurs.
16075      * @param {Number} x The new x position
16076      * @param {Number} y The new y position
16077      */
16078     onPosition : function(x, y){
16079
16080     },
16081
16082     // private
16083     adjustSize : function(w, h){
16084         if(this.autoWidth){
16085             w = 'auto';
16086         }
16087         if(this.autoHeight){
16088             h = 'auto';
16089         }
16090         return {width : w, height: h};
16091     },
16092
16093     // private
16094     adjustPosition : function(x, y){
16095         return {x : x, y: y};
16096     }
16097 });/*
16098  * Based on:
16099  * Ext JS Library 1.1.1
16100  * Copyright(c) 2006-2007, Ext JS, LLC.
16101  *
16102  * Originally Released Under LGPL - original licence link has changed is not relivant.
16103  *
16104  * Fork - LGPL
16105  * <script type="text/javascript">
16106  */
16107  (function(){ 
16108 /**
16109  * @class Roo.Layer
16110  * @extends Roo.Element
16111  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
16112  * automatic maintaining of shadow/shim positions.
16113  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
16114  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
16115  * you can pass a string with a CSS class name. False turns off the shadow.
16116  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
16117  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
16118  * @cfg {String} cls CSS class to add to the element
16119  * @cfg {Number} zindex Starting z-index (defaults to 11000)
16120  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
16121  * @constructor
16122  * @param {Object} config An object with config options.
16123  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
16124  */
16125
16126 Roo.Layer = function(config, existingEl){
16127     config = config || {};
16128     var dh = Roo.DomHelper;
16129     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
16130     if(existingEl){
16131         this.dom = Roo.getDom(existingEl);
16132     }
16133     if(!this.dom){
16134         var o = config.dh || {tag: "div", cls: "x-layer"};
16135         this.dom = dh.append(pel, o);
16136     }
16137     if(config.cls){
16138         this.addClass(config.cls);
16139     }
16140     this.constrain = config.constrain !== false;
16141     this.visibilityMode = Roo.Element.VISIBILITY;
16142     if(config.id){
16143         this.id = this.dom.id = config.id;
16144     }else{
16145         this.id = Roo.id(this.dom);
16146     }
16147     this.zindex = config.zindex || this.getZIndex();
16148     this.position("absolute", this.zindex);
16149     if(config.shadow){
16150         this.shadowOffset = config.shadowOffset || 4;
16151         this.shadow = new Roo.Shadow({
16152             offset : this.shadowOffset,
16153             mode : config.shadow
16154         });
16155     }else{
16156         this.shadowOffset = 0;
16157     }
16158     this.useShim = config.shim !== false && Roo.useShims;
16159     this.useDisplay = config.useDisplay;
16160     this.hide();
16161 };
16162
16163 var supr = Roo.Element.prototype;
16164
16165 // shims are shared among layer to keep from having 100 iframes
16166 var shims = [];
16167
16168 Roo.extend(Roo.Layer, Roo.Element, {
16169
16170     getZIndex : function(){
16171         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
16172     },
16173
16174     getShim : function(){
16175         if(!this.useShim){
16176             return null;
16177         }
16178         if(this.shim){
16179             return this.shim;
16180         }
16181         var shim = shims.shift();
16182         if(!shim){
16183             shim = this.createShim();
16184             shim.enableDisplayMode('block');
16185             shim.dom.style.display = 'none';
16186             shim.dom.style.visibility = 'visible';
16187         }
16188         var pn = this.dom.parentNode;
16189         if(shim.dom.parentNode != pn){
16190             pn.insertBefore(shim.dom, this.dom);
16191         }
16192         shim.setStyle('z-index', this.getZIndex()-2);
16193         this.shim = shim;
16194         return shim;
16195     },
16196
16197     hideShim : function(){
16198         if(this.shim){
16199             this.shim.setDisplayed(false);
16200             shims.push(this.shim);
16201             delete this.shim;
16202         }
16203     },
16204
16205     disableShadow : function(){
16206         if(this.shadow){
16207             this.shadowDisabled = true;
16208             this.shadow.hide();
16209             this.lastShadowOffset = this.shadowOffset;
16210             this.shadowOffset = 0;
16211         }
16212     },
16213
16214     enableShadow : function(show){
16215         if(this.shadow){
16216             this.shadowDisabled = false;
16217             this.shadowOffset = this.lastShadowOffset;
16218             delete this.lastShadowOffset;
16219             if(show){
16220                 this.sync(true);
16221             }
16222         }
16223     },
16224
16225     // private
16226     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
16227     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
16228     sync : function(doShow){
16229         var sw = this.shadow;
16230         if(!this.updating && this.isVisible() && (sw || this.useShim)){
16231             var sh = this.getShim();
16232
16233             var w = this.getWidth(),
16234                 h = this.getHeight();
16235
16236             var l = this.getLeft(true),
16237                 t = this.getTop(true);
16238
16239             if(sw && !this.shadowDisabled){
16240                 if(doShow && !sw.isVisible()){
16241                     sw.show(this);
16242                 }else{
16243                     sw.realign(l, t, w, h);
16244                 }
16245                 if(sh){
16246                     if(doShow){
16247                        sh.show();
16248                     }
16249                     // fit the shim behind the shadow, so it is shimmed too
16250                     var a = sw.adjusts, s = sh.dom.style;
16251                     s.left = (Math.min(l, l+a.l))+"px";
16252                     s.top = (Math.min(t, t+a.t))+"px";
16253                     s.width = (w+a.w)+"px";
16254                     s.height = (h+a.h)+"px";
16255                 }
16256             }else if(sh){
16257                 if(doShow){
16258                    sh.show();
16259                 }
16260                 sh.setSize(w, h);
16261                 sh.setLeftTop(l, t);
16262             }
16263             
16264         }
16265     },
16266
16267     // private
16268     destroy : function(){
16269         this.hideShim();
16270         if(this.shadow){
16271             this.shadow.hide();
16272         }
16273         this.removeAllListeners();
16274         var pn = this.dom.parentNode;
16275         if(pn){
16276             pn.removeChild(this.dom);
16277         }
16278         Roo.Element.uncache(this.id);
16279     },
16280
16281     remove : function(){
16282         this.destroy();
16283     },
16284
16285     // private
16286     beginUpdate : function(){
16287         this.updating = true;
16288     },
16289
16290     // private
16291     endUpdate : function(){
16292         this.updating = false;
16293         this.sync(true);
16294     },
16295
16296     // private
16297     hideUnders : function(negOffset){
16298         if(this.shadow){
16299             this.shadow.hide();
16300         }
16301         this.hideShim();
16302     },
16303
16304     // private
16305     constrainXY : function(){
16306         if(this.constrain){
16307             var vw = Roo.lib.Dom.getViewWidth(),
16308                 vh = Roo.lib.Dom.getViewHeight();
16309             var s = Roo.get(document).getScroll();
16310
16311             var xy = this.getXY();
16312             var x = xy[0], y = xy[1];   
16313             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
16314             // only move it if it needs it
16315             var moved = false;
16316             // first validate right/bottom
16317             if((x + w) > vw+s.left){
16318                 x = vw - w - this.shadowOffset;
16319                 moved = true;
16320             }
16321             if((y + h) > vh+s.top){
16322                 y = vh - h - this.shadowOffset;
16323                 moved = true;
16324             }
16325             // then make sure top/left isn't negative
16326             if(x < s.left){
16327                 x = s.left;
16328                 moved = true;
16329             }
16330             if(y < s.top){
16331                 y = s.top;
16332                 moved = true;
16333             }
16334             if(moved){
16335                 if(this.avoidY){
16336                     var ay = this.avoidY;
16337                     if(y <= ay && (y+h) >= ay){
16338                         y = ay-h-5;   
16339                     }
16340                 }
16341                 xy = [x, y];
16342                 this.storeXY(xy);
16343                 supr.setXY.call(this, xy);
16344                 this.sync();
16345             }
16346         }
16347     },
16348
16349     isVisible : function(){
16350         return this.visible;    
16351     },
16352
16353     // private
16354     showAction : function(){
16355         this.visible = true; // track visibility to prevent getStyle calls
16356         if(this.useDisplay === true){
16357             this.setDisplayed("");
16358         }else if(this.lastXY){
16359             supr.setXY.call(this, this.lastXY);
16360         }else if(this.lastLT){
16361             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
16362         }
16363     },
16364
16365     // private
16366     hideAction : function(){
16367         this.visible = false;
16368         if(this.useDisplay === true){
16369             this.setDisplayed(false);
16370         }else{
16371             this.setLeftTop(-10000,-10000);
16372         }
16373     },
16374
16375     // overridden Element method
16376     setVisible : function(v, a, d, c, e){
16377         if(v){
16378             this.showAction();
16379         }
16380         if(a && v){
16381             var cb = function(){
16382                 this.sync(true);
16383                 if(c){
16384                     c();
16385                 }
16386             }.createDelegate(this);
16387             supr.setVisible.call(this, true, true, d, cb, e);
16388         }else{
16389             if(!v){
16390                 this.hideUnders(true);
16391             }
16392             var cb = c;
16393             if(a){
16394                 cb = function(){
16395                     this.hideAction();
16396                     if(c){
16397                         c();
16398                     }
16399                 }.createDelegate(this);
16400             }
16401             supr.setVisible.call(this, v, a, d, cb, e);
16402             if(v){
16403                 this.sync(true);
16404             }else if(!a){
16405                 this.hideAction();
16406             }
16407         }
16408     },
16409
16410     storeXY : function(xy){
16411         delete this.lastLT;
16412         this.lastXY = xy;
16413     },
16414
16415     storeLeftTop : function(left, top){
16416         delete this.lastXY;
16417         this.lastLT = [left, top];
16418     },
16419
16420     // private
16421     beforeFx : function(){
16422         this.beforeAction();
16423         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
16424     },
16425
16426     // private
16427     afterFx : function(){
16428         Roo.Layer.superclass.afterFx.apply(this, arguments);
16429         this.sync(this.isVisible());
16430     },
16431
16432     // private
16433     beforeAction : function(){
16434         if(!this.updating && this.shadow){
16435             this.shadow.hide();
16436         }
16437     },
16438
16439     // overridden Element method
16440     setLeft : function(left){
16441         this.storeLeftTop(left, this.getTop(true));
16442         supr.setLeft.apply(this, arguments);
16443         this.sync();
16444     },
16445
16446     setTop : function(top){
16447         this.storeLeftTop(this.getLeft(true), top);
16448         supr.setTop.apply(this, arguments);
16449         this.sync();
16450     },
16451
16452     setLeftTop : function(left, top){
16453         this.storeLeftTop(left, top);
16454         supr.setLeftTop.apply(this, arguments);
16455         this.sync();
16456     },
16457
16458     setXY : function(xy, a, d, c, e){
16459         this.fixDisplay();
16460         this.beforeAction();
16461         this.storeXY(xy);
16462         var cb = this.createCB(c);
16463         supr.setXY.call(this, xy, a, d, cb, e);
16464         if(!a){
16465             cb();
16466         }
16467     },
16468
16469     // private
16470     createCB : function(c){
16471         var el = this;
16472         return function(){
16473             el.constrainXY();
16474             el.sync(true);
16475             if(c){
16476                 c();
16477             }
16478         };
16479     },
16480
16481     // overridden Element method
16482     setX : function(x, a, d, c, e){
16483         this.setXY([x, this.getY()], a, d, c, e);
16484     },
16485
16486     // overridden Element method
16487     setY : function(y, a, d, c, e){
16488         this.setXY([this.getX(), y], a, d, c, e);
16489     },
16490
16491     // overridden Element method
16492     setSize : function(w, h, a, d, c, e){
16493         this.beforeAction();
16494         var cb = this.createCB(c);
16495         supr.setSize.call(this, w, h, a, d, cb, e);
16496         if(!a){
16497             cb();
16498         }
16499     },
16500
16501     // overridden Element method
16502     setWidth : function(w, a, d, c, e){
16503         this.beforeAction();
16504         var cb = this.createCB(c);
16505         supr.setWidth.call(this, w, a, d, cb, e);
16506         if(!a){
16507             cb();
16508         }
16509     },
16510
16511     // overridden Element method
16512     setHeight : function(h, a, d, c, e){
16513         this.beforeAction();
16514         var cb = this.createCB(c);
16515         supr.setHeight.call(this, h, a, d, cb, e);
16516         if(!a){
16517             cb();
16518         }
16519     },
16520
16521     // overridden Element method
16522     setBounds : function(x, y, w, h, a, d, c, e){
16523         this.beforeAction();
16524         var cb = this.createCB(c);
16525         if(!a){
16526             this.storeXY([x, y]);
16527             supr.setXY.call(this, [x, y]);
16528             supr.setSize.call(this, w, h, a, d, cb, e);
16529             cb();
16530         }else{
16531             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
16532         }
16533         return this;
16534     },
16535     
16536     /**
16537      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
16538      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
16539      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
16540      * @param {Number} zindex The new z-index to set
16541      * @return {this} The Layer
16542      */
16543     setZIndex : function(zindex){
16544         this.zindex = zindex;
16545         this.setStyle("z-index", zindex + 2);
16546         if(this.shadow){
16547             this.shadow.setZIndex(zindex + 1);
16548         }
16549         if(this.shim){
16550             this.shim.setStyle("z-index", zindex);
16551         }
16552     }
16553 });
16554 })();/*
16555  * Original code for Roojs - LGPL
16556  * <script type="text/javascript">
16557  */
16558  
16559 /**
16560  * @class Roo.XComponent
16561  * A delayed Element creator...
16562  * Or a way to group chunks of interface together.
16563  * technically this is a wrapper around a tree of Roo elements (which defines a 'module'),
16564  *  used in conjunction with XComponent.build() it will create an instance of each element,
16565  *  then call addxtype() to build the User interface.
16566  * 
16567  * Mypart.xyx = new Roo.XComponent({
16568
16569     parent : 'Mypart.xyz', // empty == document.element.!!
16570     order : '001',
16571     name : 'xxxx'
16572     region : 'xxxx'
16573     disabled : function() {} 
16574      
16575     tree : function() { // return an tree of xtype declared components
16576         var MODULE = this;
16577         return 
16578         {
16579             xtype : 'NestedLayoutPanel',
16580             // technicall
16581         }
16582      ]
16583  *})
16584  *
16585  *
16586  * It can be used to build a big heiracy, with parent etc.
16587  * or you can just use this to render a single compoent to a dom element
16588  * MYPART.render(Roo.Element | String(id) | dom_element )
16589  *
16590  *
16591  * Usage patterns.
16592  *
16593  * Classic Roo
16594  *
16595  * Roo is designed primarily as a single page application, so the UI build for a standard interface will
16596  * expect a single 'TOP' level module normally indicated by the 'parent' of the XComponent definition being defined as false.
16597  *
16598  * Each sub module is expected to have a parent pointing to the class name of it's parent module.
16599  *
16600  * When the top level is false, a 'Roo.BorderLayout' is created and the element is flagged as 'topModule'
16601  * - if mulitple topModules exist, the last one is defined as the top module.
16602  *
16603  * Embeded Roo
16604  * 
16605  * When the top level or multiple modules are to embedded into a existing HTML page,
16606  * the parent element can container '#id' of the element where the module will be drawn.
16607  *
16608  * Bootstrap Roo
16609  *
16610  * Unlike classic Roo, the bootstrap tends not to be used as a single page.
16611  * it relies more on a include mechanism, where sub modules are included into an outer page.
16612  * This is normally managed by the builder tools using Roo.apply( options, Included.Sub.Module )
16613  * 
16614  * Bootstrap Roo Included elements
16615  *
16616  * Our builder application needs the ability to preview these sub compoennts. They will normally have parent=false set,
16617  * hence confusing the component builder as it thinks there are multiple top level elements. 
16618  *
16619  * String Over-ride & Translations
16620  *
16621  * Our builder application writes all the strings as _strings and _named_strings. This is to enable the translation of elements,
16622  * and also the 'overlaying of string values - needed when different versions of the same application with different text content
16623  * are needed. @see Roo.XComponent.overlayString  
16624  * 
16625  * 
16626  * 
16627  * @extends Roo.util.Observable
16628  * @constructor
16629  * @param cfg {Object} configuration of component
16630  * 
16631  */
16632 Roo.XComponent = function(cfg) {
16633     Roo.apply(this, cfg);
16634     this.addEvents({ 
16635         /**
16636              * @event built
16637              * Fires when this the componnt is built
16638              * @param {Roo.XComponent} c the component
16639              */
16640         'built' : true
16641         
16642     });
16643     this.region = this.region || 'center'; // default..
16644     Roo.XComponent.register(this);
16645     this.modules = false;
16646     this.el = false; // where the layout goes..
16647     
16648     
16649 }
16650 Roo.extend(Roo.XComponent, Roo.util.Observable, {
16651     /**
16652      * @property el
16653      * The created element (with Roo.factory())
16654      * @type {Roo.Layout}
16655      */
16656     el  : false,
16657     
16658     /**
16659      * @property el
16660      * for BC  - use el in new code
16661      * @type {Roo.Layout}
16662      */
16663     panel : false,
16664     
16665     /**
16666      * @property layout
16667      * for BC  - use el in new code
16668      * @type {Roo.Layout}
16669      */
16670     layout : false,
16671     
16672      /**
16673      * @cfg {Function|boolean} disabled
16674      * If this module is disabled by some rule, return true from the funtion
16675      */
16676     disabled : false,
16677     
16678     /**
16679      * @cfg {String} parent 
16680      * Name of parent element which it get xtype added to..
16681      */
16682     parent: false,
16683     
16684     /**
16685      * @cfg {String} order
16686      * Used to set the order in which elements are created (usefull for multiple tabs)
16687      */
16688     
16689     order : false,
16690     /**
16691      * @cfg {String} name
16692      * String to display while loading.
16693      */
16694     name : false,
16695     /**
16696      * @cfg {String} region
16697      * Region to render component to (defaults to center)
16698      */
16699     region : 'center',
16700     
16701     /**
16702      * @cfg {Array} items
16703      * A single item array - the first element is the root of the tree..
16704      * It's done this way to stay compatible with the Xtype system...
16705      */
16706     items : false,
16707     
16708     /**
16709      * @property _tree
16710      * The method that retuns the tree of parts that make up this compoennt 
16711      * @type {function}
16712      */
16713     _tree  : false,
16714     
16715      /**
16716      * render
16717      * render element to dom or tree
16718      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
16719      */
16720     
16721     render : function(el)
16722     {
16723         
16724         el = el || false;
16725         var hp = this.parent ? 1 : 0;
16726         Roo.debug &&  Roo.log(this);
16727         
16728         var tree = this._tree ? this._tree() : this.tree();
16729
16730         
16731         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
16732             // if parent is a '#.....' string, then let's use that..
16733             var ename = this.parent.substr(1);
16734             this.parent = false;
16735             Roo.debug && Roo.log(ename);
16736             switch (ename) {
16737                 case 'bootstrap-body':
16738                     if (typeof(tree.el) != 'undefined' && tree.el == document.body)  {
16739                         // this is the BorderLayout standard?
16740                        this.parent = { el : true };
16741                        break;
16742                     }
16743                     if (["Nest", "Content", "Grid", "Tree"].indexOf(tree.xtype)  > -1)  {
16744                         // need to insert stuff...
16745                         this.parent =  {
16746                              el : new Roo.bootstrap.layout.Border({
16747                                  el : document.body, 
16748                      
16749                                  center: {
16750                                     titlebar: false,
16751                                     autoScroll:false,
16752                                     closeOnTab: true,
16753                                     tabPosition: 'top',
16754                                       //resizeTabs: true,
16755                                     alwaysShowTabs: true,
16756                                     hideTabs: false
16757                                      //minTabWidth: 140
16758                                  }
16759                              })
16760                         
16761                          };
16762                          break;
16763                     }
16764                          
16765                     if (typeof(Roo.bootstrap.Body) != 'undefined' ) {
16766                         this.parent = { el :  new  Roo.bootstrap.Body() };
16767                         Roo.debug && Roo.log("setting el to doc body");
16768                          
16769                     } else {
16770                         throw "Container is bootstrap body, but Roo.bootstrap.Body is not defined";
16771                     }
16772                     break;
16773                 case 'bootstrap':
16774                     this.parent = { el : true};
16775                     // fall through
16776                 default:
16777                     el = Roo.get(ename);
16778                     if (typeof(Roo.bootstrap) != 'undefined' && tree['|xns'] == 'Roo.bootstrap') {
16779                         this.parent = { el : true};
16780                     }
16781                     
16782                     break;
16783             }
16784                 
16785             
16786             if (!el && !this.parent) {
16787                 Roo.debug && Roo.log("Warning - element can not be found :#" + ename );
16788                 return;
16789             }
16790         }
16791         
16792         Roo.debug && Roo.log("EL:");
16793         Roo.debug && Roo.log(el);
16794         Roo.debug && Roo.log("this.parent.el:");
16795         Roo.debug && Roo.log(this.parent.el);
16796         
16797
16798         // altertive root elements ??? - we need a better way to indicate these.
16799         var is_alt = Roo.XComponent.is_alt ||
16800                     (typeof(tree.el) != 'undefined' && tree.el == document.body) ||
16801                     (typeof(Roo.bootstrap) != 'undefined' && tree.xns == Roo.bootstrap) ||
16802                     (typeof(Roo.mailer) != 'undefined' && tree.xns == Roo.mailer) ;
16803         
16804         
16805         
16806         if (!this.parent && is_alt) {
16807             //el = Roo.get(document.body);
16808             this.parent = { el : true };
16809         }
16810             
16811             
16812         
16813         if (!this.parent) {
16814             
16815             Roo.debug && Roo.log("no parent - creating one");
16816             
16817             el = el ? Roo.get(el) : false;      
16818             
16819             if (typeof(Roo.BorderLayout) == 'undefined' ) {
16820                 
16821                 this.parent =  {
16822                     el : new Roo.bootstrap.layout.Border({
16823                         el: el || document.body,
16824                     
16825                         center: {
16826                             titlebar: false,
16827                             autoScroll:false,
16828                             closeOnTab: true,
16829                             tabPosition: 'top',
16830                              //resizeTabs: true,
16831                             alwaysShowTabs: false,
16832                             hideTabs: true,
16833                             minTabWidth: 140,
16834                             overflow: 'visible'
16835                          }
16836                      })
16837                 };
16838             } else {
16839             
16840                 // it's a top level one..
16841                 this.parent =  {
16842                     el : new Roo.BorderLayout(el || document.body, {
16843                         center: {
16844                             titlebar: false,
16845                             autoScroll:false,
16846                             closeOnTab: true,
16847                             tabPosition: 'top',
16848                              //resizeTabs: true,
16849                             alwaysShowTabs: el && hp? false :  true,
16850                             hideTabs: el || !hp ? true :  false,
16851                             minTabWidth: 140
16852                          }
16853                     })
16854                 };
16855             }
16856         }
16857         
16858         if (!this.parent.el) {
16859                 // probably an old style ctor, which has been disabled.
16860                 return;
16861
16862         }
16863                 // The 'tree' method is  '_tree now' 
16864             
16865         tree.region = tree.region || this.region;
16866         var is_body = false;
16867         if (this.parent.el === true) {
16868             // bootstrap... - body..
16869             if (el) {
16870                 tree.el = el;
16871             }
16872             this.parent.el = Roo.factory(tree);
16873             is_body = true;
16874         }
16875         
16876         this.el = this.parent.el.addxtype(tree, undefined, is_body);
16877         this.fireEvent('built', this);
16878         
16879         this.panel = this.el;
16880         this.layout = this.panel.layout;
16881         this.parentLayout = this.parent.layout  || false;  
16882          
16883     }
16884     
16885 });
16886
16887 Roo.apply(Roo.XComponent, {
16888     /**
16889      * @property  hideProgress
16890      * true to disable the building progress bar.. usefull on single page renders.
16891      * @type Boolean
16892      */
16893     hideProgress : false,
16894     /**
16895      * @property  buildCompleted
16896      * True when the builder has completed building the interface.
16897      * @type Boolean
16898      */
16899     buildCompleted : false,
16900      
16901     /**
16902      * @property  topModule
16903      * the upper most module - uses document.element as it's constructor.
16904      * @type Object
16905      */
16906      
16907     topModule  : false,
16908       
16909     /**
16910      * @property  modules
16911      * array of modules to be created by registration system.
16912      * @type {Array} of Roo.XComponent
16913      */
16914     
16915     modules : [],
16916     /**
16917      * @property  elmodules
16918      * array of modules to be created by which use #ID 
16919      * @type {Array} of Roo.XComponent
16920      */
16921      
16922     elmodules : [],
16923
16924      /**
16925      * @property  is_alt
16926      * Is an alternative Root - normally used by bootstrap or other systems,
16927      *    where the top element in the tree can wrap 'body' 
16928      * @type {boolean}  (default false)
16929      */
16930      
16931     is_alt : false,
16932     /**
16933      * @property  build_from_html
16934      * Build elements from html - used by bootstrap HTML stuff 
16935      *    - this is cleared after build is completed
16936      * @type {boolean}    (default false)
16937      */
16938      
16939     build_from_html : false,
16940     /**
16941      * Register components to be built later.
16942      *
16943      * This solves the following issues
16944      * - Building is not done on page load, but after an authentication process has occured.
16945      * - Interface elements are registered on page load
16946      * - Parent Interface elements may not be loaded before child, so this handles that..
16947      * 
16948      *
16949      * example:
16950      * 
16951      * MyApp.register({
16952           order : '000001',
16953           module : 'Pman.Tab.projectMgr',
16954           region : 'center',
16955           parent : 'Pman.layout',
16956           disabled : false,  // or use a function..
16957         })
16958      
16959      * * @param {Object} details about module
16960      */
16961     register : function(obj) {
16962                 
16963         Roo.XComponent.event.fireEvent('register', obj);
16964         switch(typeof(obj.disabled) ) {
16965                 
16966             case 'undefined':
16967                 break;
16968             
16969             case 'function':
16970                 if ( obj.disabled() ) {
16971                         return;
16972                 }
16973                 break;
16974             
16975             default:
16976                 if (obj.disabled || obj.region == '#disabled') {
16977                         return;
16978                 }
16979                 break;
16980         }
16981                 
16982         this.modules.push(obj);
16983          
16984     },
16985     /**
16986      * convert a string to an object..
16987      * eg. 'AAA.BBB' -> finds AAA.BBB
16988
16989      */
16990     
16991     toObject : function(str)
16992     {
16993         if (!str || typeof(str) == 'object') {
16994             return str;
16995         }
16996         if (str.substring(0,1) == '#') {
16997             return str;
16998         }
16999
17000         var ar = str.split('.');
17001         var rt, o;
17002         rt = ar.shift();
17003             /** eval:var:o */
17004         try {
17005             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
17006         } catch (e) {
17007             throw "Module not found : " + str;
17008         }
17009         
17010         if (o === false) {
17011             throw "Module not found : " + str;
17012         }
17013         Roo.each(ar, function(e) {
17014             if (typeof(o[e]) == 'undefined') {
17015                 throw "Module not found : " + str;
17016             }
17017             o = o[e];
17018         });
17019         
17020         return o;
17021         
17022     },
17023     
17024     
17025     /**
17026      * move modules into their correct place in the tree..
17027      * 
17028      */
17029     preBuild : function ()
17030     {
17031         var _t = this;
17032         Roo.each(this.modules , function (obj)
17033         {
17034             Roo.XComponent.event.fireEvent('beforebuild', obj);
17035             
17036             var opar = obj.parent;
17037             try { 
17038                 obj.parent = this.toObject(opar);
17039             } catch(e) {
17040                 Roo.debug && Roo.log("parent:toObject failed: " + e.toString());
17041                 return;
17042             }
17043             
17044             if (!obj.parent) {
17045                 Roo.debug && Roo.log("GOT top level module");
17046                 Roo.debug && Roo.log(obj);
17047                 obj.modules = new Roo.util.MixedCollection(false, 
17048                     function(o) { return o.order + '' }
17049                 );
17050                 this.topModule = obj;
17051                 return;
17052             }
17053                         // parent is a string (usually a dom element name..)
17054             if (typeof(obj.parent) == 'string') {
17055                 this.elmodules.push(obj);
17056                 return;
17057             }
17058             if (obj.parent.constructor != Roo.XComponent) {
17059                 Roo.debug && Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
17060             }
17061             if (!obj.parent.modules) {
17062                 obj.parent.modules = new Roo.util.MixedCollection(false, 
17063                     function(o) { return o.order + '' }
17064                 );
17065             }
17066             if (obj.parent.disabled) {
17067                 obj.disabled = true;
17068             }
17069             obj.parent.modules.add(obj);
17070         }, this);
17071     },
17072     
17073      /**
17074      * make a list of modules to build.
17075      * @return {Array} list of modules. 
17076      */ 
17077     
17078     buildOrder : function()
17079     {
17080         var _this = this;
17081         var cmp = function(a,b) {   
17082             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
17083         };
17084         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
17085             throw "No top level modules to build";
17086         }
17087         
17088         // make a flat list in order of modules to build.
17089         var mods = this.topModule ? [ this.topModule ] : [];
17090                 
17091         
17092         // elmodules (is a list of DOM based modules )
17093         Roo.each(this.elmodules, function(e) {
17094             mods.push(e);
17095             if (!this.topModule &&
17096                 typeof(e.parent) == 'string' &&
17097                 e.parent.substring(0,1) == '#' &&
17098                 Roo.get(e.parent.substr(1))
17099                ) {
17100                 
17101                 _this.topModule = e;
17102             }
17103             
17104         });
17105
17106         
17107         // add modules to their parents..
17108         var addMod = function(m) {
17109             Roo.debug && Roo.log("build Order: add: " + m.name);
17110                 
17111             mods.push(m);
17112             if (m.modules && !m.disabled) {
17113                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
17114                 m.modules.keySort('ASC',  cmp );
17115                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
17116     
17117                 m.modules.each(addMod);
17118             } else {
17119                 Roo.debug && Roo.log("build Order: no child modules");
17120             }
17121             // not sure if this is used any more..
17122             if (m.finalize) {
17123                 m.finalize.name = m.name + " (clean up) ";
17124                 mods.push(m.finalize);
17125             }
17126             
17127         }
17128         if (this.topModule && this.topModule.modules) { 
17129             this.topModule.modules.keySort('ASC',  cmp );
17130             this.topModule.modules.each(addMod);
17131         } 
17132         return mods;
17133     },
17134     
17135      /**
17136      * Build the registered modules.
17137      * @param {Object} parent element.
17138      * @param {Function} optional method to call after module has been added.
17139      * 
17140      */ 
17141    
17142     build : function(opts) 
17143     {
17144         
17145         if (typeof(opts) != 'undefined') {
17146             Roo.apply(this,opts);
17147         }
17148         
17149         this.preBuild();
17150         var mods = this.buildOrder();
17151       
17152         //this.allmods = mods;
17153         //Roo.debug && Roo.log(mods);
17154         //return;
17155         if (!mods.length) { // should not happen
17156             throw "NO modules!!!";
17157         }
17158         
17159         
17160         var msg = "Building Interface...";
17161         // flash it up as modal - so we store the mask!?
17162         if (!this.hideProgress && Roo.MessageBox) {
17163             Roo.MessageBox.show({ title: 'loading' });
17164             Roo.MessageBox.show({
17165                title: "Please wait...",
17166                msg: msg,
17167                width:450,
17168                progress:true,
17169                buttons : false,
17170                closable:false,
17171                modal: false
17172               
17173             });
17174         }
17175         var total = mods.length;
17176         
17177         var _this = this;
17178         var progressRun = function() {
17179             if (!mods.length) {
17180                 Roo.debug && Roo.log('hide?');
17181                 if (!this.hideProgress && Roo.MessageBox) {
17182                     Roo.MessageBox.hide();
17183                 }
17184                 Roo.XComponent.build_from_html = false; // reset, so dialogs will be build from javascript
17185                 
17186                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
17187                 
17188                 // THE END...
17189                 return false;   
17190             }
17191             
17192             var m = mods.shift();
17193             
17194             
17195             Roo.debug && Roo.log(m);
17196             // not sure if this is supported any more.. - modules that are are just function
17197             if (typeof(m) == 'function') { 
17198                 m.call(this);
17199                 return progressRun.defer(10, _this);
17200             } 
17201             
17202             
17203             msg = "Building Interface " + (total  - mods.length) + 
17204                     " of " + total + 
17205                     (m.name ? (' - ' + m.name) : '');
17206                         Roo.debug && Roo.log(msg);
17207             if (!_this.hideProgress &&  Roo.MessageBox) { 
17208                 Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
17209             }
17210             
17211          
17212             // is the module disabled?
17213             var disabled = (typeof(m.disabled) == 'function') ?
17214                 m.disabled.call(m.module.disabled) : m.disabled;    
17215             
17216             
17217             if (disabled) {
17218                 return progressRun(); // we do not update the display!
17219             }
17220             
17221             // now build 
17222             
17223                         
17224                         
17225             m.render();
17226             // it's 10 on top level, and 1 on others??? why...
17227             return progressRun.defer(10, _this);
17228              
17229         }
17230         progressRun.defer(1, _this);
17231      
17232         
17233         
17234     },
17235     /**
17236      * Overlay a set of modified strings onto a component
17237      * This is dependant on our builder exporting the strings and 'named strings' elements.
17238      * 
17239      * @param {Object} element to overlay on - eg. Pman.Dialog.Login
17240      * @param {Object} associative array of 'named' string and it's new value.
17241      * 
17242      */
17243         overlayStrings : function( component, strings )
17244     {
17245         if (typeof(component['_named_strings']) == 'undefined') {
17246             throw "ERROR: component does not have _named_strings";
17247         }
17248         for ( var k in strings ) {
17249             var md = typeof(component['_named_strings'][k]) == 'undefined' ? false : component['_named_strings'][k];
17250             if (md !== false) {
17251                 component['_strings'][md] = strings[k];
17252             } else {
17253                 Roo.log('could not find named string: ' + k + ' in');
17254                 Roo.log(component);
17255             }
17256             
17257         }
17258         
17259     },
17260     
17261         
17262         /**
17263          * Event Object.
17264          *
17265          *
17266          */
17267         event: false, 
17268     /**
17269          * wrapper for event.on - aliased later..  
17270          * Typically use to register a event handler for register:
17271          *
17272          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
17273          *
17274          */
17275     on : false
17276    
17277     
17278     
17279 });
17280
17281 Roo.XComponent.event = new Roo.util.Observable({
17282                 events : { 
17283                         /**
17284                          * @event register
17285                          * Fires when an Component is registered,
17286                          * set the disable property on the Component to stop registration.
17287                          * @param {Roo.XComponent} c the component being registerd.
17288                          * 
17289                          */
17290                         'register' : true,
17291             /**
17292                          * @event beforebuild
17293                          * Fires before each Component is built
17294                          * can be used to apply permissions.
17295                          * @param {Roo.XComponent} c the component being registerd.
17296                          * 
17297                          */
17298                         'beforebuild' : true,
17299                         /**
17300                          * @event buildcomplete
17301                          * Fires on the top level element when all elements have been built
17302                          * @param {Roo.XComponent} the top level component.
17303                          */
17304                         'buildcomplete' : true
17305                         
17306                 }
17307 });
17308
17309 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
17310  //
17311  /**
17312  * marked - a markdown parser
17313  * Copyright (c) 2011-2014, Christopher Jeffrey. (MIT Licensed)
17314  * https://github.com/chjj/marked
17315  */
17316
17317
17318 /**
17319  *
17320  * Roo.Markdown - is a very crude wrapper around marked..
17321  *
17322  * usage:
17323  * 
17324  * alert( Roo.Markdown.toHtml("Markdown *rocks*.") );
17325  * 
17326  * Note: move the sample code to the bottom of this
17327  * file before uncommenting it.
17328  *
17329  */
17330
17331 Roo.Markdown = {};
17332 Roo.Markdown.toHtml = function(text) {
17333     
17334     var c = new Roo.Markdown.marked.setOptions({
17335             renderer: new Roo.Markdown.marked.Renderer(),
17336             gfm: true,
17337             tables: true,
17338             breaks: false,
17339             pedantic: false,
17340             sanitize: false,
17341             smartLists: true,
17342             smartypants: false
17343           });
17344     // A FEW HACKS!!?
17345     
17346     text = text.replace(/\\\n/g,' ');
17347     return Roo.Markdown.marked(text);
17348 };
17349 //
17350 // converter
17351 //
17352 // Wraps all "globals" so that the only thing
17353 // exposed is makeHtml().
17354 //
17355 (function() {
17356     
17357      /**
17358          * eval:var:escape
17359          * eval:var:unescape
17360          * eval:var:replace
17361          */
17362       
17363     /**
17364      * Helpers
17365      */
17366     
17367     var escape = function (html, encode) {
17368       return html
17369         .replace(!encode ? /&(?!#?\w+;)/g : /&/g, '&amp;')
17370         .replace(/</g, '&lt;')
17371         .replace(/>/g, '&gt;')
17372         .replace(/"/g, '&quot;')
17373         .replace(/'/g, '&#39;');
17374     }
17375     
17376     var unescape = function (html) {
17377         // explicitly match decimal, hex, and named HTML entities 
17378       return html.replace(/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/g, function(_, n) {
17379         n = n.toLowerCase();
17380         if (n === 'colon') { return ':'; }
17381         if (n.charAt(0) === '#') {
17382           return n.charAt(1) === 'x'
17383             ? String.fromCharCode(parseInt(n.substring(2), 16))
17384             : String.fromCharCode(+n.substring(1));
17385         }
17386         return '';
17387       });
17388     }
17389     
17390     var replace = function (regex, opt) {
17391       regex = regex.source;
17392       opt = opt || '';
17393       return function self(name, val) {
17394         if (!name) { return new RegExp(regex, opt); }
17395         val = val.source || val;
17396         val = val.replace(/(^|[^\[])\^/g, '$1');
17397         regex = regex.replace(name, val);
17398         return self;
17399       };
17400     }
17401
17402
17403          /**
17404          * eval:var:noop
17405     */
17406     var noop = function () {}
17407     noop.exec = noop;
17408     
17409          /**
17410          * eval:var:merge
17411     */
17412     var merge = function (obj) {
17413       var i = 1
17414         , target
17415         , key;
17416     
17417       for (; i < arguments.length; i++) {
17418         target = arguments[i];
17419         for (key in target) {
17420           if (Object.prototype.hasOwnProperty.call(target, key)) {
17421             obj[key] = target[key];
17422           }
17423         }
17424       }
17425     
17426       return obj;
17427     }
17428     
17429     
17430     /**
17431      * Block-Level Grammar
17432      */
17433     
17434     
17435     
17436     
17437     var block = {
17438       newline: /^\n+/,
17439       code: /^( {4}[^\n]+\n*)+/,
17440       fences: noop,
17441       hr: /^( *[-*_]){3,} *(?:\n+|$)/,
17442       heading: /^ *(#{1,6}) *([^\n]+?) *#* *(?:\n+|$)/,
17443       nptable: noop,
17444       lheading: /^([^\n]+)\n *(=|-){2,} *(?:\n+|$)/,
17445       blockquote: /^( *>[^\n]+(\n(?!def)[^\n]+)*\n*)+/,
17446       list: /^( *)(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,
17447       html: /^ *(?:comment *(?:\n|\s*$)|closed *(?:\n{2,}|\s*$)|closing *(?:\n{2,}|\s*$))/,
17448       def: /^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +["(]([^\n]+)[")])? *(?:\n+|$)/,
17449       table: noop,
17450       paragraph: /^((?:[^\n]+\n?(?!hr|heading|lheading|blockquote|tag|def))+)\n*/,
17451       text: /^[^\n]+/
17452     };
17453     
17454     block.bullet = /(?:[*+-]|\d+\.)/;
17455     block.item = /^( *)(bull) [^\n]*(?:\n(?!\1bull )[^\n]*)*/;
17456     block.item = replace(block.item, 'gm')
17457       (/bull/g, block.bullet)
17458       ();
17459     
17460     block.list = replace(block.list)
17461       (/bull/g, block.bullet)
17462       ('hr', '\\n+(?=\\1?(?:[-*_] *){3,}(?:\\n+|$))')
17463       ('def', '\\n+(?=' + block.def.source + ')')
17464       ();
17465     
17466     block.blockquote = replace(block.blockquote)
17467       ('def', block.def)
17468       ();
17469     
17470     block._tag = '(?!(?:'
17471       + 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code'
17472       + '|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo'
17473       + '|span|br|wbr|ins|del|img)\\b)\\w+(?!:/|[^\\w\\s@]*@)\\b';
17474     
17475     block.html = replace(block.html)
17476       ('comment', /<!--[\s\S]*?-->/)
17477       ('closed', /<(tag)[\s\S]+?<\/\1>/)
17478       ('closing', /<tag(?:"[^"]*"|'[^']*'|[^'">])*?>/)
17479       (/tag/g, block._tag)
17480       ();
17481     
17482     block.paragraph = replace(block.paragraph)
17483       ('hr', block.hr)
17484       ('heading', block.heading)
17485       ('lheading', block.lheading)
17486       ('blockquote', block.blockquote)
17487       ('tag', '<' + block._tag)
17488       ('def', block.def)
17489       ();
17490     
17491     /**
17492      * Normal Block Grammar
17493      */
17494     
17495     block.normal = merge({}, block);
17496     
17497     /**
17498      * GFM Block Grammar
17499      */
17500     
17501     block.gfm = merge({}, block.normal, {
17502       fences: /^ *(`{3,}|~{3,})[ \.]*(\S+)? *\n([\s\S]*?)\s*\1 *(?:\n+|$)/,
17503       paragraph: /^/,
17504       heading: /^ *(#{1,6}) +([^\n]+?) *#* *(?:\n+|$)/
17505     });
17506     
17507     block.gfm.paragraph = replace(block.paragraph)
17508       ('(?!', '(?!'
17509         + block.gfm.fences.source.replace('\\1', '\\2') + '|'
17510         + block.list.source.replace('\\1', '\\3') + '|')
17511       ();
17512     
17513     /**
17514      * GFM + Tables Block Grammar
17515      */
17516     
17517     block.tables = merge({}, block.gfm, {
17518       nptable: /^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*/,
17519       table: /^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)\n*/
17520     });
17521     
17522     /**
17523      * Block Lexer
17524      */
17525     
17526     var Lexer = function (options) {
17527       this.tokens = [];
17528       this.tokens.links = {};
17529       this.options = options || marked.defaults;
17530       this.rules = block.normal;
17531     
17532       if (this.options.gfm) {
17533         if (this.options.tables) {
17534           this.rules = block.tables;
17535         } else {
17536           this.rules = block.gfm;
17537         }
17538       }
17539     }
17540     
17541     /**
17542      * Expose Block Rules
17543      */
17544     
17545     Lexer.rules = block;
17546     
17547     /**
17548      * Static Lex Method
17549      */
17550     
17551     Lexer.lex = function(src, options) {
17552       var lexer = new Lexer(options);
17553       return lexer.lex(src);
17554     };
17555     
17556     /**
17557      * Preprocessing
17558      */
17559     
17560     Lexer.prototype.lex = function(src) {
17561       src = src
17562         .replace(/\r\n|\r/g, '\n')
17563         .replace(/\t/g, '    ')
17564         .replace(/\u00a0/g, ' ')
17565         .replace(/\u2424/g, '\n');
17566     
17567       return this.token(src, true);
17568     };
17569     
17570     /**
17571      * Lexing
17572      */
17573     
17574     Lexer.prototype.token = function(src, top, bq) {
17575       var src = src.replace(/^ +$/gm, '')
17576         , next
17577         , loose
17578         , cap
17579         , bull
17580         , b
17581         , item
17582         , space
17583         , i
17584         , l;
17585     
17586       while (src) {
17587         // newline
17588         if (cap = this.rules.newline.exec(src)) {
17589           src = src.substring(cap[0].length);
17590           if (cap[0].length > 1) {
17591             this.tokens.push({
17592               type: 'space'
17593             });
17594           }
17595         }
17596     
17597         // code
17598         if (cap = this.rules.code.exec(src)) {
17599           src = src.substring(cap[0].length);
17600           cap = cap[0].replace(/^ {4}/gm, '');
17601           this.tokens.push({
17602             type: 'code',
17603             text: !this.options.pedantic
17604               ? cap.replace(/\n+$/, '')
17605               : cap
17606           });
17607           continue;
17608         }
17609     
17610         // fences (gfm)
17611         if (cap = this.rules.fences.exec(src)) {
17612           src = src.substring(cap[0].length);
17613           this.tokens.push({
17614             type: 'code',
17615             lang: cap[2],
17616             text: cap[3] || ''
17617           });
17618           continue;
17619         }
17620     
17621         // heading
17622         if (cap = this.rules.heading.exec(src)) {
17623           src = src.substring(cap[0].length);
17624           this.tokens.push({
17625             type: 'heading',
17626             depth: cap[1].length,
17627             text: cap[2]
17628           });
17629           continue;
17630         }
17631     
17632         // table no leading pipe (gfm)
17633         if (top && (cap = this.rules.nptable.exec(src))) {
17634           src = src.substring(cap[0].length);
17635     
17636           item = {
17637             type: 'table',
17638             header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
17639             align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
17640             cells: cap[3].replace(/\n$/, '').split('\n')
17641           };
17642     
17643           for (i = 0; i < item.align.length; i++) {
17644             if (/^ *-+: *$/.test(item.align[i])) {
17645               item.align[i] = 'right';
17646             } else if (/^ *:-+: *$/.test(item.align[i])) {
17647               item.align[i] = 'center';
17648             } else if (/^ *:-+ *$/.test(item.align[i])) {
17649               item.align[i] = 'left';
17650             } else {
17651               item.align[i] = null;
17652             }
17653           }
17654     
17655           for (i = 0; i < item.cells.length; i++) {
17656             item.cells[i] = item.cells[i].split(/ *\| */);
17657           }
17658     
17659           this.tokens.push(item);
17660     
17661           continue;
17662         }
17663     
17664         // lheading
17665         if (cap = this.rules.lheading.exec(src)) {
17666           src = src.substring(cap[0].length);
17667           this.tokens.push({
17668             type: 'heading',
17669             depth: cap[2] === '=' ? 1 : 2,
17670             text: cap[1]
17671           });
17672           continue;
17673         }
17674     
17675         // hr
17676         if (cap = this.rules.hr.exec(src)) {
17677           src = src.substring(cap[0].length);
17678           this.tokens.push({
17679             type: 'hr'
17680           });
17681           continue;
17682         }
17683     
17684         // blockquote
17685         if (cap = this.rules.blockquote.exec(src)) {
17686           src = src.substring(cap[0].length);
17687     
17688           this.tokens.push({
17689             type: 'blockquote_start'
17690           });
17691     
17692           cap = cap[0].replace(/^ *> ?/gm, '');
17693     
17694           // Pass `top` to keep the current
17695           // "toplevel" state. This is exactly
17696           // how markdown.pl works.
17697           this.token(cap, top, true);
17698     
17699           this.tokens.push({
17700             type: 'blockquote_end'
17701           });
17702     
17703           continue;
17704         }
17705     
17706         // list
17707         if (cap = this.rules.list.exec(src)) {
17708           src = src.substring(cap[0].length);
17709           bull = cap[2];
17710     
17711           this.tokens.push({
17712             type: 'list_start',
17713             ordered: bull.length > 1
17714           });
17715     
17716           // Get each top-level item.
17717           cap = cap[0].match(this.rules.item);
17718     
17719           next = false;
17720           l = cap.length;
17721           i = 0;
17722     
17723           for (; i < l; i++) {
17724             item = cap[i];
17725     
17726             // Remove the list item's bullet
17727             // so it is seen as the next token.
17728             space = item.length;
17729             item = item.replace(/^ *([*+-]|\d+\.) +/, '');
17730     
17731             // Outdent whatever the
17732             // list item contains. Hacky.
17733             if (~item.indexOf('\n ')) {
17734               space -= item.length;
17735               item = !this.options.pedantic
17736                 ? item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '')
17737                 : item.replace(/^ {1,4}/gm, '');
17738             }
17739     
17740             // Determine whether the next list item belongs here.
17741             // Backpedal if it does not belong in this list.
17742             if (this.options.smartLists && i !== l - 1) {
17743               b = block.bullet.exec(cap[i + 1])[0];
17744               if (bull !== b && !(bull.length > 1 && b.length > 1)) {
17745                 src = cap.slice(i + 1).join('\n') + src;
17746                 i = l - 1;
17747               }
17748             }
17749     
17750             // Determine whether item is loose or not.
17751             // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/
17752             // for discount behavior.
17753             loose = next || /\n\n(?!\s*$)/.test(item);
17754             if (i !== l - 1) {
17755               next = item.charAt(item.length - 1) === '\n';
17756               if (!loose) { loose = next; }
17757             }
17758     
17759             this.tokens.push({
17760               type: loose
17761                 ? 'loose_item_start'
17762                 : 'list_item_start'
17763             });
17764     
17765             // Recurse.
17766             this.token(item, false, bq);
17767     
17768             this.tokens.push({
17769               type: 'list_item_end'
17770             });
17771           }
17772     
17773           this.tokens.push({
17774             type: 'list_end'
17775           });
17776     
17777           continue;
17778         }
17779     
17780         // html
17781         if (cap = this.rules.html.exec(src)) {
17782           src = src.substring(cap[0].length);
17783           this.tokens.push({
17784             type: this.options.sanitize
17785               ? 'paragraph'
17786               : 'html',
17787             pre: !this.options.sanitizer
17788               && (cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style'),
17789             text: cap[0]
17790           });
17791           continue;
17792         }
17793     
17794         // def
17795         if ((!bq && top) && (cap = this.rules.def.exec(src))) {
17796           src = src.substring(cap[0].length);
17797           this.tokens.links[cap[1].toLowerCase()] = {
17798             href: cap[2],
17799             title: cap[3]
17800           };
17801           continue;
17802         }
17803     
17804         // table (gfm)
17805         if (top && (cap = this.rules.table.exec(src))) {
17806           src = src.substring(cap[0].length);
17807     
17808           item = {
17809             type: 'table',
17810             header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
17811             align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
17812             cells: cap[3].replace(/(?: *\| *)?\n$/, '').split('\n')
17813           };
17814     
17815           for (i = 0; i < item.align.length; i++) {
17816             if (/^ *-+: *$/.test(item.align[i])) {
17817               item.align[i] = 'right';
17818             } else if (/^ *:-+: *$/.test(item.align[i])) {
17819               item.align[i] = 'center';
17820             } else if (/^ *:-+ *$/.test(item.align[i])) {
17821               item.align[i] = 'left';
17822             } else {
17823               item.align[i] = null;
17824             }
17825           }
17826     
17827           for (i = 0; i < item.cells.length; i++) {
17828             item.cells[i] = item.cells[i]
17829               .replace(/^ *\| *| *\| *$/g, '')
17830               .split(/ *\| */);
17831           }
17832     
17833           this.tokens.push(item);
17834     
17835           continue;
17836         }
17837     
17838         // top-level paragraph
17839         if (top && (cap = this.rules.paragraph.exec(src))) {
17840           src = src.substring(cap[0].length);
17841           this.tokens.push({
17842             type: 'paragraph',
17843             text: cap[1].charAt(cap[1].length - 1) === '\n'
17844               ? cap[1].slice(0, -1)
17845               : cap[1]
17846           });
17847           continue;
17848         }
17849     
17850         // text
17851         if (cap = this.rules.text.exec(src)) {
17852           // Top-level should never reach here.
17853           src = src.substring(cap[0].length);
17854           this.tokens.push({
17855             type: 'text',
17856             text: cap[0]
17857           });
17858           continue;
17859         }
17860     
17861         if (src) {
17862           throw new
17863             Error('Infinite loop on byte: ' + src.charCodeAt(0));
17864         }
17865       }
17866     
17867       return this.tokens;
17868     };
17869     
17870     /**
17871      * Inline-Level Grammar
17872      */
17873     
17874     var inline = {
17875       escape: /^\\([\\`*{}\[\]()#+\-.!_>])/,
17876       autolink: /^<([^ >]+(@|:\/)[^ >]+)>/,
17877       url: noop,
17878       tag: /^<!--[\s\S]*?-->|^<\/?\w+(?:"[^"]*"|'[^']*'|[^'">])*?>/,
17879       link: /^!?\[(inside)\]\(href\)/,
17880       reflink: /^!?\[(inside)\]\s*\[([^\]]*)\]/,
17881       nolink: /^!?\[((?:\[[^\]]*\]|[^\[\]])*)\]/,
17882       strong: /^__([\s\S]+?)__(?!_)|^\*\*([\s\S]+?)\*\*(?!\*)/,
17883       em: /^\b_((?:[^_]|__)+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/,
17884       code: /^(`+)\s*([\s\S]*?[^`])\s*\1(?!`)/,
17885       br: /^ {2,}\n(?!\s*$)/,
17886       del: noop,
17887       text: /^[\s\S]+?(?=[\\<!\[_*`]| {2,}\n|$)/
17888     };
17889     
17890     inline._inside = /(?:\[[^\]]*\]|[^\[\]]|\](?=[^\[]*\]))*/;
17891     inline._href = /\s*<?([\s\S]*?)>?(?:\s+['"]([\s\S]*?)['"])?\s*/;
17892     
17893     inline.link = replace(inline.link)
17894       ('inside', inline._inside)
17895       ('href', inline._href)
17896       ();
17897     
17898     inline.reflink = replace(inline.reflink)
17899       ('inside', inline._inside)
17900       ();
17901     
17902     /**
17903      * Normal Inline Grammar
17904      */
17905     
17906     inline.normal = merge({}, inline);
17907     
17908     /**
17909      * Pedantic Inline Grammar
17910      */
17911     
17912     inline.pedantic = merge({}, inline.normal, {
17913       strong: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,
17914       em: /^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/
17915     });
17916     
17917     /**
17918      * GFM Inline Grammar
17919      */
17920     
17921     inline.gfm = merge({}, inline.normal, {
17922       escape: replace(inline.escape)('])', '~|])')(),
17923       url: /^(https?:\/\/[^\s<]+[^<.,:;"')\]\s])/,
17924       del: /^~~(?=\S)([\s\S]*?\S)~~/,
17925       text: replace(inline.text)
17926         (']|', '~]|')
17927         ('|', '|https?://|')
17928         ()
17929     });
17930     
17931     /**
17932      * GFM + Line Breaks Inline Grammar
17933      */
17934     
17935     inline.breaks = merge({}, inline.gfm, {
17936       br: replace(inline.br)('{2,}', '*')(),
17937       text: replace(inline.gfm.text)('{2,}', '*')()
17938     });
17939     
17940     /**
17941      * Inline Lexer & Compiler
17942      */
17943     
17944     var InlineLexer  = function (links, options) {
17945       this.options = options || marked.defaults;
17946       this.links = links;
17947       this.rules = inline.normal;
17948       this.renderer = this.options.renderer || new Renderer;
17949       this.renderer.options = this.options;
17950     
17951       if (!this.links) {
17952         throw new
17953           Error('Tokens array requires a `links` property.');
17954       }
17955     
17956       if (this.options.gfm) {
17957         if (this.options.breaks) {
17958           this.rules = inline.breaks;
17959         } else {
17960           this.rules = inline.gfm;
17961         }
17962       } else if (this.options.pedantic) {
17963         this.rules = inline.pedantic;
17964       }
17965     }
17966     
17967     /**
17968      * Expose Inline Rules
17969      */
17970     
17971     InlineLexer.rules = inline;
17972     
17973     /**
17974      * Static Lexing/Compiling Method
17975      */
17976     
17977     InlineLexer.output = function(src, links, options) {
17978       var inline = new InlineLexer(links, options);
17979       return inline.output(src);
17980     };
17981     
17982     /**
17983      * Lexing/Compiling
17984      */
17985     
17986     InlineLexer.prototype.output = function(src) {
17987       var out = ''
17988         , link
17989         , text
17990         , href
17991         , cap;
17992     
17993       while (src) {
17994         // escape
17995         if (cap = this.rules.escape.exec(src)) {
17996           src = src.substring(cap[0].length);
17997           out += cap[1];
17998           continue;
17999         }
18000     
18001         // autolink
18002         if (cap = this.rules.autolink.exec(src)) {
18003           src = src.substring(cap[0].length);
18004           if (cap[2] === '@') {
18005             text = cap[1].charAt(6) === ':'
18006               ? this.mangle(cap[1].substring(7))
18007               : this.mangle(cap[1]);
18008             href = this.mangle('mailto:') + text;
18009           } else {
18010             text = escape(cap[1]);
18011             href = text;
18012           }
18013           out += this.renderer.link(href, null, text);
18014           continue;
18015         }
18016     
18017         // url (gfm)
18018         if (!this.inLink && (cap = this.rules.url.exec(src))) {
18019           src = src.substring(cap[0].length);
18020           text = escape(cap[1]);
18021           href = text;
18022           out += this.renderer.link(href, null, text);
18023           continue;
18024         }
18025     
18026         // tag
18027         if (cap = this.rules.tag.exec(src)) {
18028           if (!this.inLink && /^<a /i.test(cap[0])) {
18029             this.inLink = true;
18030           } else if (this.inLink && /^<\/a>/i.test(cap[0])) {
18031             this.inLink = false;
18032           }
18033           src = src.substring(cap[0].length);
18034           out += this.options.sanitize
18035             ? this.options.sanitizer
18036               ? this.options.sanitizer(cap[0])
18037               : escape(cap[0])
18038             : cap[0];
18039           continue;
18040         }
18041     
18042         // link
18043         if (cap = this.rules.link.exec(src)) {
18044           src = src.substring(cap[0].length);
18045           this.inLink = true;
18046           out += this.outputLink(cap, {
18047             href: cap[2],
18048             title: cap[3]
18049           });
18050           this.inLink = false;
18051           continue;
18052         }
18053     
18054         // reflink, nolink
18055         if ((cap = this.rules.reflink.exec(src))
18056             || (cap = this.rules.nolink.exec(src))) {
18057           src = src.substring(cap[0].length);
18058           link = (cap[2] || cap[1]).replace(/\s+/g, ' ');
18059           link = this.links[link.toLowerCase()];
18060           if (!link || !link.href) {
18061             out += cap[0].charAt(0);
18062             src = cap[0].substring(1) + src;
18063             continue;
18064           }
18065           this.inLink = true;
18066           out += this.outputLink(cap, link);
18067           this.inLink = false;
18068           continue;
18069         }
18070     
18071         // strong
18072         if (cap = this.rules.strong.exec(src)) {
18073           src = src.substring(cap[0].length);
18074           out += this.renderer.strong(this.output(cap[2] || cap[1]));
18075           continue;
18076         }
18077     
18078         // em
18079         if (cap = this.rules.em.exec(src)) {
18080           src = src.substring(cap[0].length);
18081           out += this.renderer.em(this.output(cap[2] || cap[1]));
18082           continue;
18083         }
18084     
18085         // code
18086         if (cap = this.rules.code.exec(src)) {
18087           src = src.substring(cap[0].length);
18088           out += this.renderer.codespan(escape(cap[2], true));
18089           continue;
18090         }
18091     
18092         // br
18093         if (cap = this.rules.br.exec(src)) {
18094           src = src.substring(cap[0].length);
18095           out += this.renderer.br();
18096           continue;
18097         }
18098     
18099         // del (gfm)
18100         if (cap = this.rules.del.exec(src)) {
18101           src = src.substring(cap[0].length);
18102           out += this.renderer.del(this.output(cap[1]));
18103           continue;
18104         }
18105     
18106         // text
18107         if (cap = this.rules.text.exec(src)) {
18108           src = src.substring(cap[0].length);
18109           out += this.renderer.text(escape(this.smartypants(cap[0])));
18110           continue;
18111         }
18112     
18113         if (src) {
18114           throw new
18115             Error('Infinite loop on byte: ' + src.charCodeAt(0));
18116         }
18117       }
18118     
18119       return out;
18120     };
18121     
18122     /**
18123      * Compile Link
18124      */
18125     
18126     InlineLexer.prototype.outputLink = function(cap, link) {
18127       var href = escape(link.href)
18128         , title = link.title ? escape(link.title) : null;
18129     
18130       return cap[0].charAt(0) !== '!'
18131         ? this.renderer.link(href, title, this.output(cap[1]))
18132         : this.renderer.image(href, title, escape(cap[1]));
18133     };
18134     
18135     /**
18136      * Smartypants Transformations
18137      */
18138     
18139     InlineLexer.prototype.smartypants = function(text) {
18140       if (!this.options.smartypants)  { return text; }
18141       return text
18142         // em-dashes
18143         .replace(/---/g, '\u2014')
18144         // en-dashes
18145         .replace(/--/g, '\u2013')
18146         // opening singles
18147         .replace(/(^|[-\u2014/(\[{"\s])'/g, '$1\u2018')
18148         // closing singles & apostrophes
18149         .replace(/'/g, '\u2019')
18150         // opening doubles
18151         .replace(/(^|[-\u2014/(\[{\u2018\s])"/g, '$1\u201c')
18152         // closing doubles
18153         .replace(/"/g, '\u201d')
18154         // ellipses
18155         .replace(/\.{3}/g, '\u2026');
18156     };
18157     
18158     /**
18159      * Mangle Links
18160      */
18161     
18162     InlineLexer.prototype.mangle = function(text) {
18163       if (!this.options.mangle) { return text; }
18164       var out = ''
18165         , l = text.length
18166         , i = 0
18167         , ch;
18168     
18169       for (; i < l; i++) {
18170         ch = text.charCodeAt(i);
18171         if (Math.random() > 0.5) {
18172           ch = 'x' + ch.toString(16);
18173         }
18174         out += '&#' + ch + ';';
18175       }
18176     
18177       return out;
18178     };
18179     
18180     /**
18181      * Renderer
18182      */
18183     
18184      /**
18185          * eval:var:Renderer
18186     */
18187     
18188     var Renderer   = function (options) {
18189       this.options = options || {};
18190     }
18191     
18192     Renderer.prototype.code = function(code, lang, escaped) {
18193       if (this.options.highlight) {
18194         var out = this.options.highlight(code, lang);
18195         if (out != null && out !== code) {
18196           escaped = true;
18197           code = out;
18198         }
18199       } else {
18200             // hack!!! - it's already escapeD?
18201             escaped = true;
18202       }
18203     
18204       if (!lang) {
18205         return '<pre><code>'
18206           + (escaped ? code : escape(code, true))
18207           + '\n</code></pre>';
18208       }
18209     
18210       return '<pre><code class="'
18211         + this.options.langPrefix
18212         + escape(lang, true)
18213         + '">'
18214         + (escaped ? code : escape(code, true))
18215         + '\n</code></pre>\n';
18216     };
18217     
18218     Renderer.prototype.blockquote = function(quote) {
18219       return '<blockquote>\n' + quote + '</blockquote>\n';
18220     };
18221     
18222     Renderer.prototype.html = function(html) {
18223       return html;
18224     };
18225     
18226     Renderer.prototype.heading = function(text, level, raw) {
18227       return '<h'
18228         + level
18229         + ' id="'
18230         + this.options.headerPrefix
18231         + raw.toLowerCase().replace(/[^\w]+/g, '-')
18232         + '">'
18233         + text
18234         + '</h'
18235         + level
18236         + '>\n';
18237     };
18238     
18239     Renderer.prototype.hr = function() {
18240       return this.options.xhtml ? '<hr/>\n' : '<hr>\n';
18241     };
18242     
18243     Renderer.prototype.list = function(body, ordered) {
18244       var type = ordered ? 'ol' : 'ul';
18245       return '<' + type + '>\n' + body + '</' + type + '>\n';
18246     };
18247     
18248     Renderer.prototype.listitem = function(text) {
18249       return '<li>' + text + '</li>\n';
18250     };
18251     
18252     Renderer.prototype.paragraph = function(text) {
18253       return '<p>' + text + '</p>\n';
18254     };
18255     
18256     Renderer.prototype.table = function(header, body) {
18257       return '<table class="table table-striped">\n'
18258         + '<thead>\n'
18259         + header
18260         + '</thead>\n'
18261         + '<tbody>\n'
18262         + body
18263         + '</tbody>\n'
18264         + '</table>\n';
18265     };
18266     
18267     Renderer.prototype.tablerow = function(content) {
18268       return '<tr>\n' + content + '</tr>\n';
18269     };
18270     
18271     Renderer.prototype.tablecell = function(content, flags) {
18272       var type = flags.header ? 'th' : 'td';
18273       var tag = flags.align
18274         ? '<' + type + ' style="text-align:' + flags.align + '">'
18275         : '<' + type + '>';
18276       return tag + content + '</' + type + '>\n';
18277     };
18278     
18279     // span level renderer
18280     Renderer.prototype.strong = function(text) {
18281       return '<strong>' + text + '</strong>';
18282     };
18283     
18284     Renderer.prototype.em = function(text) {
18285       return '<em>' + text + '</em>';
18286     };
18287     
18288     Renderer.prototype.codespan = function(text) {
18289       return '<code>' + text + '</code>';
18290     };
18291     
18292     Renderer.prototype.br = function() {
18293       return this.options.xhtml ? '<br/>' : '<br>';
18294     };
18295     
18296     Renderer.prototype.del = function(text) {
18297       return '<del>' + text + '</del>';
18298     };
18299     
18300     Renderer.prototype.link = function(href, title, text) {
18301       if (this.options.sanitize) {
18302         try {
18303           var prot = decodeURIComponent(unescape(href))
18304             .replace(/[^\w:]/g, '')
18305             .toLowerCase();
18306         } catch (e) {
18307           return '';
18308         }
18309         if (prot.indexOf('javascript:') === 0 || prot.indexOf('vbscript:') === 0) {
18310           return '';
18311         }
18312       }
18313       var out = '<a href="' + href + '"';
18314       if (title) {
18315         out += ' title="' + title + '"';
18316       }
18317       out += '>' + text + '</a>';
18318       return out;
18319     };
18320     
18321     Renderer.prototype.image = function(href, title, text) {
18322       var out = '<img src="' + href + '" alt="' + text + '"';
18323       if (title) {
18324         out += ' title="' + title + '"';
18325       }
18326       out += this.options.xhtml ? '/>' : '>';
18327       return out;
18328     };
18329     
18330     Renderer.prototype.text = function(text) {
18331       return text;
18332     };
18333     
18334     /**
18335      * Parsing & Compiling
18336      */
18337          /**
18338          * eval:var:Parser
18339     */
18340     
18341     var Parser= function (options) {
18342       this.tokens = [];
18343       this.token = null;
18344       this.options = options || marked.defaults;
18345       this.options.renderer = this.options.renderer || new Renderer;
18346       this.renderer = this.options.renderer;
18347       this.renderer.options = this.options;
18348     }
18349     
18350     /**
18351      * Static Parse Method
18352      */
18353     
18354     Parser.parse = function(src, options, renderer) {
18355       var parser = new Parser(options, renderer);
18356       return parser.parse(src);
18357     };
18358     
18359     /**
18360      * Parse Loop
18361      */
18362     
18363     Parser.prototype.parse = function(src) {
18364       this.inline = new InlineLexer(src.links, this.options, this.renderer);
18365       this.tokens = src.reverse();
18366     
18367       var out = '';
18368       while (this.next()) {
18369         out += this.tok();
18370       }
18371     
18372       return out;
18373     };
18374     
18375     /**
18376      * Next Token
18377      */
18378     
18379     Parser.prototype.next = function() {
18380       return this.token = this.tokens.pop();
18381     };
18382     
18383     /**
18384      * Preview Next Token
18385      */
18386     
18387     Parser.prototype.peek = function() {
18388       return this.tokens[this.tokens.length - 1] || 0;
18389     };
18390     
18391     /**
18392      * Parse Text Tokens
18393      */
18394     
18395     Parser.prototype.parseText = function() {
18396       var body = this.token.text;
18397     
18398       while (this.peek().type === 'text') {
18399         body += '\n' + this.next().text;
18400       }
18401     
18402       return this.inline.output(body);
18403     };
18404     
18405     /**
18406      * Parse Current Token
18407      */
18408     
18409     Parser.prototype.tok = function() {
18410       switch (this.token.type) {
18411         case 'space': {
18412           return '';
18413         }
18414         case 'hr': {
18415           return this.renderer.hr();
18416         }
18417         case 'heading': {
18418           return this.renderer.heading(
18419             this.inline.output(this.token.text),
18420             this.token.depth,
18421             this.token.text);
18422         }
18423         case 'code': {
18424           return this.renderer.code(this.token.text,
18425             this.token.lang,
18426             this.token.escaped);
18427         }
18428         case 'table': {
18429           var header = ''
18430             , body = ''
18431             , i
18432             , row
18433             , cell
18434             , flags
18435             , j;
18436     
18437           // header
18438           cell = '';
18439           for (i = 0; i < this.token.header.length; i++) {
18440             flags = { header: true, align: this.token.align[i] };
18441             cell += this.renderer.tablecell(
18442               this.inline.output(this.token.header[i]),
18443               { header: true, align: this.token.align[i] }
18444             );
18445           }
18446           header += this.renderer.tablerow(cell);
18447     
18448           for (i = 0; i < this.token.cells.length; i++) {
18449             row = this.token.cells[i];
18450     
18451             cell = '';
18452             for (j = 0; j < row.length; j++) {
18453               cell += this.renderer.tablecell(
18454                 this.inline.output(row[j]),
18455                 { header: false, align: this.token.align[j] }
18456               );
18457             }
18458     
18459             body += this.renderer.tablerow(cell);
18460           }
18461           return this.renderer.table(header, body);
18462         }
18463         case 'blockquote_start': {
18464           var body = '';
18465     
18466           while (this.next().type !== 'blockquote_end') {
18467             body += this.tok();
18468           }
18469     
18470           return this.renderer.blockquote(body);
18471         }
18472         case 'list_start': {
18473           var body = ''
18474             , ordered = this.token.ordered;
18475     
18476           while (this.next().type !== 'list_end') {
18477             body += this.tok();
18478           }
18479     
18480           return this.renderer.list(body, ordered);
18481         }
18482         case 'list_item_start': {
18483           var body = '';
18484     
18485           while (this.next().type !== 'list_item_end') {
18486             body += this.token.type === 'text'
18487               ? this.parseText()
18488               : this.tok();
18489           }
18490     
18491           return this.renderer.listitem(body);
18492         }
18493         case 'loose_item_start': {
18494           var body = '';
18495     
18496           while (this.next().type !== 'list_item_end') {
18497             body += this.tok();
18498           }
18499     
18500           return this.renderer.listitem(body);
18501         }
18502         case 'html': {
18503           var html = !this.token.pre && !this.options.pedantic
18504             ? this.inline.output(this.token.text)
18505             : this.token.text;
18506           return this.renderer.html(html);
18507         }
18508         case 'paragraph': {
18509           return this.renderer.paragraph(this.inline.output(this.token.text));
18510         }
18511         case 'text': {
18512           return this.renderer.paragraph(this.parseText());
18513         }
18514       }
18515     };
18516   
18517     
18518     /**
18519      * Marked
18520      */
18521          /**
18522          * eval:var:marked
18523     */
18524     var marked = function (src, opt, callback) {
18525       if (callback || typeof opt === 'function') {
18526         if (!callback) {
18527           callback = opt;
18528           opt = null;
18529         }
18530     
18531         opt = merge({}, marked.defaults, opt || {});
18532     
18533         var highlight = opt.highlight
18534           , tokens
18535           , pending
18536           , i = 0;
18537     
18538         try {
18539           tokens = Lexer.lex(src, opt)
18540         } catch (e) {
18541           return callback(e);
18542         }
18543     
18544         pending = tokens.length;
18545          /**
18546          * eval:var:done
18547     */
18548         var done = function(err) {
18549           if (err) {
18550             opt.highlight = highlight;
18551             return callback(err);
18552           }
18553     
18554           var out;
18555     
18556           try {
18557             out = Parser.parse(tokens, opt);
18558           } catch (e) {
18559             err = e;
18560           }
18561     
18562           opt.highlight = highlight;
18563     
18564           return err
18565             ? callback(err)
18566             : callback(null, out);
18567         };
18568     
18569         if (!highlight || highlight.length < 3) {
18570           return done();
18571         }
18572     
18573         delete opt.highlight;
18574     
18575         if (!pending) { return done(); }
18576     
18577         for (; i < tokens.length; i++) {
18578           (function(token) {
18579             if (token.type !== 'code') {
18580               return --pending || done();
18581             }
18582             return highlight(token.text, token.lang, function(err, code) {
18583               if (err) { return done(err); }
18584               if (code == null || code === token.text) {
18585                 return --pending || done();
18586               }
18587               token.text = code;
18588               token.escaped = true;
18589               --pending || done();
18590             });
18591           })(tokens[i]);
18592         }
18593     
18594         return;
18595       }
18596       try {
18597         if (opt) { opt = merge({}, marked.defaults, opt); }
18598         return Parser.parse(Lexer.lex(src, opt), opt);
18599       } catch (e) {
18600         e.message += '\nPlease report this to https://github.com/chjj/marked.';
18601         if ((opt || marked.defaults).silent) {
18602           return '<p>An error occured:</p><pre>'
18603             + escape(e.message + '', true)
18604             + '</pre>';
18605         }
18606         throw e;
18607       }
18608     }
18609     
18610     /**
18611      * Options
18612      */
18613     
18614     marked.options =
18615     marked.setOptions = function(opt) {
18616       merge(marked.defaults, opt);
18617       return marked;
18618     };
18619     
18620     marked.defaults = {
18621       gfm: true,
18622       tables: true,
18623       breaks: false,
18624       pedantic: false,
18625       sanitize: false,
18626       sanitizer: null,
18627       mangle: true,
18628       smartLists: false,
18629       silent: false,
18630       highlight: null,
18631       langPrefix: 'lang-',
18632       smartypants: false,
18633       headerPrefix: '',
18634       renderer: new Renderer,
18635       xhtml: false
18636     };
18637     
18638     /**
18639      * Expose
18640      */
18641     
18642     marked.Parser = Parser;
18643     marked.parser = Parser.parse;
18644     
18645     marked.Renderer = Renderer;
18646     
18647     marked.Lexer = Lexer;
18648     marked.lexer = Lexer.lex;
18649     
18650     marked.InlineLexer = InlineLexer;
18651     marked.inlineLexer = InlineLexer.output;
18652     
18653     marked.parse = marked;
18654     
18655     Roo.Markdown.marked = marked;
18656
18657 })();/*
18658  * Based on:
18659  * Ext JS Library 1.1.1
18660  * Copyright(c) 2006-2007, Ext JS, LLC.
18661  *
18662  * Originally Released Under LGPL - original licence link has changed is not relivant.
18663  *
18664  * Fork - LGPL
18665  * <script type="text/javascript">
18666  */
18667
18668
18669
18670 /*
18671  * These classes are derivatives of the similarly named classes in the YUI Library.
18672  * The original license:
18673  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
18674  * Code licensed under the BSD License:
18675  * http://developer.yahoo.net/yui/license.txt
18676  */
18677
18678 (function() {
18679
18680 var Event=Roo.EventManager;
18681 var Dom=Roo.lib.Dom;
18682
18683 /**
18684  * @class Roo.dd.DragDrop
18685  * @extends Roo.util.Observable
18686  * Defines the interface and base operation of items that that can be
18687  * dragged or can be drop targets.  It was designed to be extended, overriding
18688  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
18689  * Up to three html elements can be associated with a DragDrop instance:
18690  * <ul>
18691  * <li>linked element: the element that is passed into the constructor.
18692  * This is the element which defines the boundaries for interaction with
18693  * other DragDrop objects.</li>
18694  * <li>handle element(s): The drag operation only occurs if the element that
18695  * was clicked matches a handle element.  By default this is the linked
18696  * element, but there are times that you will want only a portion of the
18697  * linked element to initiate the drag operation, and the setHandleElId()
18698  * method provides a way to define this.</li>
18699  * <li>drag element: this represents the element that would be moved along
18700  * with the cursor during a drag operation.  By default, this is the linked
18701  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
18702  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
18703  * </li>
18704  * </ul>
18705  * This class should not be instantiated until the onload event to ensure that
18706  * the associated elements are available.
18707  * The following would define a DragDrop obj that would interact with any
18708  * other DragDrop obj in the "group1" group:
18709  * <pre>
18710  *  dd = new Roo.dd.DragDrop("div1", "group1");
18711  * </pre>
18712  * Since none of the event handlers have been implemented, nothing would
18713  * actually happen if you were to run the code above.  Normally you would
18714  * override this class or one of the default implementations, but you can
18715  * also override the methods you want on an instance of the class...
18716  * <pre>
18717  *  dd.onDragDrop = function(e, id) {
18718  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
18719  *  }
18720  * </pre>
18721  * @constructor
18722  * @param {String} id of the element that is linked to this instance
18723  * @param {String} sGroup the group of related DragDrop objects
18724  * @param {object} config an object containing configurable attributes
18725  *                Valid properties for DragDrop:
18726  *                    padding, isTarget, maintainOffset, primaryButtonOnly
18727  */
18728 Roo.dd.DragDrop = function(id, sGroup, config) {
18729     if (id) {
18730         this.init(id, sGroup, config);
18731     }
18732     
18733 };
18734
18735 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
18736
18737     /**
18738      * The id of the element associated with this object.  This is what we
18739      * refer to as the "linked element" because the size and position of
18740      * this element is used to determine when the drag and drop objects have
18741      * interacted.
18742      * @property id
18743      * @type String
18744      */
18745     id: null,
18746
18747     /**
18748      * Configuration attributes passed into the constructor
18749      * @property config
18750      * @type object
18751      */
18752     config: null,
18753
18754     /**
18755      * The id of the element that will be dragged.  By default this is same
18756      * as the linked element , but could be changed to another element. Ex:
18757      * Roo.dd.DDProxy
18758      * @property dragElId
18759      * @type String
18760      * @private
18761      */
18762     dragElId: null,
18763
18764     /**
18765      * the id of the element that initiates the drag operation.  By default
18766      * this is the linked element, but could be changed to be a child of this
18767      * element.  This lets us do things like only starting the drag when the
18768      * header element within the linked html element is clicked.
18769      * @property handleElId
18770      * @type String
18771      * @private
18772      */
18773     handleElId: null,
18774
18775     /**
18776      * An associative array of HTML tags that will be ignored if clicked.
18777      * @property invalidHandleTypes
18778      * @type {string: string}
18779      */
18780     invalidHandleTypes: null,
18781
18782     /**
18783      * An associative array of ids for elements that will be ignored if clicked
18784      * @property invalidHandleIds
18785      * @type {string: string}
18786      */
18787     invalidHandleIds: null,
18788
18789     /**
18790      * An indexted array of css class names for elements that will be ignored
18791      * if clicked.
18792      * @property invalidHandleClasses
18793      * @type string[]
18794      */
18795     invalidHandleClasses: null,
18796
18797     /**
18798      * The linked element's absolute X position at the time the drag was
18799      * started
18800      * @property startPageX
18801      * @type int
18802      * @private
18803      */
18804     startPageX: 0,
18805
18806     /**
18807      * The linked element's absolute X position at the time the drag was
18808      * started
18809      * @property startPageY
18810      * @type int
18811      * @private
18812      */
18813     startPageY: 0,
18814
18815     /**
18816      * The group defines a logical collection of DragDrop objects that are
18817      * related.  Instances only get events when interacting with other
18818      * DragDrop object in the same group.  This lets us define multiple
18819      * groups using a single DragDrop subclass if we want.
18820      * @property groups
18821      * @type {string: string}
18822      */
18823     groups: null,
18824
18825     /**
18826      * Individual drag/drop instances can be locked.  This will prevent
18827      * onmousedown start drag.
18828      * @property locked
18829      * @type boolean
18830      * @private
18831      */
18832     locked: false,
18833
18834     /**
18835      * Lock this instance
18836      * @method lock
18837      */
18838     lock: function() { this.locked = true; },
18839
18840     /**
18841      * Unlock this instace
18842      * @method unlock
18843      */
18844     unlock: function() { this.locked = false; },
18845
18846     /**
18847      * By default, all insances can be a drop target.  This can be disabled by
18848      * setting isTarget to false.
18849      * @method isTarget
18850      * @type boolean
18851      */
18852     isTarget: true,
18853
18854     /**
18855      * The padding configured for this drag and drop object for calculating
18856      * the drop zone intersection with this object.
18857      * @method padding
18858      * @type int[]
18859      */
18860     padding: null,
18861
18862     /**
18863      * Cached reference to the linked element
18864      * @property _domRef
18865      * @private
18866      */
18867     _domRef: null,
18868
18869     /**
18870      * Internal typeof flag
18871      * @property __ygDragDrop
18872      * @private
18873      */
18874     __ygDragDrop: true,
18875
18876     /**
18877      * Set to true when horizontal contraints are applied
18878      * @property constrainX
18879      * @type boolean
18880      * @private
18881      */
18882     constrainX: false,
18883
18884     /**
18885      * Set to true when vertical contraints are applied
18886      * @property constrainY
18887      * @type boolean
18888      * @private
18889      */
18890     constrainY: false,
18891
18892     /**
18893      * The left constraint
18894      * @property minX
18895      * @type int
18896      * @private
18897      */
18898     minX: 0,
18899
18900     /**
18901      * The right constraint
18902      * @property maxX
18903      * @type int
18904      * @private
18905      */
18906     maxX: 0,
18907
18908     /**
18909      * The up constraint
18910      * @property minY
18911      * @type int
18912      * @type int
18913      * @private
18914      */
18915     minY: 0,
18916
18917     /**
18918      * The down constraint
18919      * @property maxY
18920      * @type int
18921      * @private
18922      */
18923     maxY: 0,
18924
18925     /**
18926      * Maintain offsets when we resetconstraints.  Set to true when you want
18927      * the position of the element relative to its parent to stay the same
18928      * when the page changes
18929      *
18930      * @property maintainOffset
18931      * @type boolean
18932      */
18933     maintainOffset: false,
18934
18935     /**
18936      * Array of pixel locations the element will snap to if we specified a
18937      * horizontal graduation/interval.  This array is generated automatically
18938      * when you define a tick interval.
18939      * @property xTicks
18940      * @type int[]
18941      */
18942     xTicks: null,
18943
18944     /**
18945      * Array of pixel locations the element will snap to if we specified a
18946      * vertical graduation/interval.  This array is generated automatically
18947      * when you define a tick interval.
18948      * @property yTicks
18949      * @type int[]
18950      */
18951     yTicks: null,
18952
18953     /**
18954      * By default the drag and drop instance will only respond to the primary
18955      * button click (left button for a right-handed mouse).  Set to true to
18956      * allow drag and drop to start with any mouse click that is propogated
18957      * by the browser
18958      * @property primaryButtonOnly
18959      * @type boolean
18960      */
18961     primaryButtonOnly: true,
18962
18963     /**
18964      * The availabe property is false until the linked dom element is accessible.
18965      * @property available
18966      * @type boolean
18967      */
18968     available: false,
18969
18970     /**
18971      * By default, drags can only be initiated if the mousedown occurs in the
18972      * region the linked element is.  This is done in part to work around a
18973      * bug in some browsers that mis-report the mousedown if the previous
18974      * mouseup happened outside of the window.  This property is set to true
18975      * if outer handles are defined.
18976      *
18977      * @property hasOuterHandles
18978      * @type boolean
18979      * @default false
18980      */
18981     hasOuterHandles: false,
18982
18983     /**
18984      * Code that executes immediately before the startDrag event
18985      * @method b4StartDrag
18986      * @private
18987      */
18988     b4StartDrag: function(x, y) { },
18989
18990     /**
18991      * Abstract method called after a drag/drop object is clicked
18992      * and the drag or mousedown time thresholds have beeen met.
18993      * @method startDrag
18994      * @param {int} X click location
18995      * @param {int} Y click location
18996      */
18997     startDrag: function(x, y) { /* override this */ },
18998
18999     /**
19000      * Code that executes immediately before the onDrag event
19001      * @method b4Drag
19002      * @private
19003      */
19004     b4Drag: function(e) { },
19005
19006     /**
19007      * Abstract method called during the onMouseMove event while dragging an
19008      * object.
19009      * @method onDrag
19010      * @param {Event} e the mousemove event
19011      */
19012     onDrag: function(e) { /* override this */ },
19013
19014     /**
19015      * Abstract method called when this element fist begins hovering over
19016      * another DragDrop obj
19017      * @method onDragEnter
19018      * @param {Event} e the mousemove event
19019      * @param {String|DragDrop[]} id In POINT mode, the element
19020      * id this is hovering over.  In INTERSECT mode, an array of one or more
19021      * dragdrop items being hovered over.
19022      */
19023     onDragEnter: function(e, id) { /* override this */ },
19024
19025     /**
19026      * Code that executes immediately before the onDragOver event
19027      * @method b4DragOver
19028      * @private
19029      */
19030     b4DragOver: function(e) { },
19031
19032     /**
19033      * Abstract method called when this element is hovering over another
19034      * DragDrop obj
19035      * @method onDragOver
19036      * @param {Event} e the mousemove event
19037      * @param {String|DragDrop[]} id In POINT mode, the element
19038      * id this is hovering over.  In INTERSECT mode, an array of dd items
19039      * being hovered over.
19040      */
19041     onDragOver: function(e, id) { /* override this */ },
19042
19043     /**
19044      * Code that executes immediately before the onDragOut event
19045      * @method b4DragOut
19046      * @private
19047      */
19048     b4DragOut: function(e) { },
19049
19050     /**
19051      * Abstract method called when we are no longer hovering over an element
19052      * @method onDragOut
19053      * @param {Event} e the mousemove event
19054      * @param {String|DragDrop[]} id In POINT mode, the element
19055      * id this was hovering over.  In INTERSECT mode, an array of dd items
19056      * that the mouse is no longer over.
19057      */
19058     onDragOut: function(e, id) { /* override this */ },
19059
19060     /**
19061      * Code that executes immediately before the onDragDrop event
19062      * @method b4DragDrop
19063      * @private
19064      */
19065     b4DragDrop: function(e) { },
19066
19067     /**
19068      * Abstract method called when this item is dropped on another DragDrop
19069      * obj
19070      * @method onDragDrop
19071      * @param {Event} e the mouseup event
19072      * @param {String|DragDrop[]} id In POINT mode, the element
19073      * id this was dropped on.  In INTERSECT mode, an array of dd items this
19074      * was dropped on.
19075      */
19076     onDragDrop: function(e, id) { /* override this */ },
19077
19078     /**
19079      * Abstract method called when this item is dropped on an area with no
19080      * drop target
19081      * @method onInvalidDrop
19082      * @param {Event} e the mouseup event
19083      */
19084     onInvalidDrop: function(e) { /* override this */ },
19085
19086     /**
19087      * Code that executes immediately before the endDrag event
19088      * @method b4EndDrag
19089      * @private
19090      */
19091     b4EndDrag: function(e) { },
19092
19093     /**
19094      * Fired when we are done dragging the object
19095      * @method endDrag
19096      * @param {Event} e the mouseup event
19097      */
19098     endDrag: function(e) { /* override this */ },
19099
19100     /**
19101      * Code executed immediately before the onMouseDown event
19102      * @method b4MouseDown
19103      * @param {Event} e the mousedown event
19104      * @private
19105      */
19106     b4MouseDown: function(e) {  },
19107
19108     /**
19109      * Event handler that fires when a drag/drop obj gets a mousedown
19110      * @method onMouseDown
19111      * @param {Event} e the mousedown event
19112      */
19113     onMouseDown: function(e) { /* override this */ },
19114
19115     /**
19116      * Event handler that fires when a drag/drop obj gets a mouseup
19117      * @method onMouseUp
19118      * @param {Event} e the mouseup event
19119      */
19120     onMouseUp: function(e) { /* override this */ },
19121
19122     /**
19123      * Override the onAvailable method to do what is needed after the initial
19124      * position was determined.
19125      * @method onAvailable
19126      */
19127     onAvailable: function () {
19128     },
19129
19130     /*
19131      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
19132      * @type Object
19133      */
19134     defaultPadding : {left:0, right:0, top:0, bottom:0},
19135
19136     /*
19137      * Initializes the drag drop object's constraints to restrict movement to a certain element.
19138  *
19139  * Usage:
19140  <pre><code>
19141  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
19142                 { dragElId: "existingProxyDiv" });
19143  dd.startDrag = function(){
19144      this.constrainTo("parent-id");
19145  };
19146  </code></pre>
19147  * Or you can initalize it using the {@link Roo.Element} object:
19148  <pre><code>
19149  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
19150      startDrag : function(){
19151          this.constrainTo("parent-id");
19152      }
19153  });
19154  </code></pre>
19155      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
19156      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
19157      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
19158      * an object containing the sides to pad. For example: {right:10, bottom:10}
19159      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
19160      */
19161     constrainTo : function(constrainTo, pad, inContent){
19162         if(typeof pad == "number"){
19163             pad = {left: pad, right:pad, top:pad, bottom:pad};
19164         }
19165         pad = pad || this.defaultPadding;
19166         var b = Roo.get(this.getEl()).getBox();
19167         var ce = Roo.get(constrainTo);
19168         var s = ce.getScroll();
19169         var c, cd = ce.dom;
19170         if(cd == document.body){
19171             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
19172         }else{
19173             xy = ce.getXY();
19174             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
19175         }
19176
19177
19178         var topSpace = b.y - c.y;
19179         var leftSpace = b.x - c.x;
19180
19181         this.resetConstraints();
19182         this.setXConstraint(leftSpace - (pad.left||0), // left
19183                 c.width - leftSpace - b.width - (pad.right||0) //right
19184         );
19185         this.setYConstraint(topSpace - (pad.top||0), //top
19186                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
19187         );
19188     },
19189
19190     /**
19191      * Returns a reference to the linked element
19192      * @method getEl
19193      * @return {HTMLElement} the html element
19194      */
19195     getEl: function() {
19196         if (!this._domRef) {
19197             this._domRef = Roo.getDom(this.id);
19198         }
19199
19200         return this._domRef;
19201     },
19202
19203     /**
19204      * Returns a reference to the actual element to drag.  By default this is
19205      * the same as the html element, but it can be assigned to another
19206      * element. An example of this can be found in Roo.dd.DDProxy
19207      * @method getDragEl
19208      * @return {HTMLElement} the html element
19209      */
19210     getDragEl: function() {
19211         return Roo.getDom(this.dragElId);
19212     },
19213
19214     /**
19215      * Sets up the DragDrop object.  Must be called in the constructor of any
19216      * Roo.dd.DragDrop subclass
19217      * @method init
19218      * @param id the id of the linked element
19219      * @param {String} sGroup the group of related items
19220      * @param {object} config configuration attributes
19221      */
19222     init: function(id, sGroup, config) {
19223         this.initTarget(id, sGroup, config);
19224         if (!Roo.isTouch) {
19225             Event.on(this.id, "mousedown", this.handleMouseDown, this);
19226         }
19227         Event.on(this.id, "touchstart", this.handleMouseDown, this);
19228         // Event.on(this.id, "selectstart", Event.preventDefault);
19229     },
19230
19231     /**
19232      * Initializes Targeting functionality only... the object does not
19233      * get a mousedown handler.
19234      * @method initTarget
19235      * @param id the id of the linked element
19236      * @param {String} sGroup the group of related items
19237      * @param {object} config configuration attributes
19238      */
19239     initTarget: function(id, sGroup, config) {
19240
19241         // configuration attributes
19242         this.config = config || {};
19243
19244         // create a local reference to the drag and drop manager
19245         this.DDM = Roo.dd.DDM;
19246         // initialize the groups array
19247         this.groups = {};
19248
19249         // assume that we have an element reference instead of an id if the
19250         // parameter is not a string
19251         if (typeof id !== "string") {
19252             id = Roo.id(id);
19253         }
19254
19255         // set the id
19256         this.id = id;
19257
19258         // add to an interaction group
19259         this.addToGroup((sGroup) ? sGroup : "default");
19260
19261         // We don't want to register this as the handle with the manager
19262         // so we just set the id rather than calling the setter.
19263         this.handleElId = id;
19264
19265         // the linked element is the element that gets dragged by default
19266         this.setDragElId(id);
19267
19268         // by default, clicked anchors will not start drag operations.
19269         this.invalidHandleTypes = { A: "A" };
19270         this.invalidHandleIds = {};
19271         this.invalidHandleClasses = [];
19272
19273         this.applyConfig();
19274
19275         this.handleOnAvailable();
19276     },
19277
19278     /**
19279      * Applies the configuration parameters that were passed into the constructor.
19280      * This is supposed to happen at each level through the inheritance chain.  So
19281      * a DDProxy implentation will execute apply config on DDProxy, DD, and
19282      * DragDrop in order to get all of the parameters that are available in
19283      * each object.
19284      * @method applyConfig
19285      */
19286     applyConfig: function() {
19287
19288         // configurable properties:
19289         //    padding, isTarget, maintainOffset, primaryButtonOnly
19290         this.padding           = this.config.padding || [0, 0, 0, 0];
19291         this.isTarget          = (this.config.isTarget !== false);
19292         this.maintainOffset    = (this.config.maintainOffset);
19293         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
19294
19295     },
19296
19297     /**
19298      * Executed when the linked element is available
19299      * @method handleOnAvailable
19300      * @private
19301      */
19302     handleOnAvailable: function() {
19303         this.available = true;
19304         this.resetConstraints();
19305         this.onAvailable();
19306     },
19307
19308      /**
19309      * Configures the padding for the target zone in px.  Effectively expands
19310      * (or reduces) the virtual object size for targeting calculations.
19311      * Supports css-style shorthand; if only one parameter is passed, all sides
19312      * will have that padding, and if only two are passed, the top and bottom
19313      * will have the first param, the left and right the second.
19314      * @method setPadding
19315      * @param {int} iTop    Top pad
19316      * @param {int} iRight  Right pad
19317      * @param {int} iBot    Bot pad
19318      * @param {int} iLeft   Left pad
19319      */
19320     setPadding: function(iTop, iRight, iBot, iLeft) {
19321         // this.padding = [iLeft, iRight, iTop, iBot];
19322         if (!iRight && 0 !== iRight) {
19323             this.padding = [iTop, iTop, iTop, iTop];
19324         } else if (!iBot && 0 !== iBot) {
19325             this.padding = [iTop, iRight, iTop, iRight];
19326         } else {
19327             this.padding = [iTop, iRight, iBot, iLeft];
19328         }
19329     },
19330
19331     /**
19332      * Stores the initial placement of the linked element.
19333      * @method setInitialPosition
19334      * @param {int} diffX   the X offset, default 0
19335      * @param {int} diffY   the Y offset, default 0
19336      */
19337     setInitPosition: function(diffX, diffY) {
19338         var el = this.getEl();
19339
19340         if (!this.DDM.verifyEl(el)) {
19341             return;
19342         }
19343
19344         var dx = diffX || 0;
19345         var dy = diffY || 0;
19346
19347         var p = Dom.getXY( el );
19348
19349         this.initPageX = p[0] - dx;
19350         this.initPageY = p[1] - dy;
19351
19352         this.lastPageX = p[0];
19353         this.lastPageY = p[1];
19354
19355
19356         this.setStartPosition(p);
19357     },
19358
19359     /**
19360      * Sets the start position of the element.  This is set when the obj
19361      * is initialized, the reset when a drag is started.
19362      * @method setStartPosition
19363      * @param pos current position (from previous lookup)
19364      * @private
19365      */
19366     setStartPosition: function(pos) {
19367         var p = pos || Dom.getXY( this.getEl() );
19368         this.deltaSetXY = null;
19369
19370         this.startPageX = p[0];
19371         this.startPageY = p[1];
19372     },
19373
19374     /**
19375      * Add this instance to a group of related drag/drop objects.  All
19376      * instances belong to at least one group, and can belong to as many
19377      * groups as needed.
19378      * @method addToGroup
19379      * @param sGroup {string} the name of the group
19380      */
19381     addToGroup: function(sGroup) {
19382         this.groups[sGroup] = true;
19383         this.DDM.regDragDrop(this, sGroup);
19384     },
19385
19386     /**
19387      * Remove's this instance from the supplied interaction group
19388      * @method removeFromGroup
19389      * @param {string}  sGroup  The group to drop
19390      */
19391     removeFromGroup: function(sGroup) {
19392         if (this.groups[sGroup]) {
19393             delete this.groups[sGroup];
19394         }
19395
19396         this.DDM.removeDDFromGroup(this, sGroup);
19397     },
19398
19399     /**
19400      * Allows you to specify that an element other than the linked element
19401      * will be moved with the cursor during a drag
19402      * @method setDragElId
19403      * @param id {string} the id of the element that will be used to initiate the drag
19404      */
19405     setDragElId: function(id) {
19406         this.dragElId = id;
19407     },
19408
19409     /**
19410      * Allows you to specify a child of the linked element that should be
19411      * used to initiate the drag operation.  An example of this would be if
19412      * you have a content div with text and links.  Clicking anywhere in the
19413      * content area would normally start the drag operation.  Use this method
19414      * to specify that an element inside of the content div is the element
19415      * that starts the drag operation.
19416      * @method setHandleElId
19417      * @param id {string} the id of the element that will be used to
19418      * initiate the drag.
19419      */
19420     setHandleElId: function(id) {
19421         if (typeof id !== "string") {
19422             id = Roo.id(id);
19423         }
19424         this.handleElId = id;
19425         this.DDM.regHandle(this.id, id);
19426     },
19427
19428     /**
19429      * Allows you to set an element outside of the linked element as a drag
19430      * handle
19431      * @method setOuterHandleElId
19432      * @param id the id of the element that will be used to initiate the drag
19433      */
19434     setOuterHandleElId: function(id) {
19435         if (typeof id !== "string") {
19436             id = Roo.id(id);
19437         }
19438         Event.on(id, "mousedown",
19439                 this.handleMouseDown, this);
19440         this.setHandleElId(id);
19441
19442         this.hasOuterHandles = true;
19443     },
19444
19445     /**
19446      * Remove all drag and drop hooks for this element
19447      * @method unreg
19448      */
19449     unreg: function() {
19450         Event.un(this.id, "mousedown",
19451                 this.handleMouseDown);
19452         Event.un(this.id, "touchstart",
19453                 this.handleMouseDown);
19454         this._domRef = null;
19455         this.DDM._remove(this);
19456     },
19457
19458     destroy : function(){
19459         this.unreg();
19460     },
19461
19462     /**
19463      * Returns true if this instance is locked, or the drag drop mgr is locked
19464      * (meaning that all drag/drop is disabled on the page.)
19465      * @method isLocked
19466      * @return {boolean} true if this obj or all drag/drop is locked, else
19467      * false
19468      */
19469     isLocked: function() {
19470         return (this.DDM.isLocked() || this.locked);
19471     },
19472
19473     /**
19474      * Fired when this object is clicked
19475      * @method handleMouseDown
19476      * @param {Event} e
19477      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
19478      * @private
19479      */
19480     handleMouseDown: function(e, oDD){
19481      
19482         if (!Roo.isTouch && this.primaryButtonOnly && e.button != 0) {
19483             //Roo.log('not touch/ button !=0');
19484             return;
19485         }
19486         if (e.browserEvent.touches && e.browserEvent.touches.length != 1) {
19487             return; // double touch..
19488         }
19489         
19490
19491         if (this.isLocked()) {
19492             //Roo.log('locked');
19493             return;
19494         }
19495
19496         this.DDM.refreshCache(this.groups);
19497 //        Roo.log([Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e)]);
19498         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
19499         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
19500             //Roo.log('no outer handes or not over target');
19501                 // do nothing.
19502         } else {
19503 //            Roo.log('check validator');
19504             if (this.clickValidator(e)) {
19505 //                Roo.log('validate success');
19506                 // set the initial element position
19507                 this.setStartPosition();
19508
19509
19510                 this.b4MouseDown(e);
19511                 this.onMouseDown(e);
19512
19513                 this.DDM.handleMouseDown(e, this);
19514
19515                 this.DDM.stopEvent(e);
19516             } else {
19517
19518
19519             }
19520         }
19521     },
19522
19523     clickValidator: function(e) {
19524         var target = e.getTarget();
19525         return ( this.isValidHandleChild(target) &&
19526                     (this.id == this.handleElId ||
19527                         this.DDM.handleWasClicked(target, this.id)) );
19528     },
19529
19530     /**
19531      * Allows you to specify a tag name that should not start a drag operation
19532      * when clicked.  This is designed to facilitate embedding links within a
19533      * drag handle that do something other than start the drag.
19534      * @method addInvalidHandleType
19535      * @param {string} tagName the type of element to exclude
19536      */
19537     addInvalidHandleType: function(tagName) {
19538         var type = tagName.toUpperCase();
19539         this.invalidHandleTypes[type] = type;
19540     },
19541
19542     /**
19543      * Lets you to specify an element id for a child of a drag handle
19544      * that should not initiate a drag
19545      * @method addInvalidHandleId
19546      * @param {string} id the element id of the element you wish to ignore
19547      */
19548     addInvalidHandleId: function(id) {
19549         if (typeof id !== "string") {
19550             id = Roo.id(id);
19551         }
19552         this.invalidHandleIds[id] = id;
19553     },
19554
19555     /**
19556      * Lets you specify a css class of elements that will not initiate a drag
19557      * @method addInvalidHandleClass
19558      * @param {string} cssClass the class of the elements you wish to ignore
19559      */
19560     addInvalidHandleClass: function(cssClass) {
19561         this.invalidHandleClasses.push(cssClass);
19562     },
19563
19564     /**
19565      * Unsets an excluded tag name set by addInvalidHandleType
19566      * @method removeInvalidHandleType
19567      * @param {string} tagName the type of element to unexclude
19568      */
19569     removeInvalidHandleType: function(tagName) {
19570         var type = tagName.toUpperCase();
19571         // this.invalidHandleTypes[type] = null;
19572         delete this.invalidHandleTypes[type];
19573     },
19574
19575     /**
19576      * Unsets an invalid handle id
19577      * @method removeInvalidHandleId
19578      * @param {string} id the id of the element to re-enable
19579      */
19580     removeInvalidHandleId: function(id) {
19581         if (typeof id !== "string") {
19582             id = Roo.id(id);
19583         }
19584         delete this.invalidHandleIds[id];
19585     },
19586
19587     /**
19588      * Unsets an invalid css class
19589      * @method removeInvalidHandleClass
19590      * @param {string} cssClass the class of the element(s) you wish to
19591      * re-enable
19592      */
19593     removeInvalidHandleClass: function(cssClass) {
19594         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
19595             if (this.invalidHandleClasses[i] == cssClass) {
19596                 delete this.invalidHandleClasses[i];
19597             }
19598         }
19599     },
19600
19601     /**
19602      * Checks the tag exclusion list to see if this click should be ignored
19603      * @method isValidHandleChild
19604      * @param {HTMLElement} node the HTMLElement to evaluate
19605      * @return {boolean} true if this is a valid tag type, false if not
19606      */
19607     isValidHandleChild: function(node) {
19608
19609         var valid = true;
19610         // var n = (node.nodeName == "#text") ? node.parentNode : node;
19611         var nodeName;
19612         try {
19613             nodeName = node.nodeName.toUpperCase();
19614         } catch(e) {
19615             nodeName = node.nodeName;
19616         }
19617         valid = valid && !this.invalidHandleTypes[nodeName];
19618         valid = valid && !this.invalidHandleIds[node.id];
19619
19620         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
19621             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
19622         }
19623
19624
19625         return valid;
19626
19627     },
19628
19629     /**
19630      * Create the array of horizontal tick marks if an interval was specified
19631      * in setXConstraint().
19632      * @method setXTicks
19633      * @private
19634      */
19635     setXTicks: function(iStartX, iTickSize) {
19636         this.xTicks = [];
19637         this.xTickSize = iTickSize;
19638
19639         var tickMap = {};
19640
19641         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
19642             if (!tickMap[i]) {
19643                 this.xTicks[this.xTicks.length] = i;
19644                 tickMap[i] = true;
19645             }
19646         }
19647
19648         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
19649             if (!tickMap[i]) {
19650                 this.xTicks[this.xTicks.length] = i;
19651                 tickMap[i] = true;
19652             }
19653         }
19654
19655         this.xTicks.sort(this.DDM.numericSort) ;
19656     },
19657
19658     /**
19659      * Create the array of vertical tick marks if an interval was specified in
19660      * setYConstraint().
19661      * @method setYTicks
19662      * @private
19663      */
19664     setYTicks: function(iStartY, iTickSize) {
19665         this.yTicks = [];
19666         this.yTickSize = iTickSize;
19667
19668         var tickMap = {};
19669
19670         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
19671             if (!tickMap[i]) {
19672                 this.yTicks[this.yTicks.length] = i;
19673                 tickMap[i] = true;
19674             }
19675         }
19676
19677         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
19678             if (!tickMap[i]) {
19679                 this.yTicks[this.yTicks.length] = i;
19680                 tickMap[i] = true;
19681             }
19682         }
19683
19684         this.yTicks.sort(this.DDM.numericSort) ;
19685     },
19686
19687     /**
19688      * By default, the element can be dragged any place on the screen.  Use
19689      * this method to limit the horizontal travel of the element.  Pass in
19690      * 0,0 for the parameters if you want to lock the drag to the y axis.
19691      * @method setXConstraint
19692      * @param {int} iLeft the number of pixels the element can move to the left
19693      * @param {int} iRight the number of pixels the element can move to the
19694      * right
19695      * @param {int} iTickSize optional parameter for specifying that the
19696      * element
19697      * should move iTickSize pixels at a time.
19698      */
19699     setXConstraint: function(iLeft, iRight, iTickSize) {
19700         this.leftConstraint = iLeft;
19701         this.rightConstraint = iRight;
19702
19703         this.minX = this.initPageX - iLeft;
19704         this.maxX = this.initPageX + iRight;
19705         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
19706
19707         this.constrainX = true;
19708     },
19709
19710     /**
19711      * Clears any constraints applied to this instance.  Also clears ticks
19712      * since they can't exist independent of a constraint at this time.
19713      * @method clearConstraints
19714      */
19715     clearConstraints: function() {
19716         this.constrainX = false;
19717         this.constrainY = false;
19718         this.clearTicks();
19719     },
19720
19721     /**
19722      * Clears any tick interval defined for this instance
19723      * @method clearTicks
19724      */
19725     clearTicks: function() {
19726         this.xTicks = null;
19727         this.yTicks = null;
19728         this.xTickSize = 0;
19729         this.yTickSize = 0;
19730     },
19731
19732     /**
19733      * By default, the element can be dragged any place on the screen.  Set
19734      * this to limit the vertical travel of the element.  Pass in 0,0 for the
19735      * parameters if you want to lock the drag to the x axis.
19736      * @method setYConstraint
19737      * @param {int} iUp the number of pixels the element can move up
19738      * @param {int} iDown the number of pixels the element can move down
19739      * @param {int} iTickSize optional parameter for specifying that the
19740      * element should move iTickSize pixels at a time.
19741      */
19742     setYConstraint: function(iUp, iDown, iTickSize) {
19743         this.topConstraint = iUp;
19744         this.bottomConstraint = iDown;
19745
19746         this.minY = this.initPageY - iUp;
19747         this.maxY = this.initPageY + iDown;
19748         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
19749
19750         this.constrainY = true;
19751
19752     },
19753
19754     /**
19755      * resetConstraints must be called if you manually reposition a dd element.
19756      * @method resetConstraints
19757      * @param {boolean} maintainOffset
19758      */
19759     resetConstraints: function() {
19760
19761
19762         // Maintain offsets if necessary
19763         if (this.initPageX || this.initPageX === 0) {
19764             // figure out how much this thing has moved
19765             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
19766             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
19767
19768             this.setInitPosition(dx, dy);
19769
19770         // This is the first time we have detected the element's position
19771         } else {
19772             this.setInitPosition();
19773         }
19774
19775         if (this.constrainX) {
19776             this.setXConstraint( this.leftConstraint,
19777                                  this.rightConstraint,
19778                                  this.xTickSize        );
19779         }
19780
19781         if (this.constrainY) {
19782             this.setYConstraint( this.topConstraint,
19783                                  this.bottomConstraint,
19784                                  this.yTickSize         );
19785         }
19786     },
19787
19788     /**
19789      * Normally the drag element is moved pixel by pixel, but we can specify
19790      * that it move a number of pixels at a time.  This method resolves the
19791      * location when we have it set up like this.
19792      * @method getTick
19793      * @param {int} val where we want to place the object
19794      * @param {int[]} tickArray sorted array of valid points
19795      * @return {int} the closest tick
19796      * @private
19797      */
19798     getTick: function(val, tickArray) {
19799
19800         if (!tickArray) {
19801             // If tick interval is not defined, it is effectively 1 pixel,
19802             // so we return the value passed to us.
19803             return val;
19804         } else if (tickArray[0] >= val) {
19805             // The value is lower than the first tick, so we return the first
19806             // tick.
19807             return tickArray[0];
19808         } else {
19809             for (var i=0, len=tickArray.length; i<len; ++i) {
19810                 var next = i + 1;
19811                 if (tickArray[next] && tickArray[next] >= val) {
19812                     var diff1 = val - tickArray[i];
19813                     var diff2 = tickArray[next] - val;
19814                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
19815                 }
19816             }
19817
19818             // The value is larger than the last tick, so we return the last
19819             // tick.
19820             return tickArray[tickArray.length - 1];
19821         }
19822     },
19823
19824     /**
19825      * toString method
19826      * @method toString
19827      * @return {string} string representation of the dd obj
19828      */
19829     toString: function() {
19830         return ("DragDrop " + this.id);
19831     }
19832
19833 });
19834
19835 })();
19836 /*
19837  * Based on:
19838  * Ext JS Library 1.1.1
19839  * Copyright(c) 2006-2007, Ext JS, LLC.
19840  *
19841  * Originally Released Under LGPL - original licence link has changed is not relivant.
19842  *
19843  * Fork - LGPL
19844  * <script type="text/javascript">
19845  */
19846
19847
19848 /**
19849  * The drag and drop utility provides a framework for building drag and drop
19850  * applications.  In addition to enabling drag and drop for specific elements,
19851  * the drag and drop elements are tracked by the manager class, and the
19852  * interactions between the various elements are tracked during the drag and
19853  * the implementing code is notified about these important moments.
19854  */
19855
19856 // Only load the library once.  Rewriting the manager class would orphan
19857 // existing drag and drop instances.
19858 if (!Roo.dd.DragDropMgr) {
19859
19860 /**
19861  * @class Roo.dd.DragDropMgr
19862  * DragDropMgr is a singleton that tracks the element interaction for
19863  * all DragDrop items in the window.  Generally, you will not call
19864  * this class directly, but it does have helper methods that could
19865  * be useful in your DragDrop implementations.
19866  * @singleton
19867  */
19868 Roo.dd.DragDropMgr = function() {
19869
19870     var Event = Roo.EventManager;
19871
19872     return {
19873
19874         /**
19875          * Two dimensional Array of registered DragDrop objects.  The first
19876          * dimension is the DragDrop item group, the second the DragDrop
19877          * object.
19878          * @property ids
19879          * @type {string: string}
19880          * @private
19881          * @static
19882          */
19883         ids: {},
19884
19885         /**
19886          * Array of element ids defined as drag handles.  Used to determine
19887          * if the element that generated the mousedown event is actually the
19888          * handle and not the html element itself.
19889          * @property handleIds
19890          * @type {string: string}
19891          * @private
19892          * @static
19893          */
19894         handleIds: {},
19895
19896         /**
19897          * the DragDrop object that is currently being dragged
19898          * @property dragCurrent
19899          * @type DragDrop
19900          * @private
19901          * @static
19902          **/
19903         dragCurrent: null,
19904
19905         /**
19906          * the DragDrop object(s) that are being hovered over
19907          * @property dragOvers
19908          * @type Array
19909          * @private
19910          * @static
19911          */
19912         dragOvers: {},
19913
19914         /**
19915          * the X distance between the cursor and the object being dragged
19916          * @property deltaX
19917          * @type int
19918          * @private
19919          * @static
19920          */
19921         deltaX: 0,
19922
19923         /**
19924          * the Y distance between the cursor and the object being dragged
19925          * @property deltaY
19926          * @type int
19927          * @private
19928          * @static
19929          */
19930         deltaY: 0,
19931
19932         /**
19933          * Flag to determine if we should prevent the default behavior of the
19934          * events we define. By default this is true, but this can be set to
19935          * false if you need the default behavior (not recommended)
19936          * @property preventDefault
19937          * @type boolean
19938          * @static
19939          */
19940         preventDefault: true,
19941
19942         /**
19943          * Flag to determine if we should stop the propagation of the events
19944          * we generate. This is true by default but you may want to set it to
19945          * false if the html element contains other features that require the
19946          * mouse click.
19947          * @property stopPropagation
19948          * @type boolean
19949          * @static
19950          */
19951         stopPropagation: true,
19952
19953         /**
19954          * Internal flag that is set to true when drag and drop has been
19955          * intialized
19956          * @property initialized
19957          * @private
19958          * @static
19959          */
19960         initalized: false,
19961
19962         /**
19963          * All drag and drop can be disabled.
19964          * @property locked
19965          * @private
19966          * @static
19967          */
19968         locked: false,
19969
19970         /**
19971          * Called the first time an element is registered.
19972          * @method init
19973          * @private
19974          * @static
19975          */
19976         init: function() {
19977             this.initialized = true;
19978         },
19979
19980         /**
19981          * In point mode, drag and drop interaction is defined by the
19982          * location of the cursor during the drag/drop
19983          * @property POINT
19984          * @type int
19985          * @static
19986          */
19987         POINT: 0,
19988
19989         /**
19990          * In intersect mode, drag and drop interactio nis defined by the
19991          * overlap of two or more drag and drop objects.
19992          * @property INTERSECT
19993          * @type int
19994          * @static
19995          */
19996         INTERSECT: 1,
19997
19998         /**
19999          * The current drag and drop mode.  Default: POINT
20000          * @property mode
20001          * @type int
20002          * @static
20003          */
20004         mode: 0,
20005
20006         /**
20007          * Runs method on all drag and drop objects
20008          * @method _execOnAll
20009          * @private
20010          * @static
20011          */
20012         _execOnAll: function(sMethod, args) {
20013             for (var i in this.ids) {
20014                 for (var j in this.ids[i]) {
20015                     var oDD = this.ids[i][j];
20016                     if (! this.isTypeOfDD(oDD)) {
20017                         continue;
20018                     }
20019                     oDD[sMethod].apply(oDD, args);
20020                 }
20021             }
20022         },
20023
20024         /**
20025          * Drag and drop initialization.  Sets up the global event handlers
20026          * @method _onLoad
20027          * @private
20028          * @static
20029          */
20030         _onLoad: function() {
20031
20032             this.init();
20033
20034             if (!Roo.isTouch) {
20035                 Event.on(document, "mouseup",   this.handleMouseUp, this, true);
20036                 Event.on(document, "mousemove", this.handleMouseMove, this, true);
20037             }
20038             Event.on(document, "touchend",   this.handleMouseUp, this, true);
20039             Event.on(document, "touchmove", this.handleMouseMove, this, true);
20040             
20041             Event.on(window,   "unload",    this._onUnload, this, true);
20042             Event.on(window,   "resize",    this._onResize, this, true);
20043             // Event.on(window,   "mouseout",    this._test);
20044
20045         },
20046
20047         /**
20048          * Reset constraints on all drag and drop objs
20049          * @method _onResize
20050          * @private
20051          * @static
20052          */
20053         _onResize: function(e) {
20054             this._execOnAll("resetConstraints", []);
20055         },
20056
20057         /**
20058          * Lock all drag and drop functionality
20059          * @method lock
20060          * @static
20061          */
20062         lock: function() { this.locked = true; },
20063
20064         /**
20065          * Unlock all drag and drop functionality
20066          * @method unlock
20067          * @static
20068          */
20069         unlock: function() { this.locked = false; },
20070
20071         /**
20072          * Is drag and drop locked?
20073          * @method isLocked
20074          * @return {boolean} True if drag and drop is locked, false otherwise.
20075          * @static
20076          */
20077         isLocked: function() { return this.locked; },
20078
20079         /**
20080          * Location cache that is set for all drag drop objects when a drag is
20081          * initiated, cleared when the drag is finished.
20082          * @property locationCache
20083          * @private
20084          * @static
20085          */
20086         locationCache: {},
20087
20088         /**
20089          * Set useCache to false if you want to force object the lookup of each
20090          * drag and drop linked element constantly during a drag.
20091          * @property useCache
20092          * @type boolean
20093          * @static
20094          */
20095         useCache: true,
20096
20097         /**
20098          * The number of pixels that the mouse needs to move after the
20099          * mousedown before the drag is initiated.  Default=3;
20100          * @property clickPixelThresh
20101          * @type int
20102          * @static
20103          */
20104         clickPixelThresh: 3,
20105
20106         /**
20107          * The number of milliseconds after the mousedown event to initiate the
20108          * drag if we don't get a mouseup event. Default=1000
20109          * @property clickTimeThresh
20110          * @type int
20111          * @static
20112          */
20113         clickTimeThresh: 350,
20114
20115         /**
20116          * Flag that indicates that either the drag pixel threshold or the
20117          * mousdown time threshold has been met
20118          * @property dragThreshMet
20119          * @type boolean
20120          * @private
20121          * @static
20122          */
20123         dragThreshMet: false,
20124
20125         /**
20126          * Timeout used for the click time threshold
20127          * @property clickTimeout
20128          * @type Object
20129          * @private
20130          * @static
20131          */
20132         clickTimeout: null,
20133
20134         /**
20135          * The X position of the mousedown event stored for later use when a
20136          * drag threshold is met.
20137          * @property startX
20138          * @type int
20139          * @private
20140          * @static
20141          */
20142         startX: 0,
20143
20144         /**
20145          * The Y position of the mousedown event stored for later use when a
20146          * drag threshold is met.
20147          * @property startY
20148          * @type int
20149          * @private
20150          * @static
20151          */
20152         startY: 0,
20153
20154         /**
20155          * Each DragDrop instance must be registered with the DragDropMgr.
20156          * This is executed in DragDrop.init()
20157          * @method regDragDrop
20158          * @param {DragDrop} oDD the DragDrop object to register
20159          * @param {String} sGroup the name of the group this element belongs to
20160          * @static
20161          */
20162         regDragDrop: function(oDD, sGroup) {
20163             if (!this.initialized) { this.init(); }
20164
20165             if (!this.ids[sGroup]) {
20166                 this.ids[sGroup] = {};
20167             }
20168             this.ids[sGroup][oDD.id] = oDD;
20169         },
20170
20171         /**
20172          * Removes the supplied dd instance from the supplied group. Executed
20173          * by DragDrop.removeFromGroup, so don't call this function directly.
20174          * @method removeDDFromGroup
20175          * @private
20176          * @static
20177          */
20178         removeDDFromGroup: function(oDD, sGroup) {
20179             if (!this.ids[sGroup]) {
20180                 this.ids[sGroup] = {};
20181             }
20182
20183             var obj = this.ids[sGroup];
20184             if (obj && obj[oDD.id]) {
20185                 delete obj[oDD.id];
20186             }
20187         },
20188
20189         /**
20190          * Unregisters a drag and drop item.  This is executed in
20191          * DragDrop.unreg, use that method instead of calling this directly.
20192          * @method _remove
20193          * @private
20194          * @static
20195          */
20196         _remove: function(oDD) {
20197             for (var g in oDD.groups) {
20198                 if (g && this.ids[g][oDD.id]) {
20199                     delete this.ids[g][oDD.id];
20200                 }
20201             }
20202             delete this.handleIds[oDD.id];
20203         },
20204
20205         /**
20206          * Each DragDrop handle element must be registered.  This is done
20207          * automatically when executing DragDrop.setHandleElId()
20208          * @method regHandle
20209          * @param {String} sDDId the DragDrop id this element is a handle for
20210          * @param {String} sHandleId the id of the element that is the drag
20211          * handle
20212          * @static
20213          */
20214         regHandle: function(sDDId, sHandleId) {
20215             if (!this.handleIds[sDDId]) {
20216                 this.handleIds[sDDId] = {};
20217             }
20218             this.handleIds[sDDId][sHandleId] = sHandleId;
20219         },
20220
20221         /**
20222          * Utility function to determine if a given element has been
20223          * registered as a drag drop item.
20224          * @method isDragDrop
20225          * @param {String} id the element id to check
20226          * @return {boolean} true if this element is a DragDrop item,
20227          * false otherwise
20228          * @static
20229          */
20230         isDragDrop: function(id) {
20231             return ( this.getDDById(id) ) ? true : false;
20232         },
20233
20234         /**
20235          * Returns the drag and drop instances that are in all groups the
20236          * passed in instance belongs to.
20237          * @method getRelated
20238          * @param {DragDrop} p_oDD the obj to get related data for
20239          * @param {boolean} bTargetsOnly if true, only return targetable objs
20240          * @return {DragDrop[]} the related instances
20241          * @static
20242          */
20243         getRelated: function(p_oDD, bTargetsOnly) {
20244             var oDDs = [];
20245             for (var i in p_oDD.groups) {
20246                 for (j in this.ids[i]) {
20247                     var dd = this.ids[i][j];
20248                     if (! this.isTypeOfDD(dd)) {
20249                         continue;
20250                     }
20251                     if (!bTargetsOnly || dd.isTarget) {
20252                         oDDs[oDDs.length] = dd;
20253                     }
20254                 }
20255             }
20256
20257             return oDDs;
20258         },
20259
20260         /**
20261          * Returns true if the specified dd target is a legal target for
20262          * the specifice drag obj
20263          * @method isLegalTarget
20264          * @param {DragDrop} the drag obj
20265          * @param {DragDrop} the target
20266          * @return {boolean} true if the target is a legal target for the
20267          * dd obj
20268          * @static
20269          */
20270         isLegalTarget: function (oDD, oTargetDD) {
20271             var targets = this.getRelated(oDD, true);
20272             for (var i=0, len=targets.length;i<len;++i) {
20273                 if (targets[i].id == oTargetDD.id) {
20274                     return true;
20275                 }
20276             }
20277
20278             return false;
20279         },
20280
20281         /**
20282          * My goal is to be able to transparently determine if an object is
20283          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
20284          * returns "object", oDD.constructor.toString() always returns
20285          * "DragDrop" and not the name of the subclass.  So for now it just
20286          * evaluates a well-known variable in DragDrop.
20287          * @method isTypeOfDD
20288          * @param {Object} the object to evaluate
20289          * @return {boolean} true if typeof oDD = DragDrop
20290          * @static
20291          */
20292         isTypeOfDD: function (oDD) {
20293             return (oDD && oDD.__ygDragDrop);
20294         },
20295
20296         /**
20297          * Utility function to determine if a given element has been
20298          * registered as a drag drop handle for the given Drag Drop object.
20299          * @method isHandle
20300          * @param {String} id the element id to check
20301          * @return {boolean} true if this element is a DragDrop handle, false
20302          * otherwise
20303          * @static
20304          */
20305         isHandle: function(sDDId, sHandleId) {
20306             return ( this.handleIds[sDDId] &&
20307                             this.handleIds[sDDId][sHandleId] );
20308         },
20309
20310         /**
20311          * Returns the DragDrop instance for a given id
20312          * @method getDDById
20313          * @param {String} id the id of the DragDrop object
20314          * @return {DragDrop} the drag drop object, null if it is not found
20315          * @static
20316          */
20317         getDDById: function(id) {
20318             for (var i in this.ids) {
20319                 if (this.ids[i][id]) {
20320                     return this.ids[i][id];
20321                 }
20322             }
20323             return null;
20324         },
20325
20326         /**
20327          * Fired after a registered DragDrop object gets the mousedown event.
20328          * Sets up the events required to track the object being dragged
20329          * @method handleMouseDown
20330          * @param {Event} e the event
20331          * @param oDD the DragDrop object being dragged
20332          * @private
20333          * @static
20334          */
20335         handleMouseDown: function(e, oDD) {
20336             if(Roo.QuickTips){
20337                 Roo.QuickTips.disable();
20338             }
20339             this.currentTarget = e.getTarget();
20340
20341             this.dragCurrent = oDD;
20342
20343             var el = oDD.getEl();
20344
20345             // track start position
20346             this.startX = e.getPageX();
20347             this.startY = e.getPageY();
20348
20349             this.deltaX = this.startX - el.offsetLeft;
20350             this.deltaY = this.startY - el.offsetTop;
20351
20352             this.dragThreshMet = false;
20353
20354             this.clickTimeout = setTimeout(
20355                     function() {
20356                         var DDM = Roo.dd.DDM;
20357                         DDM.startDrag(DDM.startX, DDM.startY);
20358                     },
20359                     this.clickTimeThresh );
20360         },
20361
20362         /**
20363          * Fired when either the drag pixel threshol or the mousedown hold
20364          * time threshold has been met.
20365          * @method startDrag
20366          * @param x {int} the X position of the original mousedown
20367          * @param y {int} the Y position of the original mousedown
20368          * @static
20369          */
20370         startDrag: function(x, y) {
20371             clearTimeout(this.clickTimeout);
20372             if (this.dragCurrent) {
20373                 this.dragCurrent.b4StartDrag(x, y);
20374                 this.dragCurrent.startDrag(x, y);
20375             }
20376             this.dragThreshMet = true;
20377         },
20378
20379         /**
20380          * Internal function to handle the mouseup event.  Will be invoked
20381          * from the context of the document.
20382          * @method handleMouseUp
20383          * @param {Event} e the event
20384          * @private
20385          * @static
20386          */
20387         handleMouseUp: function(e) {
20388
20389             if(Roo.QuickTips){
20390                 Roo.QuickTips.enable();
20391             }
20392             if (! this.dragCurrent) {
20393                 return;
20394             }
20395
20396             clearTimeout(this.clickTimeout);
20397
20398             if (this.dragThreshMet) {
20399                 this.fireEvents(e, true);
20400             } else {
20401             }
20402
20403             this.stopDrag(e);
20404
20405             this.stopEvent(e);
20406         },
20407
20408         /**
20409          * Utility to stop event propagation and event default, if these
20410          * features are turned on.
20411          * @method stopEvent
20412          * @param {Event} e the event as returned by this.getEvent()
20413          * @static
20414          */
20415         stopEvent: function(e){
20416             if(this.stopPropagation) {
20417                 e.stopPropagation();
20418             }
20419
20420             if (this.preventDefault) {
20421                 e.preventDefault();
20422             }
20423         },
20424
20425         /**
20426          * Internal function to clean up event handlers after the drag
20427          * operation is complete
20428          * @method stopDrag
20429          * @param {Event} e the event
20430          * @private
20431          * @static
20432          */
20433         stopDrag: function(e) {
20434             // Fire the drag end event for the item that was dragged
20435             if (this.dragCurrent) {
20436                 if (this.dragThreshMet) {
20437                     this.dragCurrent.b4EndDrag(e);
20438                     this.dragCurrent.endDrag(e);
20439                 }
20440
20441                 this.dragCurrent.onMouseUp(e);
20442             }
20443
20444             this.dragCurrent = null;
20445             this.dragOvers = {};
20446         },
20447
20448         /**
20449          * Internal function to handle the mousemove event.  Will be invoked
20450          * from the context of the html element.
20451          *
20452          * @TODO figure out what we can do about mouse events lost when the
20453          * user drags objects beyond the window boundary.  Currently we can
20454          * detect this in internet explorer by verifying that the mouse is
20455          * down during the mousemove event.  Firefox doesn't give us the
20456          * button state on the mousemove event.
20457          * @method handleMouseMove
20458          * @param {Event} e the event
20459          * @private
20460          * @static
20461          */
20462         handleMouseMove: function(e) {
20463             if (! this.dragCurrent) {
20464                 return true;
20465             }
20466
20467             // var button = e.which || e.button;
20468
20469             // check for IE mouseup outside of page boundary
20470             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
20471                 this.stopEvent(e);
20472                 return this.handleMouseUp(e);
20473             }
20474
20475             if (!this.dragThreshMet) {
20476                 var diffX = Math.abs(this.startX - e.getPageX());
20477                 var diffY = Math.abs(this.startY - e.getPageY());
20478                 if (diffX > this.clickPixelThresh ||
20479                             diffY > this.clickPixelThresh) {
20480                     this.startDrag(this.startX, this.startY);
20481                 }
20482             }
20483
20484             if (this.dragThreshMet) {
20485                 this.dragCurrent.b4Drag(e);
20486                 this.dragCurrent.onDrag(e);
20487                 if(!this.dragCurrent.moveOnly){
20488                     this.fireEvents(e, false);
20489                 }
20490             }
20491
20492             this.stopEvent(e);
20493
20494             return true;
20495         },
20496
20497         /**
20498          * Iterates over all of the DragDrop elements to find ones we are
20499          * hovering over or dropping on
20500          * @method fireEvents
20501          * @param {Event} e the event
20502          * @param {boolean} isDrop is this a drop op or a mouseover op?
20503          * @private
20504          * @static
20505          */
20506         fireEvents: function(e, isDrop) {
20507             var dc = this.dragCurrent;
20508
20509             // If the user did the mouse up outside of the window, we could
20510             // get here even though we have ended the drag.
20511             if (!dc || dc.isLocked()) {
20512                 return;
20513             }
20514
20515             var pt = e.getPoint();
20516
20517             // cache the previous dragOver array
20518             var oldOvers = [];
20519
20520             var outEvts   = [];
20521             var overEvts  = [];
20522             var dropEvts  = [];
20523             var enterEvts = [];
20524
20525             // Check to see if the object(s) we were hovering over is no longer
20526             // being hovered over so we can fire the onDragOut event
20527             for (var i in this.dragOvers) {
20528
20529                 var ddo = this.dragOvers[i];
20530
20531                 if (! this.isTypeOfDD(ddo)) {
20532                     continue;
20533                 }
20534
20535                 if (! this.isOverTarget(pt, ddo, this.mode)) {
20536                     outEvts.push( ddo );
20537                 }
20538
20539                 oldOvers[i] = true;
20540                 delete this.dragOvers[i];
20541             }
20542
20543             for (var sGroup in dc.groups) {
20544
20545                 if ("string" != typeof sGroup) {
20546                     continue;
20547                 }
20548
20549                 for (i in this.ids[sGroup]) {
20550                     var oDD = this.ids[sGroup][i];
20551                     if (! this.isTypeOfDD(oDD)) {
20552                         continue;
20553                     }
20554
20555                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
20556                         if (this.isOverTarget(pt, oDD, this.mode)) {
20557                             // look for drop interactions
20558                             if (isDrop) {
20559                                 dropEvts.push( oDD );
20560                             // look for drag enter and drag over interactions
20561                             } else {
20562
20563                                 // initial drag over: dragEnter fires
20564                                 if (!oldOvers[oDD.id]) {
20565                                     enterEvts.push( oDD );
20566                                 // subsequent drag overs: dragOver fires
20567                                 } else {
20568                                     overEvts.push( oDD );
20569                                 }
20570
20571                                 this.dragOvers[oDD.id] = oDD;
20572                             }
20573                         }
20574                     }
20575                 }
20576             }
20577
20578             if (this.mode) {
20579                 if (outEvts.length) {
20580                     dc.b4DragOut(e, outEvts);
20581                     dc.onDragOut(e, outEvts);
20582                 }
20583
20584                 if (enterEvts.length) {
20585                     dc.onDragEnter(e, enterEvts);
20586                 }
20587
20588                 if (overEvts.length) {
20589                     dc.b4DragOver(e, overEvts);
20590                     dc.onDragOver(e, overEvts);
20591                 }
20592
20593                 if (dropEvts.length) {
20594                     dc.b4DragDrop(e, dropEvts);
20595                     dc.onDragDrop(e, dropEvts);
20596                 }
20597
20598             } else {
20599                 // fire dragout events
20600                 var len = 0;
20601                 for (i=0, len=outEvts.length; i<len; ++i) {
20602                     dc.b4DragOut(e, outEvts[i].id);
20603                     dc.onDragOut(e, outEvts[i].id);
20604                 }
20605
20606                 // fire enter events
20607                 for (i=0,len=enterEvts.length; i<len; ++i) {
20608                     // dc.b4DragEnter(e, oDD.id);
20609                     dc.onDragEnter(e, enterEvts[i].id);
20610                 }
20611
20612                 // fire over events
20613                 for (i=0,len=overEvts.length; i<len; ++i) {
20614                     dc.b4DragOver(e, overEvts[i].id);
20615                     dc.onDragOver(e, overEvts[i].id);
20616                 }
20617
20618                 // fire drop events
20619                 for (i=0, len=dropEvts.length; i<len; ++i) {
20620                     dc.b4DragDrop(e, dropEvts[i].id);
20621                     dc.onDragDrop(e, dropEvts[i].id);
20622                 }
20623
20624             }
20625
20626             // notify about a drop that did not find a target
20627             if (isDrop && !dropEvts.length) {
20628                 dc.onInvalidDrop(e);
20629             }
20630
20631         },
20632
20633         /**
20634          * Helper function for getting the best match from the list of drag
20635          * and drop objects returned by the drag and drop events when we are
20636          * in INTERSECT mode.  It returns either the first object that the
20637          * cursor is over, or the object that has the greatest overlap with
20638          * the dragged element.
20639          * @method getBestMatch
20640          * @param  {DragDrop[]} dds The array of drag and drop objects
20641          * targeted
20642          * @return {DragDrop}       The best single match
20643          * @static
20644          */
20645         getBestMatch: function(dds) {
20646             var winner = null;
20647             // Return null if the input is not what we expect
20648             //if (!dds || !dds.length || dds.length == 0) {
20649                // winner = null;
20650             // If there is only one item, it wins
20651             //} else if (dds.length == 1) {
20652
20653             var len = dds.length;
20654
20655             if (len == 1) {
20656                 winner = dds[0];
20657             } else {
20658                 // Loop through the targeted items
20659                 for (var i=0; i<len; ++i) {
20660                     var dd = dds[i];
20661                     // If the cursor is over the object, it wins.  If the
20662                     // cursor is over multiple matches, the first one we come
20663                     // to wins.
20664                     if (dd.cursorIsOver) {
20665                         winner = dd;
20666                         break;
20667                     // Otherwise the object with the most overlap wins
20668                     } else {
20669                         if (!winner ||
20670                             winner.overlap.getArea() < dd.overlap.getArea()) {
20671                             winner = dd;
20672                         }
20673                     }
20674                 }
20675             }
20676
20677             return winner;
20678         },
20679
20680         /**
20681          * Refreshes the cache of the top-left and bottom-right points of the
20682          * drag and drop objects in the specified group(s).  This is in the
20683          * format that is stored in the drag and drop instance, so typical
20684          * usage is:
20685          * <code>
20686          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
20687          * </code>
20688          * Alternatively:
20689          * <code>
20690          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
20691          * </code>
20692          * @TODO this really should be an indexed array.  Alternatively this
20693          * method could accept both.
20694          * @method refreshCache
20695          * @param {Object} groups an associative array of groups to refresh
20696          * @static
20697          */
20698         refreshCache: function(groups) {
20699             for (var sGroup in groups) {
20700                 if ("string" != typeof sGroup) {
20701                     continue;
20702                 }
20703                 for (var i in this.ids[sGroup]) {
20704                     var oDD = this.ids[sGroup][i];
20705
20706                     if (this.isTypeOfDD(oDD)) {
20707                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
20708                         var loc = this.getLocation(oDD);
20709                         if (loc) {
20710                             this.locationCache[oDD.id] = loc;
20711                         } else {
20712                             delete this.locationCache[oDD.id];
20713                             // this will unregister the drag and drop object if
20714                             // the element is not in a usable state
20715                             // oDD.unreg();
20716                         }
20717                     }
20718                 }
20719             }
20720         },
20721
20722         /**
20723          * This checks to make sure an element exists and is in the DOM.  The
20724          * main purpose is to handle cases where innerHTML is used to remove
20725          * drag and drop objects from the DOM.  IE provides an 'unspecified
20726          * error' when trying to access the offsetParent of such an element
20727          * @method verifyEl
20728          * @param {HTMLElement} el the element to check
20729          * @return {boolean} true if the element looks usable
20730          * @static
20731          */
20732         verifyEl: function(el) {
20733             if (el) {
20734                 var parent;
20735                 if(Roo.isIE){
20736                     try{
20737                         parent = el.offsetParent;
20738                     }catch(e){}
20739                 }else{
20740                     parent = el.offsetParent;
20741                 }
20742                 if (parent) {
20743                     return true;
20744                 }
20745             }
20746
20747             return false;
20748         },
20749
20750         /**
20751          * Returns a Region object containing the drag and drop element's position
20752          * and size, including the padding configured for it
20753          * @method getLocation
20754          * @param {DragDrop} oDD the drag and drop object to get the
20755          *                       location for
20756          * @return {Roo.lib.Region} a Region object representing the total area
20757          *                             the element occupies, including any padding
20758          *                             the instance is configured for.
20759          * @static
20760          */
20761         getLocation: function(oDD) {
20762             if (! this.isTypeOfDD(oDD)) {
20763                 return null;
20764             }
20765
20766             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
20767
20768             try {
20769                 pos= Roo.lib.Dom.getXY(el);
20770             } catch (e) { }
20771
20772             if (!pos) {
20773                 return null;
20774             }
20775
20776             x1 = pos[0];
20777             x2 = x1 + el.offsetWidth;
20778             y1 = pos[1];
20779             y2 = y1 + el.offsetHeight;
20780
20781             t = y1 - oDD.padding[0];
20782             r = x2 + oDD.padding[1];
20783             b = y2 + oDD.padding[2];
20784             l = x1 - oDD.padding[3];
20785
20786             return new Roo.lib.Region( t, r, b, l );
20787         },
20788
20789         /**
20790          * Checks the cursor location to see if it over the target
20791          * @method isOverTarget
20792          * @param {Roo.lib.Point} pt The point to evaluate
20793          * @param {DragDrop} oTarget the DragDrop object we are inspecting
20794          * @return {boolean} true if the mouse is over the target
20795          * @private
20796          * @static
20797          */
20798         isOverTarget: function(pt, oTarget, intersect) {
20799             // use cache if available
20800             var loc = this.locationCache[oTarget.id];
20801             if (!loc || !this.useCache) {
20802                 loc = this.getLocation(oTarget);
20803                 this.locationCache[oTarget.id] = loc;
20804
20805             }
20806
20807             if (!loc) {
20808                 return false;
20809             }
20810
20811             oTarget.cursorIsOver = loc.contains( pt );
20812
20813             // DragDrop is using this as a sanity check for the initial mousedown
20814             // in this case we are done.  In POINT mode, if the drag obj has no
20815             // contraints, we are also done. Otherwise we need to evaluate the
20816             // location of the target as related to the actual location of the
20817             // dragged element.
20818             var dc = this.dragCurrent;
20819             if (!dc || !dc.getTargetCoord ||
20820                     (!intersect && !dc.constrainX && !dc.constrainY)) {
20821                 return oTarget.cursorIsOver;
20822             }
20823
20824             oTarget.overlap = null;
20825
20826             // Get the current location of the drag element, this is the
20827             // location of the mouse event less the delta that represents
20828             // where the original mousedown happened on the element.  We
20829             // need to consider constraints and ticks as well.
20830             var pos = dc.getTargetCoord(pt.x, pt.y);
20831
20832             var el = dc.getDragEl();
20833             var curRegion = new Roo.lib.Region( pos.y,
20834                                                    pos.x + el.offsetWidth,
20835                                                    pos.y + el.offsetHeight,
20836                                                    pos.x );
20837
20838             var overlap = curRegion.intersect(loc);
20839
20840             if (overlap) {
20841                 oTarget.overlap = overlap;
20842                 return (intersect) ? true : oTarget.cursorIsOver;
20843             } else {
20844                 return false;
20845             }
20846         },
20847
20848         /**
20849          * unload event handler
20850          * @method _onUnload
20851          * @private
20852          * @static
20853          */
20854         _onUnload: function(e, me) {
20855             Roo.dd.DragDropMgr.unregAll();
20856         },
20857
20858         /**
20859          * Cleans up the drag and drop events and objects.
20860          * @method unregAll
20861          * @private
20862          * @static
20863          */
20864         unregAll: function() {
20865
20866             if (this.dragCurrent) {
20867                 this.stopDrag();
20868                 this.dragCurrent = null;
20869             }
20870
20871             this._execOnAll("unreg", []);
20872
20873             for (i in this.elementCache) {
20874                 delete this.elementCache[i];
20875             }
20876
20877             this.elementCache = {};
20878             this.ids = {};
20879         },
20880
20881         /**
20882          * A cache of DOM elements
20883          * @property elementCache
20884          * @private
20885          * @static
20886          */
20887         elementCache: {},
20888
20889         /**
20890          * Get the wrapper for the DOM element specified
20891          * @method getElWrapper
20892          * @param {String} id the id of the element to get
20893          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
20894          * @private
20895          * @deprecated This wrapper isn't that useful
20896          * @static
20897          */
20898         getElWrapper: function(id) {
20899             var oWrapper = this.elementCache[id];
20900             if (!oWrapper || !oWrapper.el) {
20901                 oWrapper = this.elementCache[id] =
20902                     new this.ElementWrapper(Roo.getDom(id));
20903             }
20904             return oWrapper;
20905         },
20906
20907         /**
20908          * Returns the actual DOM element
20909          * @method getElement
20910          * @param {String} id the id of the elment to get
20911          * @return {Object} The element
20912          * @deprecated use Roo.getDom instead
20913          * @static
20914          */
20915         getElement: function(id) {
20916             return Roo.getDom(id);
20917         },
20918
20919         /**
20920          * Returns the style property for the DOM element (i.e.,
20921          * document.getElById(id).style)
20922          * @method getCss
20923          * @param {String} id the id of the elment to get
20924          * @return {Object} The style property of the element
20925          * @deprecated use Roo.getDom instead
20926          * @static
20927          */
20928         getCss: function(id) {
20929             var el = Roo.getDom(id);
20930             return (el) ? el.style : null;
20931         },
20932
20933         /**
20934          * Inner class for cached elements
20935          * @class DragDropMgr.ElementWrapper
20936          * @for DragDropMgr
20937          * @private
20938          * @deprecated
20939          */
20940         ElementWrapper: function(el) {
20941                 /**
20942                  * The element
20943                  * @property el
20944                  */
20945                 this.el = el || null;
20946                 /**
20947                  * The element id
20948                  * @property id
20949                  */
20950                 this.id = this.el && el.id;
20951                 /**
20952                  * A reference to the style property
20953                  * @property css
20954                  */
20955                 this.css = this.el && el.style;
20956             },
20957
20958         /**
20959          * Returns the X position of an html element
20960          * @method getPosX
20961          * @param el the element for which to get the position
20962          * @return {int} the X coordinate
20963          * @for DragDropMgr
20964          * @deprecated use Roo.lib.Dom.getX instead
20965          * @static
20966          */
20967         getPosX: function(el) {
20968             return Roo.lib.Dom.getX(el);
20969         },
20970
20971         /**
20972          * Returns the Y position of an html element
20973          * @method getPosY
20974          * @param el the element for which to get the position
20975          * @return {int} the Y coordinate
20976          * @deprecated use Roo.lib.Dom.getY instead
20977          * @static
20978          */
20979         getPosY: function(el) {
20980             return Roo.lib.Dom.getY(el);
20981         },
20982
20983         /**
20984          * Swap two nodes.  In IE, we use the native method, for others we
20985          * emulate the IE behavior
20986          * @method swapNode
20987          * @param n1 the first node to swap
20988          * @param n2 the other node to swap
20989          * @static
20990          */
20991         swapNode: function(n1, n2) {
20992             if (n1.swapNode) {
20993                 n1.swapNode(n2);
20994             } else {
20995                 var p = n2.parentNode;
20996                 var s = n2.nextSibling;
20997
20998                 if (s == n1) {
20999                     p.insertBefore(n1, n2);
21000                 } else if (n2 == n1.nextSibling) {
21001                     p.insertBefore(n2, n1);
21002                 } else {
21003                     n1.parentNode.replaceChild(n2, n1);
21004                     p.insertBefore(n1, s);
21005                 }
21006             }
21007         },
21008
21009         /**
21010          * Returns the current scroll position
21011          * @method getScroll
21012          * @private
21013          * @static
21014          */
21015         getScroll: function () {
21016             var t, l, dde=document.documentElement, db=document.body;
21017             if (dde && (dde.scrollTop || dde.scrollLeft)) {
21018                 t = dde.scrollTop;
21019                 l = dde.scrollLeft;
21020             } else if (db) {
21021                 t = db.scrollTop;
21022                 l = db.scrollLeft;
21023             } else {
21024
21025             }
21026             return { top: t, left: l };
21027         },
21028
21029         /**
21030          * Returns the specified element style property
21031          * @method getStyle
21032          * @param {HTMLElement} el          the element
21033          * @param {string}      styleProp   the style property
21034          * @return {string} The value of the style property
21035          * @deprecated use Roo.lib.Dom.getStyle
21036          * @static
21037          */
21038         getStyle: function(el, styleProp) {
21039             return Roo.fly(el).getStyle(styleProp);
21040         },
21041
21042         /**
21043          * Gets the scrollTop
21044          * @method getScrollTop
21045          * @return {int} the document's scrollTop
21046          * @static
21047          */
21048         getScrollTop: function () { return this.getScroll().top; },
21049
21050         /**
21051          * Gets the scrollLeft
21052          * @method getScrollLeft
21053          * @return {int} the document's scrollTop
21054          * @static
21055          */
21056         getScrollLeft: function () { return this.getScroll().left; },
21057
21058         /**
21059          * Sets the x/y position of an element to the location of the
21060          * target element.
21061          * @method moveToEl
21062          * @param {HTMLElement} moveEl      The element to move
21063          * @param {HTMLElement} targetEl    The position reference element
21064          * @static
21065          */
21066         moveToEl: function (moveEl, targetEl) {
21067             var aCoord = Roo.lib.Dom.getXY(targetEl);
21068             Roo.lib.Dom.setXY(moveEl, aCoord);
21069         },
21070
21071         /**
21072          * Numeric array sort function
21073          * @method numericSort
21074          * @static
21075          */
21076         numericSort: function(a, b) { return (a - b); },
21077
21078         /**
21079          * Internal counter
21080          * @property _timeoutCount
21081          * @private
21082          * @static
21083          */
21084         _timeoutCount: 0,
21085
21086         /**
21087          * Trying to make the load order less important.  Without this we get
21088          * an error if this file is loaded before the Event Utility.
21089          * @method _addListeners
21090          * @private
21091          * @static
21092          */
21093         _addListeners: function() {
21094             var DDM = Roo.dd.DDM;
21095             if ( Roo.lib.Event && document ) {
21096                 DDM._onLoad();
21097             } else {
21098                 if (DDM._timeoutCount > 2000) {
21099                 } else {
21100                     setTimeout(DDM._addListeners, 10);
21101                     if (document && document.body) {
21102                         DDM._timeoutCount += 1;
21103                     }
21104                 }
21105             }
21106         },
21107
21108         /**
21109          * Recursively searches the immediate parent and all child nodes for
21110          * the handle element in order to determine wheter or not it was
21111          * clicked.
21112          * @method handleWasClicked
21113          * @param node the html element to inspect
21114          * @static
21115          */
21116         handleWasClicked: function(node, id) {
21117             if (this.isHandle(id, node.id)) {
21118                 return true;
21119             } else {
21120                 // check to see if this is a text node child of the one we want
21121                 var p = node.parentNode;
21122
21123                 while (p) {
21124                     if (this.isHandle(id, p.id)) {
21125                         return true;
21126                     } else {
21127                         p = p.parentNode;
21128                     }
21129                 }
21130             }
21131
21132             return false;
21133         }
21134
21135     };
21136
21137 }();
21138
21139 // shorter alias, save a few bytes
21140 Roo.dd.DDM = Roo.dd.DragDropMgr;
21141 Roo.dd.DDM._addListeners();
21142
21143 }/*
21144  * Based on:
21145  * Ext JS Library 1.1.1
21146  * Copyright(c) 2006-2007, Ext JS, LLC.
21147  *
21148  * Originally Released Under LGPL - original licence link has changed is not relivant.
21149  *
21150  * Fork - LGPL
21151  * <script type="text/javascript">
21152  */
21153
21154 /**
21155  * @class Roo.dd.DD
21156  * A DragDrop implementation where the linked element follows the
21157  * mouse cursor during a drag.
21158  * @extends Roo.dd.DragDrop
21159  * @constructor
21160  * @param {String} id the id of the linked element
21161  * @param {String} sGroup the group of related DragDrop items
21162  * @param {object} config an object containing configurable attributes
21163  *                Valid properties for DD:
21164  *                    scroll
21165  */
21166 Roo.dd.DD = function(id, sGroup, config) {
21167     if (id) {
21168         this.init(id, sGroup, config);
21169     }
21170 };
21171
21172 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
21173
21174     /**
21175      * When set to true, the utility automatically tries to scroll the browser
21176      * window wehn a drag and drop element is dragged near the viewport boundary.
21177      * Defaults to true.
21178      * @property scroll
21179      * @type boolean
21180      */
21181     scroll: true,
21182
21183     /**
21184      * Sets the pointer offset to the distance between the linked element's top
21185      * left corner and the location the element was clicked
21186      * @method autoOffset
21187      * @param {int} iPageX the X coordinate of the click
21188      * @param {int} iPageY the Y coordinate of the click
21189      */
21190     autoOffset: function(iPageX, iPageY) {
21191         var x = iPageX - this.startPageX;
21192         var y = iPageY - this.startPageY;
21193         this.setDelta(x, y);
21194     },
21195
21196     /**
21197      * Sets the pointer offset.  You can call this directly to force the
21198      * offset to be in a particular location (e.g., pass in 0,0 to set it
21199      * to the center of the object)
21200      * @method setDelta
21201      * @param {int} iDeltaX the distance from the left
21202      * @param {int} iDeltaY the distance from the top
21203      */
21204     setDelta: function(iDeltaX, iDeltaY) {
21205         this.deltaX = iDeltaX;
21206         this.deltaY = iDeltaY;
21207     },
21208
21209     /**
21210      * Sets the drag element to the location of the mousedown or click event,
21211      * maintaining the cursor location relative to the location on the element
21212      * that was clicked.  Override this if you want to place the element in a
21213      * location other than where the cursor is.
21214      * @method setDragElPos
21215      * @param {int} iPageX the X coordinate of the mousedown or drag event
21216      * @param {int} iPageY the Y coordinate of the mousedown or drag event
21217      */
21218     setDragElPos: function(iPageX, iPageY) {
21219         // the first time we do this, we are going to check to make sure
21220         // the element has css positioning
21221
21222         var el = this.getDragEl();
21223         this.alignElWithMouse(el, iPageX, iPageY);
21224     },
21225
21226     /**
21227      * Sets the element to the location of the mousedown or click event,
21228      * maintaining the cursor location relative to the location on the element
21229      * that was clicked.  Override this if you want to place the element in a
21230      * location other than where the cursor is.
21231      * @method alignElWithMouse
21232      * @param {HTMLElement} el the element to move
21233      * @param {int} iPageX the X coordinate of the mousedown or drag event
21234      * @param {int} iPageY the Y coordinate of the mousedown or drag event
21235      */
21236     alignElWithMouse: function(el, iPageX, iPageY) {
21237         var oCoord = this.getTargetCoord(iPageX, iPageY);
21238         var fly = el.dom ? el : Roo.fly(el);
21239         if (!this.deltaSetXY) {
21240             var aCoord = [oCoord.x, oCoord.y];
21241             fly.setXY(aCoord);
21242             var newLeft = fly.getLeft(true);
21243             var newTop  = fly.getTop(true);
21244             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
21245         } else {
21246             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
21247         }
21248
21249         this.cachePosition(oCoord.x, oCoord.y);
21250         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
21251         return oCoord;
21252     },
21253
21254     /**
21255      * Saves the most recent position so that we can reset the constraints and
21256      * tick marks on-demand.  We need to know this so that we can calculate the
21257      * number of pixels the element is offset from its original position.
21258      * @method cachePosition
21259      * @param iPageX the current x position (optional, this just makes it so we
21260      * don't have to look it up again)
21261      * @param iPageY the current y position (optional, this just makes it so we
21262      * don't have to look it up again)
21263      */
21264     cachePosition: function(iPageX, iPageY) {
21265         if (iPageX) {
21266             this.lastPageX = iPageX;
21267             this.lastPageY = iPageY;
21268         } else {
21269             var aCoord = Roo.lib.Dom.getXY(this.getEl());
21270             this.lastPageX = aCoord[0];
21271             this.lastPageY = aCoord[1];
21272         }
21273     },
21274
21275     /**
21276      * Auto-scroll the window if the dragged object has been moved beyond the
21277      * visible window boundary.
21278      * @method autoScroll
21279      * @param {int} x the drag element's x position
21280      * @param {int} y the drag element's y position
21281      * @param {int} h the height of the drag element
21282      * @param {int} w the width of the drag element
21283      * @private
21284      */
21285     autoScroll: function(x, y, h, w) {
21286
21287         if (this.scroll) {
21288             // The client height
21289             var clientH = Roo.lib.Dom.getViewWidth();
21290
21291             // The client width
21292             var clientW = Roo.lib.Dom.getViewHeight();
21293
21294             // The amt scrolled down
21295             var st = this.DDM.getScrollTop();
21296
21297             // The amt scrolled right
21298             var sl = this.DDM.getScrollLeft();
21299
21300             // Location of the bottom of the element
21301             var bot = h + y;
21302
21303             // Location of the right of the element
21304             var right = w + x;
21305
21306             // The distance from the cursor to the bottom of the visible area,
21307             // adjusted so that we don't scroll if the cursor is beyond the
21308             // element drag constraints
21309             var toBot = (clientH + st - y - this.deltaY);
21310
21311             // The distance from the cursor to the right of the visible area
21312             var toRight = (clientW + sl - x - this.deltaX);
21313
21314
21315             // How close to the edge the cursor must be before we scroll
21316             // var thresh = (document.all) ? 100 : 40;
21317             var thresh = 40;
21318
21319             // How many pixels to scroll per autoscroll op.  This helps to reduce
21320             // clunky scrolling. IE is more sensitive about this ... it needs this
21321             // value to be higher.
21322             var scrAmt = (document.all) ? 80 : 30;
21323
21324             // Scroll down if we are near the bottom of the visible page and the
21325             // obj extends below the crease
21326             if ( bot > clientH && toBot < thresh ) {
21327                 window.scrollTo(sl, st + scrAmt);
21328             }
21329
21330             // Scroll up if the window is scrolled down and the top of the object
21331             // goes above the top border
21332             if ( y < st && st > 0 && y - st < thresh ) {
21333                 window.scrollTo(sl, st - scrAmt);
21334             }
21335
21336             // Scroll right if the obj is beyond the right border and the cursor is
21337             // near the border.
21338             if ( right > clientW && toRight < thresh ) {
21339                 window.scrollTo(sl + scrAmt, st);
21340             }
21341
21342             // Scroll left if the window has been scrolled to the right and the obj
21343             // extends past the left border
21344             if ( x < sl && sl > 0 && x - sl < thresh ) {
21345                 window.scrollTo(sl - scrAmt, st);
21346             }
21347         }
21348     },
21349
21350     /**
21351      * Finds the location the element should be placed if we want to move
21352      * it to where the mouse location less the click offset would place us.
21353      * @method getTargetCoord
21354      * @param {int} iPageX the X coordinate of the click
21355      * @param {int} iPageY the Y coordinate of the click
21356      * @return an object that contains the coordinates (Object.x and Object.y)
21357      * @private
21358      */
21359     getTargetCoord: function(iPageX, iPageY) {
21360
21361
21362         var x = iPageX - this.deltaX;
21363         var y = iPageY - this.deltaY;
21364
21365         if (this.constrainX) {
21366             if (x < this.minX) { x = this.minX; }
21367             if (x > this.maxX) { x = this.maxX; }
21368         }
21369
21370         if (this.constrainY) {
21371             if (y < this.minY) { y = this.minY; }
21372             if (y > this.maxY) { y = this.maxY; }
21373         }
21374
21375         x = this.getTick(x, this.xTicks);
21376         y = this.getTick(y, this.yTicks);
21377
21378
21379         return {x:x, y:y};
21380     },
21381
21382     /*
21383      * Sets up config options specific to this class. Overrides
21384      * Roo.dd.DragDrop, but all versions of this method through the
21385      * inheritance chain are called
21386      */
21387     applyConfig: function() {
21388         Roo.dd.DD.superclass.applyConfig.call(this);
21389         this.scroll = (this.config.scroll !== false);
21390     },
21391
21392     /*
21393      * Event that fires prior to the onMouseDown event.  Overrides
21394      * Roo.dd.DragDrop.
21395      */
21396     b4MouseDown: function(e) {
21397         // this.resetConstraints();
21398         this.autoOffset(e.getPageX(),
21399                             e.getPageY());
21400     },
21401
21402     /*
21403      * Event that fires prior to the onDrag event.  Overrides
21404      * Roo.dd.DragDrop.
21405      */
21406     b4Drag: function(e) {
21407         this.setDragElPos(e.getPageX(),
21408                             e.getPageY());
21409     },
21410
21411     toString: function() {
21412         return ("DD " + this.id);
21413     }
21414
21415     //////////////////////////////////////////////////////////////////////////
21416     // Debugging ygDragDrop events that can be overridden
21417     //////////////////////////////////////////////////////////////////////////
21418     /*
21419     startDrag: function(x, y) {
21420     },
21421
21422     onDrag: function(e) {
21423     },
21424
21425     onDragEnter: function(e, id) {
21426     },
21427
21428     onDragOver: function(e, id) {
21429     },
21430
21431     onDragOut: function(e, id) {
21432     },
21433
21434     onDragDrop: function(e, id) {
21435     },
21436
21437     endDrag: function(e) {
21438     }
21439
21440     */
21441
21442 });/*
21443  * Based on:
21444  * Ext JS Library 1.1.1
21445  * Copyright(c) 2006-2007, Ext JS, LLC.
21446  *
21447  * Originally Released Under LGPL - original licence link has changed is not relivant.
21448  *
21449  * Fork - LGPL
21450  * <script type="text/javascript">
21451  */
21452
21453 /**
21454  * @class Roo.dd.DDProxy
21455  * A DragDrop implementation that inserts an empty, bordered div into
21456  * the document that follows the cursor during drag operations.  At the time of
21457  * the click, the frame div is resized to the dimensions of the linked html
21458  * element, and moved to the exact location of the linked element.
21459  *
21460  * References to the "frame" element refer to the single proxy element that
21461  * was created to be dragged in place of all DDProxy elements on the
21462  * page.
21463  *
21464  * @extends Roo.dd.DD
21465  * @constructor
21466  * @param {String} id the id of the linked html element
21467  * @param {String} sGroup the group of related DragDrop objects
21468  * @param {object} config an object containing configurable attributes
21469  *                Valid properties for DDProxy in addition to those in DragDrop:
21470  *                   resizeFrame, centerFrame, dragElId
21471  */
21472 Roo.dd.DDProxy = function(id, sGroup, config) {
21473     if (id) {
21474         this.init(id, sGroup, config);
21475         this.initFrame();
21476     }
21477 };
21478
21479 /**
21480  * The default drag frame div id
21481  * @property Roo.dd.DDProxy.dragElId
21482  * @type String
21483  * @static
21484  */
21485 Roo.dd.DDProxy.dragElId = "ygddfdiv";
21486
21487 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
21488
21489     /**
21490      * By default we resize the drag frame to be the same size as the element
21491      * we want to drag (this is to get the frame effect).  We can turn it off
21492      * if we want a different behavior.
21493      * @property resizeFrame
21494      * @type boolean
21495      */
21496     resizeFrame: true,
21497
21498     /**
21499      * By default the frame is positioned exactly where the drag element is, so
21500      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
21501      * you do not have constraints on the obj is to have the drag frame centered
21502      * around the cursor.  Set centerFrame to true for this effect.
21503      * @property centerFrame
21504      * @type boolean
21505      */
21506     centerFrame: false,
21507
21508     /**
21509      * Creates the proxy element if it does not yet exist
21510      * @method createFrame
21511      */
21512     createFrame: function() {
21513         var self = this;
21514         var body = document.body;
21515
21516         if (!body || !body.firstChild) {
21517             setTimeout( function() { self.createFrame(); }, 50 );
21518             return;
21519         }
21520
21521         var div = this.getDragEl();
21522
21523         if (!div) {
21524             div    = document.createElement("div");
21525             div.id = this.dragElId;
21526             var s  = div.style;
21527
21528             s.position   = "absolute";
21529             s.visibility = "hidden";
21530             s.cursor     = "move";
21531             s.border     = "2px solid #aaa";
21532             s.zIndex     = 999;
21533
21534             // appendChild can blow up IE if invoked prior to the window load event
21535             // while rendering a table.  It is possible there are other scenarios
21536             // that would cause this to happen as well.
21537             body.insertBefore(div, body.firstChild);
21538         }
21539     },
21540
21541     /**
21542      * Initialization for the drag frame element.  Must be called in the
21543      * constructor of all subclasses
21544      * @method initFrame
21545      */
21546     initFrame: function() {
21547         this.createFrame();
21548     },
21549
21550     applyConfig: function() {
21551         Roo.dd.DDProxy.superclass.applyConfig.call(this);
21552
21553         this.resizeFrame = (this.config.resizeFrame !== false);
21554         this.centerFrame = (this.config.centerFrame);
21555         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
21556     },
21557
21558     /**
21559      * Resizes the drag frame to the dimensions of the clicked object, positions
21560      * it over the object, and finally displays it
21561      * @method showFrame
21562      * @param {int} iPageX X click position
21563      * @param {int} iPageY Y click position
21564      * @private
21565      */
21566     showFrame: function(iPageX, iPageY) {
21567         var el = this.getEl();
21568         var dragEl = this.getDragEl();
21569         var s = dragEl.style;
21570
21571         this._resizeProxy();
21572
21573         if (this.centerFrame) {
21574             this.setDelta( Math.round(parseInt(s.width,  10)/2),
21575                            Math.round(parseInt(s.height, 10)/2) );
21576         }
21577
21578         this.setDragElPos(iPageX, iPageY);
21579
21580         Roo.fly(dragEl).show();
21581     },
21582
21583     /**
21584      * The proxy is automatically resized to the dimensions of the linked
21585      * element when a drag is initiated, unless resizeFrame is set to false
21586      * @method _resizeProxy
21587      * @private
21588      */
21589     _resizeProxy: function() {
21590         if (this.resizeFrame) {
21591             var el = this.getEl();
21592             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
21593         }
21594     },
21595
21596     // overrides Roo.dd.DragDrop
21597     b4MouseDown: function(e) {
21598         var x = e.getPageX();
21599         var y = e.getPageY();
21600         this.autoOffset(x, y);
21601         this.setDragElPos(x, y);
21602     },
21603
21604     // overrides Roo.dd.DragDrop
21605     b4StartDrag: function(x, y) {
21606         // show the drag frame
21607         this.showFrame(x, y);
21608     },
21609
21610     // overrides Roo.dd.DragDrop
21611     b4EndDrag: function(e) {
21612         Roo.fly(this.getDragEl()).hide();
21613     },
21614
21615     // overrides Roo.dd.DragDrop
21616     // By default we try to move the element to the last location of the frame.
21617     // This is so that the default behavior mirrors that of Roo.dd.DD.
21618     endDrag: function(e) {
21619
21620         var lel = this.getEl();
21621         var del = this.getDragEl();
21622
21623         // Show the drag frame briefly so we can get its position
21624         del.style.visibility = "";
21625
21626         this.beforeMove();
21627         // Hide the linked element before the move to get around a Safari
21628         // rendering bug.
21629         lel.style.visibility = "hidden";
21630         Roo.dd.DDM.moveToEl(lel, del);
21631         del.style.visibility = "hidden";
21632         lel.style.visibility = "";
21633
21634         this.afterDrag();
21635     },
21636
21637     beforeMove : function(){
21638
21639     },
21640
21641     afterDrag : function(){
21642
21643     },
21644
21645     toString: function() {
21646         return ("DDProxy " + this.id);
21647     }
21648
21649 });
21650 /*
21651  * Based on:
21652  * Ext JS Library 1.1.1
21653  * Copyright(c) 2006-2007, Ext JS, LLC.
21654  *
21655  * Originally Released Under LGPL - original licence link has changed is not relivant.
21656  *
21657  * Fork - LGPL
21658  * <script type="text/javascript">
21659  */
21660
21661  /**
21662  * @class Roo.dd.DDTarget
21663  * A DragDrop implementation that does not move, but can be a drop
21664  * target.  You would get the same result by simply omitting implementation
21665  * for the event callbacks, but this way we reduce the processing cost of the
21666  * event listener and the callbacks.
21667  * @extends Roo.dd.DragDrop
21668  * @constructor
21669  * @param {String} id the id of the element that is a drop target
21670  * @param {String} sGroup the group of related DragDrop objects
21671  * @param {object} config an object containing configurable attributes
21672  *                 Valid properties for DDTarget in addition to those in
21673  *                 DragDrop:
21674  *                    none
21675  */
21676 Roo.dd.DDTarget = function(id, sGroup, config) {
21677     if (id) {
21678         this.initTarget(id, sGroup, config);
21679     }
21680     if (config && (config.listeners || config.events)) { 
21681         Roo.dd.DragDrop.superclass.constructor.call(this,  { 
21682             listeners : config.listeners || {}, 
21683             events : config.events || {} 
21684         });    
21685     }
21686 };
21687
21688 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
21689 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
21690     toString: function() {
21691         return ("DDTarget " + this.id);
21692     }
21693 });
21694 /*
21695  * Based on:
21696  * Ext JS Library 1.1.1
21697  * Copyright(c) 2006-2007, Ext JS, LLC.
21698  *
21699  * Originally Released Under LGPL - original licence link has changed is not relivant.
21700  *
21701  * Fork - LGPL
21702  * <script type="text/javascript">
21703  */
21704  
21705
21706 /**
21707  * @class Roo.dd.ScrollManager
21708  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
21709  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
21710  * @singleton
21711  */
21712 Roo.dd.ScrollManager = function(){
21713     var ddm = Roo.dd.DragDropMgr;
21714     var els = {};
21715     var dragEl = null;
21716     var proc = {};
21717     
21718     
21719     
21720     var onStop = function(e){
21721         dragEl = null;
21722         clearProc();
21723     };
21724     
21725     var triggerRefresh = function(){
21726         if(ddm.dragCurrent){
21727              ddm.refreshCache(ddm.dragCurrent.groups);
21728         }
21729     };
21730     
21731     var doScroll = function(){
21732         if(ddm.dragCurrent){
21733             var dds = Roo.dd.ScrollManager;
21734             if(!dds.animate){
21735                 if(proc.el.scroll(proc.dir, dds.increment)){
21736                     triggerRefresh();
21737                 }
21738             }else{
21739                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
21740             }
21741         }
21742     };
21743     
21744     var clearProc = function(){
21745         if(proc.id){
21746             clearInterval(proc.id);
21747         }
21748         proc.id = 0;
21749         proc.el = null;
21750         proc.dir = "";
21751     };
21752     
21753     var startProc = function(el, dir){
21754          Roo.log('scroll startproc');
21755         clearProc();
21756         proc.el = el;
21757         proc.dir = dir;
21758         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
21759     };
21760     
21761     var onFire = function(e, isDrop){
21762        
21763         if(isDrop || !ddm.dragCurrent){ return; }
21764         var dds = Roo.dd.ScrollManager;
21765         if(!dragEl || dragEl != ddm.dragCurrent){
21766             dragEl = ddm.dragCurrent;
21767             // refresh regions on drag start
21768             dds.refreshCache();
21769         }
21770         
21771         var xy = Roo.lib.Event.getXY(e);
21772         var pt = new Roo.lib.Point(xy[0], xy[1]);
21773         for(var id in els){
21774             var el = els[id], r = el._region;
21775             if(r && r.contains(pt) && el.isScrollable()){
21776                 if(r.bottom - pt.y <= dds.thresh){
21777                     if(proc.el != el){
21778                         startProc(el, "down");
21779                     }
21780                     return;
21781                 }else if(r.right - pt.x <= dds.thresh){
21782                     if(proc.el != el){
21783                         startProc(el, "left");
21784                     }
21785                     return;
21786                 }else if(pt.y - r.top <= dds.thresh){
21787                     if(proc.el != el){
21788                         startProc(el, "up");
21789                     }
21790                     return;
21791                 }else if(pt.x - r.left <= dds.thresh){
21792                     if(proc.el != el){
21793                         startProc(el, "right");
21794                     }
21795                     return;
21796                 }
21797             }
21798         }
21799         clearProc();
21800     };
21801     
21802     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
21803     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
21804     
21805     return {
21806         /**
21807          * Registers new overflow element(s) to auto scroll
21808          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
21809          */
21810         register : function(el){
21811             if(el instanceof Array){
21812                 for(var i = 0, len = el.length; i < len; i++) {
21813                         this.register(el[i]);
21814                 }
21815             }else{
21816                 el = Roo.get(el);
21817                 els[el.id] = el;
21818             }
21819             Roo.dd.ScrollManager.els = els;
21820         },
21821         
21822         /**
21823          * Unregisters overflow element(s) so they are no longer scrolled
21824          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
21825          */
21826         unregister : function(el){
21827             if(el instanceof Array){
21828                 for(var i = 0, len = el.length; i < len; i++) {
21829                         this.unregister(el[i]);
21830                 }
21831             }else{
21832                 el = Roo.get(el);
21833                 delete els[el.id];
21834             }
21835         },
21836         
21837         /**
21838          * The number of pixels from the edge of a container the pointer needs to be to 
21839          * trigger scrolling (defaults to 25)
21840          * @type Number
21841          */
21842         thresh : 25,
21843         
21844         /**
21845          * The number of pixels to scroll in each scroll increment (defaults to 50)
21846          * @type Number
21847          */
21848         increment : 100,
21849         
21850         /**
21851          * The frequency of scrolls in milliseconds (defaults to 500)
21852          * @type Number
21853          */
21854         frequency : 500,
21855         
21856         /**
21857          * True to animate the scroll (defaults to true)
21858          * @type Boolean
21859          */
21860         animate: true,
21861         
21862         /**
21863          * The animation duration in seconds - 
21864          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
21865          * @type Number
21866          */
21867         animDuration: .4,
21868         
21869         /**
21870          * Manually trigger a cache refresh.
21871          */
21872         refreshCache : function(){
21873             for(var id in els){
21874                 if(typeof els[id] == 'object'){ // for people extending the object prototype
21875                     els[id]._region = els[id].getRegion();
21876                 }
21877             }
21878         }
21879     };
21880 }();/*
21881  * Based on:
21882  * Ext JS Library 1.1.1
21883  * Copyright(c) 2006-2007, Ext JS, LLC.
21884  *
21885  * Originally Released Under LGPL - original licence link has changed is not relivant.
21886  *
21887  * Fork - LGPL
21888  * <script type="text/javascript">
21889  */
21890  
21891
21892 /**
21893  * @class Roo.dd.Registry
21894  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
21895  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
21896  * @singleton
21897  */
21898 Roo.dd.Registry = function(){
21899     var elements = {}; 
21900     var handles = {}; 
21901     var autoIdSeed = 0;
21902
21903     var getId = function(el, autogen){
21904         if(typeof el == "string"){
21905             return el;
21906         }
21907         var id = el.id;
21908         if(!id && autogen !== false){
21909             id = "roodd-" + (++autoIdSeed);
21910             el.id = id;
21911         }
21912         return id;
21913     };
21914     
21915     return {
21916     /**
21917      * Register a drag drop element
21918      * @param {String|HTMLElement} element The id or DOM node to register
21919      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
21920      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
21921      * knows how to interpret, plus there are some specific properties known to the Registry that should be
21922      * populated in the data object (if applicable):
21923      * <pre>
21924 Value      Description<br />
21925 ---------  ------------------------------------------<br />
21926 handles    Array of DOM nodes that trigger dragging<br />
21927            for the element being registered<br />
21928 isHandle   True if the element passed in triggers<br />
21929            dragging itself, else false
21930 </pre>
21931      */
21932         register : function(el, data){
21933             data = data || {};
21934             if(typeof el == "string"){
21935                 el = document.getElementById(el);
21936             }
21937             data.ddel = el;
21938             elements[getId(el)] = data;
21939             if(data.isHandle !== false){
21940                 handles[data.ddel.id] = data;
21941             }
21942             if(data.handles){
21943                 var hs = data.handles;
21944                 for(var i = 0, len = hs.length; i < len; i++){
21945                         handles[getId(hs[i])] = data;
21946                 }
21947             }
21948         },
21949
21950     /**
21951      * Unregister a drag drop element
21952      * @param {String|HTMLElement}  element The id or DOM node to unregister
21953      */
21954         unregister : function(el){
21955             var id = getId(el, false);
21956             var data = elements[id];
21957             if(data){
21958                 delete elements[id];
21959                 if(data.handles){
21960                     var hs = data.handles;
21961                     for(var i = 0, len = hs.length; i < len; i++){
21962                         delete handles[getId(hs[i], false)];
21963                     }
21964                 }
21965             }
21966         },
21967
21968     /**
21969      * Returns the handle registered for a DOM Node by id
21970      * @param {String|HTMLElement} id The DOM node or id to look up
21971      * @return {Object} handle The custom handle data
21972      */
21973         getHandle : function(id){
21974             if(typeof id != "string"){ // must be element?
21975                 id = id.id;
21976             }
21977             return handles[id];
21978         },
21979
21980     /**
21981      * Returns the handle that is registered for the DOM node that is the target of the event
21982      * @param {Event} e The event
21983      * @return {Object} handle The custom handle data
21984      */
21985         getHandleFromEvent : function(e){
21986             var t = Roo.lib.Event.getTarget(e);
21987             return t ? handles[t.id] : null;
21988         },
21989
21990     /**
21991      * Returns a custom data object that is registered for a DOM node by id
21992      * @param {String|HTMLElement} id The DOM node or id to look up
21993      * @return {Object} data The custom data
21994      */
21995         getTarget : function(id){
21996             if(typeof id != "string"){ // must be element?
21997                 id = id.id;
21998             }
21999             return elements[id];
22000         },
22001
22002     /**
22003      * Returns a custom data object that is registered for the DOM node that is the target of the event
22004      * @param {Event} e The event
22005      * @return {Object} data The custom data
22006      */
22007         getTargetFromEvent : function(e){
22008             var t = Roo.lib.Event.getTarget(e);
22009             return t ? elements[t.id] || handles[t.id] : null;
22010         }
22011     };
22012 }();/*
22013  * Based on:
22014  * Ext JS Library 1.1.1
22015  * Copyright(c) 2006-2007, Ext JS, LLC.
22016  *
22017  * Originally Released Under LGPL - original licence link has changed is not relivant.
22018  *
22019  * Fork - LGPL
22020  * <script type="text/javascript">
22021  */
22022  
22023
22024 /**
22025  * @class Roo.dd.StatusProxy
22026  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
22027  * default drag proxy used by all Roo.dd components.
22028  * @constructor
22029  * @param {Object} config
22030  */
22031 Roo.dd.StatusProxy = function(config){
22032     Roo.apply(this, config);
22033     this.id = this.id || Roo.id();
22034     this.el = new Roo.Layer({
22035         dh: {
22036             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
22037                 {tag: "div", cls: "x-dd-drop-icon"},
22038                 {tag: "div", cls: "x-dd-drag-ghost"}
22039             ]
22040         }, 
22041         shadow: !config || config.shadow !== false
22042     });
22043     this.ghost = Roo.get(this.el.dom.childNodes[1]);
22044     this.dropStatus = this.dropNotAllowed;
22045 };
22046
22047 Roo.dd.StatusProxy.prototype = {
22048     /**
22049      * @cfg {String} dropAllowed
22050      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
22051      */
22052     dropAllowed : "x-dd-drop-ok",
22053     /**
22054      * @cfg {String} dropNotAllowed
22055      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
22056      */
22057     dropNotAllowed : "x-dd-drop-nodrop",
22058
22059     /**
22060      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
22061      * over the current target element.
22062      * @param {String} cssClass The css class for the new drop status indicator image
22063      */
22064     setStatus : function(cssClass){
22065         cssClass = cssClass || this.dropNotAllowed;
22066         if(this.dropStatus != cssClass){
22067             this.el.replaceClass(this.dropStatus, cssClass);
22068             this.dropStatus = cssClass;
22069         }
22070     },
22071
22072     /**
22073      * Resets the status indicator to the default dropNotAllowed value
22074      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
22075      */
22076     reset : function(clearGhost){
22077         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
22078         this.dropStatus = this.dropNotAllowed;
22079         if(clearGhost){
22080             this.ghost.update("");
22081         }
22082     },
22083
22084     /**
22085      * Updates the contents of the ghost element
22086      * @param {String} html The html that will replace the current innerHTML of the ghost element
22087      */
22088     update : function(html){
22089         if(typeof html == "string"){
22090             this.ghost.update(html);
22091         }else{
22092             this.ghost.update("");
22093             html.style.margin = "0";
22094             this.ghost.dom.appendChild(html);
22095         }
22096         // ensure float = none set?? cant remember why though.
22097         var el = this.ghost.dom.firstChild;
22098                 if(el){
22099                         Roo.fly(el).setStyle('float', 'none');
22100                 }
22101     },
22102     
22103     /**
22104      * Returns the underlying proxy {@link Roo.Layer}
22105      * @return {Roo.Layer} el
22106     */
22107     getEl : function(){
22108         return this.el;
22109     },
22110
22111     /**
22112      * Returns the ghost element
22113      * @return {Roo.Element} el
22114      */
22115     getGhost : function(){
22116         return this.ghost;
22117     },
22118
22119     /**
22120      * Hides the proxy
22121      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
22122      */
22123     hide : function(clear){
22124         this.el.hide();
22125         if(clear){
22126             this.reset(true);
22127         }
22128     },
22129
22130     /**
22131      * Stops the repair animation if it's currently running
22132      */
22133     stop : function(){
22134         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
22135             this.anim.stop();
22136         }
22137     },
22138
22139     /**
22140      * Displays this proxy
22141      */
22142     show : function(){
22143         this.el.show();
22144     },
22145
22146     /**
22147      * Force the Layer to sync its shadow and shim positions to the element
22148      */
22149     sync : function(){
22150         this.el.sync();
22151     },
22152
22153     /**
22154      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
22155      * invalid drop operation by the item being dragged.
22156      * @param {Array} xy The XY position of the element ([x, y])
22157      * @param {Function} callback The function to call after the repair is complete
22158      * @param {Object} scope The scope in which to execute the callback
22159      */
22160     repair : function(xy, callback, scope){
22161         this.callback = callback;
22162         this.scope = scope;
22163         if(xy && this.animRepair !== false){
22164             this.el.addClass("x-dd-drag-repair");
22165             this.el.hideUnders(true);
22166             this.anim = this.el.shift({
22167                 duration: this.repairDuration || .5,
22168                 easing: 'easeOut',
22169                 xy: xy,
22170                 stopFx: true,
22171                 callback: this.afterRepair,
22172                 scope: this
22173             });
22174         }else{
22175             this.afterRepair();
22176         }
22177     },
22178
22179     // private
22180     afterRepair : function(){
22181         this.hide(true);
22182         if(typeof this.callback == "function"){
22183             this.callback.call(this.scope || this);
22184         }
22185         this.callback = null;
22186         this.scope = null;
22187     }
22188 };/*
22189  * Based on:
22190  * Ext JS Library 1.1.1
22191  * Copyright(c) 2006-2007, Ext JS, LLC.
22192  *
22193  * Originally Released Under LGPL - original licence link has changed is not relivant.
22194  *
22195  * Fork - LGPL
22196  * <script type="text/javascript">
22197  */
22198
22199 /**
22200  * @class Roo.dd.DragSource
22201  * @extends Roo.dd.DDProxy
22202  * A simple class that provides the basic implementation needed to make any element draggable.
22203  * @constructor
22204  * @param {String/HTMLElement/Element} el The container element
22205  * @param {Object} config
22206  */
22207 Roo.dd.DragSource = function(el, config){
22208     this.el = Roo.get(el);
22209     this.dragData = {};
22210     
22211     Roo.apply(this, config);
22212     
22213     if(!this.proxy){
22214         this.proxy = new Roo.dd.StatusProxy();
22215     }
22216
22217     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
22218           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
22219     
22220     this.dragging = false;
22221 };
22222
22223 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
22224     /**
22225      * @cfg {String} dropAllowed
22226      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
22227      */
22228     dropAllowed : "x-dd-drop-ok",
22229     /**
22230      * @cfg {String} dropNotAllowed
22231      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
22232      */
22233     dropNotAllowed : "x-dd-drop-nodrop",
22234
22235     /**
22236      * Returns the data object associated with this drag source
22237      * @return {Object} data An object containing arbitrary data
22238      */
22239     getDragData : function(e){
22240         return this.dragData;
22241     },
22242
22243     // private
22244     onDragEnter : function(e, id){
22245         var target = Roo.dd.DragDropMgr.getDDById(id);
22246         this.cachedTarget = target;
22247         if(this.beforeDragEnter(target, e, id) !== false){
22248             if(target.isNotifyTarget){
22249                 var status = target.notifyEnter(this, e, this.dragData);
22250                 this.proxy.setStatus(status);
22251             }else{
22252                 this.proxy.setStatus(this.dropAllowed);
22253             }
22254             
22255             if(this.afterDragEnter){
22256                 /**
22257                  * An empty function by default, but provided so that you can perform a custom action
22258                  * when the dragged item enters the drop target by providing an implementation.
22259                  * @param {Roo.dd.DragDrop} target The drop target
22260                  * @param {Event} e The event object
22261                  * @param {String} id The id of the dragged element
22262                  * @method afterDragEnter
22263                  */
22264                 this.afterDragEnter(target, e, id);
22265             }
22266         }
22267     },
22268
22269     /**
22270      * An empty function by default, but provided so that you can perform a custom action
22271      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
22272      * @param {Roo.dd.DragDrop} target The drop target
22273      * @param {Event} e The event object
22274      * @param {String} id The id of the dragged element
22275      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
22276      */
22277     beforeDragEnter : function(target, e, id){
22278         return true;
22279     },
22280
22281     // private
22282     alignElWithMouse: function() {
22283         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
22284         this.proxy.sync();
22285     },
22286
22287     // private
22288     onDragOver : function(e, id){
22289         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
22290         if(this.beforeDragOver(target, e, id) !== false){
22291             if(target.isNotifyTarget){
22292                 var status = target.notifyOver(this, e, this.dragData);
22293                 this.proxy.setStatus(status);
22294             }
22295
22296             if(this.afterDragOver){
22297                 /**
22298                  * An empty function by default, but provided so that you can perform a custom action
22299                  * while the dragged item is over the drop target by providing an implementation.
22300                  * @param {Roo.dd.DragDrop} target The drop target
22301                  * @param {Event} e The event object
22302                  * @param {String} id The id of the dragged element
22303                  * @method afterDragOver
22304                  */
22305                 this.afterDragOver(target, e, id);
22306             }
22307         }
22308     },
22309
22310     /**
22311      * An empty function by default, but provided so that you can perform a custom action
22312      * while the dragged item is over the drop target and optionally cancel the onDragOver.
22313      * @param {Roo.dd.DragDrop} target The drop target
22314      * @param {Event} e The event object
22315      * @param {String} id The id of the dragged element
22316      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
22317      */
22318     beforeDragOver : function(target, e, id){
22319         return true;
22320     },
22321
22322     // private
22323     onDragOut : function(e, id){
22324         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
22325         if(this.beforeDragOut(target, e, id) !== false){
22326             if(target.isNotifyTarget){
22327                 target.notifyOut(this, e, this.dragData);
22328             }
22329             this.proxy.reset();
22330             if(this.afterDragOut){
22331                 /**
22332                  * An empty function by default, but provided so that you can perform a custom action
22333                  * after the dragged item is dragged out of the target without dropping.
22334                  * @param {Roo.dd.DragDrop} target The drop target
22335                  * @param {Event} e The event object
22336                  * @param {String} id The id of the dragged element
22337                  * @method afterDragOut
22338                  */
22339                 this.afterDragOut(target, e, id);
22340             }
22341         }
22342         this.cachedTarget = null;
22343     },
22344
22345     /**
22346      * An empty function by default, but provided so that you can perform a custom action before the dragged
22347      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
22348      * @param {Roo.dd.DragDrop} target The drop target
22349      * @param {Event} e The event object
22350      * @param {String} id The id of the dragged element
22351      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
22352      */
22353     beforeDragOut : function(target, e, id){
22354         return true;
22355     },
22356     
22357     // private
22358     onDragDrop : function(e, id){
22359         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
22360         if(this.beforeDragDrop(target, e, id) !== false){
22361             if(target.isNotifyTarget){
22362                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
22363                     this.onValidDrop(target, e, id);
22364                 }else{
22365                     this.onInvalidDrop(target, e, id);
22366                 }
22367             }else{
22368                 this.onValidDrop(target, e, id);
22369             }
22370             
22371             if(this.afterDragDrop){
22372                 /**
22373                  * An empty function by default, but provided so that you can perform a custom action
22374                  * after a valid drag drop has occurred by providing an implementation.
22375                  * @param {Roo.dd.DragDrop} target The drop target
22376                  * @param {Event} e The event object
22377                  * @param {String} id The id of the dropped element
22378                  * @method afterDragDrop
22379                  */
22380                 this.afterDragDrop(target, e, id);
22381             }
22382         }
22383         delete this.cachedTarget;
22384     },
22385
22386     /**
22387      * An empty function by default, but provided so that you can perform a custom action before the dragged
22388      * item is dropped onto the target and optionally cancel the onDragDrop.
22389      * @param {Roo.dd.DragDrop} target The drop target
22390      * @param {Event} e The event object
22391      * @param {String} id The id of the dragged element
22392      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
22393      */
22394     beforeDragDrop : function(target, e, id){
22395         return true;
22396     },
22397
22398     // private
22399     onValidDrop : function(target, e, id){
22400         this.hideProxy();
22401         if(this.afterValidDrop){
22402             /**
22403              * An empty function by default, but provided so that you can perform a custom action
22404              * after a valid drop has occurred by providing an implementation.
22405              * @param {Object} target The target DD 
22406              * @param {Event} e The event object
22407              * @param {String} id The id of the dropped element
22408              * @method afterInvalidDrop
22409              */
22410             this.afterValidDrop(target, e, id);
22411         }
22412     },
22413
22414     // private
22415     getRepairXY : function(e, data){
22416         return this.el.getXY();  
22417     },
22418
22419     // private
22420     onInvalidDrop : function(target, e, id){
22421         this.beforeInvalidDrop(target, e, id);
22422         if(this.cachedTarget){
22423             if(this.cachedTarget.isNotifyTarget){
22424                 this.cachedTarget.notifyOut(this, e, this.dragData);
22425             }
22426             this.cacheTarget = null;
22427         }
22428         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
22429
22430         if(this.afterInvalidDrop){
22431             /**
22432              * An empty function by default, but provided so that you can perform a custom action
22433              * after an invalid drop has occurred by providing an implementation.
22434              * @param {Event} e The event object
22435              * @param {String} id The id of the dropped element
22436              * @method afterInvalidDrop
22437              */
22438             this.afterInvalidDrop(e, id);
22439         }
22440     },
22441
22442     // private
22443     afterRepair : function(){
22444         if(Roo.enableFx){
22445             this.el.highlight(this.hlColor || "c3daf9");
22446         }
22447         this.dragging = false;
22448     },
22449
22450     /**
22451      * An empty function by default, but provided so that you can perform a custom action after an invalid
22452      * drop has occurred.
22453      * @param {Roo.dd.DragDrop} target The drop target
22454      * @param {Event} e The event object
22455      * @param {String} id The id of the dragged element
22456      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
22457      */
22458     beforeInvalidDrop : function(target, e, id){
22459         return true;
22460     },
22461
22462     // private
22463     handleMouseDown : function(e){
22464         if(this.dragging) {
22465             return;
22466         }
22467         var data = this.getDragData(e);
22468         if(data && this.onBeforeDrag(data, e) !== false){
22469             this.dragData = data;
22470             this.proxy.stop();
22471             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
22472         } 
22473     },
22474
22475     /**
22476      * An empty function by default, but provided so that you can perform a custom action before the initial
22477      * drag event begins and optionally cancel it.
22478      * @param {Object} data An object containing arbitrary data to be shared with drop targets
22479      * @param {Event} e The event object
22480      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
22481      */
22482     onBeforeDrag : function(data, e){
22483         return true;
22484     },
22485
22486     /**
22487      * An empty function by default, but provided so that you can perform a custom action once the initial
22488      * drag event has begun.  The drag cannot be canceled from this function.
22489      * @param {Number} x The x position of the click on the dragged object
22490      * @param {Number} y The y position of the click on the dragged object
22491      */
22492     onStartDrag : Roo.emptyFn,
22493
22494     // private - YUI override
22495     startDrag : function(x, y){
22496         this.proxy.reset();
22497         this.dragging = true;
22498         this.proxy.update("");
22499         this.onInitDrag(x, y);
22500         this.proxy.show();
22501     },
22502
22503     // private
22504     onInitDrag : function(x, y){
22505         var clone = this.el.dom.cloneNode(true);
22506         clone.id = Roo.id(); // prevent duplicate ids
22507         this.proxy.update(clone);
22508         this.onStartDrag(x, y);
22509         return true;
22510     },
22511
22512     /**
22513      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
22514      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
22515      */
22516     getProxy : function(){
22517         return this.proxy;  
22518     },
22519
22520     /**
22521      * Hides the drag source's {@link Roo.dd.StatusProxy}
22522      */
22523     hideProxy : function(){
22524         this.proxy.hide();  
22525         this.proxy.reset(true);
22526         this.dragging = false;
22527     },
22528
22529     // private
22530     triggerCacheRefresh : function(){
22531         Roo.dd.DDM.refreshCache(this.groups);
22532     },
22533
22534     // private - override to prevent hiding
22535     b4EndDrag: function(e) {
22536     },
22537
22538     // private - override to prevent moving
22539     endDrag : function(e){
22540         this.onEndDrag(this.dragData, e);
22541     },
22542
22543     // private
22544     onEndDrag : function(data, e){
22545     },
22546     
22547     // private - pin to cursor
22548     autoOffset : function(x, y) {
22549         this.setDelta(-12, -20);
22550     }    
22551 });/*
22552  * Based on:
22553  * Ext JS Library 1.1.1
22554  * Copyright(c) 2006-2007, Ext JS, LLC.
22555  *
22556  * Originally Released Under LGPL - original licence link has changed is not relivant.
22557  *
22558  * Fork - LGPL
22559  * <script type="text/javascript">
22560  */
22561
22562
22563 /**
22564  * @class Roo.dd.DropTarget
22565  * @extends Roo.dd.DDTarget
22566  * A simple class that provides the basic implementation needed to make any element a drop target that can have
22567  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
22568  * @constructor
22569  * @param {String/HTMLElement/Element} el The container element
22570  * @param {Object} config
22571  */
22572 Roo.dd.DropTarget = function(el, config){
22573     this.el = Roo.get(el);
22574     
22575     var listeners = false; ;
22576     if (config && config.listeners) {
22577         listeners= config.listeners;
22578         delete config.listeners;
22579     }
22580     Roo.apply(this, config);
22581     
22582     if(this.containerScroll){
22583         Roo.dd.ScrollManager.register(this.el);
22584     }
22585     this.addEvents( {
22586          /**
22587          * @scope Roo.dd.DropTarget
22588          */
22589          
22590          /**
22591          * @event enter
22592          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
22593          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
22594          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
22595          * 
22596          * IMPORTANT : it should set  this.valid to true|false
22597          * 
22598          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
22599          * @param {Event} e The event
22600          * @param {Object} data An object containing arbitrary data supplied by the drag source
22601          */
22602         "enter" : true,
22603         
22604          /**
22605          * @event over
22606          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
22607          * This method will be called on every mouse movement while the drag source is over the drop target.
22608          * This default implementation simply returns the dropAllowed config value.
22609          * 
22610          * IMPORTANT : it should set  this.valid to true|false
22611          * 
22612          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
22613          * @param {Event} e The event
22614          * @param {Object} data An object containing arbitrary data supplied by the drag source
22615          
22616          */
22617         "over" : true,
22618         /**
22619          * @event out
22620          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
22621          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
22622          * overClass (if any) from the drop element.
22623          * 
22624          * 
22625          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
22626          * @param {Event} e The event
22627          * @param {Object} data An object containing arbitrary data supplied by the drag source
22628          */
22629          "out" : true,
22630          
22631         /**
22632          * @event drop
22633          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
22634          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
22635          * implementation that does something to process the drop event and returns true so that the drag source's
22636          * repair action does not run.
22637          * 
22638          * IMPORTANT : it should set this.success
22639          * 
22640          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
22641          * @param {Event} e The event
22642          * @param {Object} data An object containing arbitrary data supplied by the drag source
22643         */
22644          "drop" : true
22645     });
22646             
22647      
22648     Roo.dd.DropTarget.superclass.constructor.call(  this, 
22649         this.el.dom, 
22650         this.ddGroup || this.group,
22651         {
22652             isTarget: true,
22653             listeners : listeners || {} 
22654            
22655         
22656         }
22657     );
22658
22659 };
22660
22661 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
22662     /**
22663      * @cfg {String} overClass
22664      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
22665      */
22666      /**
22667      * @cfg {String} ddGroup
22668      * The drag drop group to handle drop events for
22669      */
22670      
22671     /**
22672      * @cfg {String} dropAllowed
22673      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
22674      */
22675     dropAllowed : "x-dd-drop-ok",
22676     /**
22677      * @cfg {String} dropNotAllowed
22678      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
22679      */
22680     dropNotAllowed : "x-dd-drop-nodrop",
22681     /**
22682      * @cfg {boolean} success
22683      * set this after drop listener.. 
22684      */
22685     success : false,
22686     /**
22687      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
22688      * if the drop point is valid for over/enter..
22689      */
22690     valid : false,
22691     // private
22692     isTarget : true,
22693
22694     // private
22695     isNotifyTarget : true,
22696     
22697     /**
22698      * @hide
22699      */
22700     notifyEnter : function(dd, e, data)
22701     {
22702         this.valid = true;
22703         this.fireEvent('enter', dd, e, data);
22704         if(this.overClass){
22705             this.el.addClass(this.overClass);
22706         }
22707         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
22708             this.valid ? this.dropAllowed : this.dropNotAllowed
22709         );
22710     },
22711
22712     /**
22713      * @hide
22714      */
22715     notifyOver : function(dd, e, data)
22716     {
22717         this.valid = true;
22718         this.fireEvent('over', dd, e, data);
22719         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
22720             this.valid ? this.dropAllowed : this.dropNotAllowed
22721         );
22722     },
22723
22724     /**
22725      * @hide
22726      */
22727     notifyOut : function(dd, e, data)
22728     {
22729         this.fireEvent('out', dd, e, data);
22730         if(this.overClass){
22731             this.el.removeClass(this.overClass);
22732         }
22733     },
22734
22735     /**
22736      * @hide
22737      */
22738     notifyDrop : function(dd, e, data)
22739     {
22740         this.success = false;
22741         this.fireEvent('drop', dd, e, data);
22742         return this.success;
22743     }
22744 });/*
22745  * Based on:
22746  * Ext JS Library 1.1.1
22747  * Copyright(c) 2006-2007, Ext JS, LLC.
22748  *
22749  * Originally Released Under LGPL - original licence link has changed is not relivant.
22750  *
22751  * Fork - LGPL
22752  * <script type="text/javascript">
22753  */
22754
22755
22756 /**
22757  * @class Roo.dd.DragZone
22758  * @extends Roo.dd.DragSource
22759  * This class provides a container DD instance that proxies for multiple child node sources.<br />
22760  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
22761  * @constructor
22762  * @param {String/HTMLElement/Element} el The container element
22763  * @param {Object} config
22764  */
22765 Roo.dd.DragZone = function(el, config){
22766     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
22767     if(this.containerScroll){
22768         Roo.dd.ScrollManager.register(this.el);
22769     }
22770 };
22771
22772 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
22773     /**
22774      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
22775      * for auto scrolling during drag operations.
22776      */
22777     /**
22778      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
22779      * method after a failed drop (defaults to "c3daf9" - light blue)
22780      */
22781
22782     /**
22783      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
22784      * for a valid target to drag based on the mouse down. Override this method
22785      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
22786      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
22787      * @param {EventObject} e The mouse down event
22788      * @return {Object} The dragData
22789      */
22790     getDragData : function(e){
22791         return Roo.dd.Registry.getHandleFromEvent(e);
22792     },
22793     
22794     /**
22795      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
22796      * this.dragData.ddel
22797      * @param {Number} x The x position of the click on the dragged object
22798      * @param {Number} y The y position of the click on the dragged object
22799      * @return {Boolean} true to continue the drag, false to cancel
22800      */
22801     onInitDrag : function(x, y){
22802         this.proxy.update(this.dragData.ddel.cloneNode(true));
22803         this.onStartDrag(x, y);
22804         return true;
22805     },
22806     
22807     /**
22808      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
22809      */
22810     afterRepair : function(){
22811         if(Roo.enableFx){
22812             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
22813         }
22814         this.dragging = false;
22815     },
22816
22817     /**
22818      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
22819      * the XY of this.dragData.ddel
22820      * @param {EventObject} e The mouse up event
22821      * @return {Array} The xy location (e.g. [100, 200])
22822      */
22823     getRepairXY : function(e){
22824         return Roo.Element.fly(this.dragData.ddel).getXY();  
22825     }
22826 });/*
22827  * Based on:
22828  * Ext JS Library 1.1.1
22829  * Copyright(c) 2006-2007, Ext JS, LLC.
22830  *
22831  * Originally Released Under LGPL - original licence link has changed is not relivant.
22832  *
22833  * Fork - LGPL
22834  * <script type="text/javascript">
22835  */
22836 /**
22837  * @class Roo.dd.DropZone
22838  * @extends Roo.dd.DropTarget
22839  * This class provides a container DD instance that proxies for multiple child node targets.<br />
22840  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
22841  * @constructor
22842  * @param {String/HTMLElement/Element} el The container element
22843  * @param {Object} config
22844  */
22845 Roo.dd.DropZone = function(el, config){
22846     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
22847 };
22848
22849 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
22850     /**
22851      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
22852      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
22853      * provide your own custom lookup.
22854      * @param {Event} e The event
22855      * @return {Object} data The custom data
22856      */
22857     getTargetFromEvent : function(e){
22858         return Roo.dd.Registry.getTargetFromEvent(e);
22859     },
22860
22861     /**
22862      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
22863      * that it has registered.  This method has no default implementation and should be overridden to provide
22864      * node-specific processing if necessary.
22865      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
22866      * {@link #getTargetFromEvent} for this node)
22867      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22868      * @param {Event} e The event
22869      * @param {Object} data An object containing arbitrary data supplied by the drag source
22870      */
22871     onNodeEnter : function(n, dd, e, data){
22872         
22873     },
22874
22875     /**
22876      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
22877      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
22878      * overridden to provide the proper feedback.
22879      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
22880      * {@link #getTargetFromEvent} for this node)
22881      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22882      * @param {Event} e The event
22883      * @param {Object} data An object containing arbitrary data supplied by the drag source
22884      * @return {String} status The CSS class that communicates the drop status back to the source so that the
22885      * underlying {@link Roo.dd.StatusProxy} can be updated
22886      */
22887     onNodeOver : function(n, dd, e, data){
22888         return this.dropAllowed;
22889     },
22890
22891     /**
22892      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
22893      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
22894      * node-specific processing if necessary.
22895      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
22896      * {@link #getTargetFromEvent} for this node)
22897      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22898      * @param {Event} e The event
22899      * @param {Object} data An object containing arbitrary data supplied by the drag source
22900      */
22901     onNodeOut : function(n, dd, e, data){
22902         
22903     },
22904
22905     /**
22906      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
22907      * the drop node.  The default implementation returns false, so it should be overridden to provide the
22908      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
22909      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
22910      * {@link #getTargetFromEvent} for this node)
22911      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22912      * @param {Event} e The event
22913      * @param {Object} data An object containing arbitrary data supplied by the drag source
22914      * @return {Boolean} True if the drop was valid, else false
22915      */
22916     onNodeDrop : function(n, dd, e, data){
22917         return false;
22918     },
22919
22920     /**
22921      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
22922      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
22923      * it should be overridden to provide the proper feedback if necessary.
22924      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22925      * @param {Event} e The event
22926      * @param {Object} data An object containing arbitrary data supplied by the drag source
22927      * @return {String} status The CSS class that communicates the drop status back to the source so that the
22928      * underlying {@link Roo.dd.StatusProxy} can be updated
22929      */
22930     onContainerOver : function(dd, e, data){
22931         return this.dropNotAllowed;
22932     },
22933
22934     /**
22935      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
22936      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
22937      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
22938      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
22939      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22940      * @param {Event} e The event
22941      * @param {Object} data An object containing arbitrary data supplied by the drag source
22942      * @return {Boolean} True if the drop was valid, else false
22943      */
22944     onContainerDrop : function(dd, e, data){
22945         return false;
22946     },
22947
22948     /**
22949      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
22950      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
22951      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
22952      * you should override this method and provide a custom implementation.
22953      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22954      * @param {Event} e The event
22955      * @param {Object} data An object containing arbitrary data supplied by the drag source
22956      * @return {String} status The CSS class that communicates the drop status back to the source so that the
22957      * underlying {@link Roo.dd.StatusProxy} can be updated
22958      */
22959     notifyEnter : function(dd, e, data){
22960         return this.dropNotAllowed;
22961     },
22962
22963     /**
22964      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
22965      * This method will be called on every mouse movement while the drag source is over the drop zone.
22966      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
22967      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
22968      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
22969      * registered node, it will call {@link #onContainerOver}.
22970      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22971      * @param {Event} e The event
22972      * @param {Object} data An object containing arbitrary data supplied by the drag source
22973      * @return {String} status The CSS class that communicates the drop status back to the source so that the
22974      * underlying {@link Roo.dd.StatusProxy} can be updated
22975      */
22976     notifyOver : function(dd, e, data){
22977         var n = this.getTargetFromEvent(e);
22978         if(!n){ // not over valid drop target
22979             if(this.lastOverNode){
22980                 this.onNodeOut(this.lastOverNode, dd, e, data);
22981                 this.lastOverNode = null;
22982             }
22983             return this.onContainerOver(dd, e, data);
22984         }
22985         if(this.lastOverNode != n){
22986             if(this.lastOverNode){
22987                 this.onNodeOut(this.lastOverNode, dd, e, data);
22988             }
22989             this.onNodeEnter(n, dd, e, data);
22990             this.lastOverNode = n;
22991         }
22992         return this.onNodeOver(n, dd, e, data);
22993     },
22994
22995     /**
22996      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
22997      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
22998      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
22999      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
23000      * @param {Event} e The event
23001      * @param {Object} data An object containing arbitrary data supplied by the drag zone
23002      */
23003     notifyOut : function(dd, e, data){
23004         if(this.lastOverNode){
23005             this.onNodeOut(this.lastOverNode, dd, e, data);
23006             this.lastOverNode = null;
23007         }
23008     },
23009
23010     /**
23011      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
23012      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
23013      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
23014      * otherwise it will call {@link #onContainerDrop}.
23015      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23016      * @param {Event} e The event
23017      * @param {Object} data An object containing arbitrary data supplied by the drag source
23018      * @return {Boolean} True if the drop was valid, else false
23019      */
23020     notifyDrop : function(dd, e, data){
23021         if(this.lastOverNode){
23022             this.onNodeOut(this.lastOverNode, dd, e, data);
23023             this.lastOverNode = null;
23024         }
23025         var n = this.getTargetFromEvent(e);
23026         return n ?
23027             this.onNodeDrop(n, dd, e, data) :
23028             this.onContainerDrop(dd, e, data);
23029     },
23030
23031     // private
23032     triggerCacheRefresh : function(){
23033         Roo.dd.DDM.refreshCache(this.groups);
23034     }  
23035 });/*
23036  * Based on:
23037  * Ext JS Library 1.1.1
23038  * Copyright(c) 2006-2007, Ext JS, LLC.
23039  *
23040  * Originally Released Under LGPL - original licence link has changed is not relivant.
23041  *
23042  * Fork - LGPL
23043  * <script type="text/javascript">
23044  */
23045
23046
23047 /**
23048  * @class Roo.data.SortTypes
23049  * @singleton
23050  * Defines the default sorting (casting?) comparison functions used when sorting data.
23051  */
23052 Roo.data.SortTypes = {
23053     /**
23054      * Default sort that does nothing
23055      * @param {Mixed} s The value being converted
23056      * @return {Mixed} The comparison value
23057      */
23058     none : function(s){
23059         return s;
23060     },
23061     
23062     /**
23063      * The regular expression used to strip tags
23064      * @type {RegExp}
23065      * @property
23066      */
23067     stripTagsRE : /<\/?[^>]+>/gi,
23068     
23069     /**
23070      * Strips all HTML tags to sort on text only
23071      * @param {Mixed} s The value being converted
23072      * @return {String} The comparison value
23073      */
23074     asText : function(s){
23075         return String(s).replace(this.stripTagsRE, "");
23076     },
23077     
23078     /**
23079      * Strips all HTML tags to sort on text only - Case insensitive
23080      * @param {Mixed} s The value being converted
23081      * @return {String} The comparison value
23082      */
23083     asUCText : function(s){
23084         return String(s).toUpperCase().replace(this.stripTagsRE, "");
23085     },
23086     
23087     /**
23088      * Case insensitive string
23089      * @param {Mixed} s The value being converted
23090      * @return {String} The comparison value
23091      */
23092     asUCString : function(s) {
23093         return String(s).toUpperCase();
23094     },
23095     
23096     /**
23097      * Date sorting
23098      * @param {Mixed} s The value being converted
23099      * @return {Number} The comparison value
23100      */
23101     asDate : function(s) {
23102         if(!s){
23103             return 0;
23104         }
23105         if(s instanceof Date){
23106             return s.getTime();
23107         }
23108         return Date.parse(String(s));
23109     },
23110     
23111     /**
23112      * Float sorting
23113      * @param {Mixed} s The value being converted
23114      * @return {Float} The comparison value
23115      */
23116     asFloat : function(s) {
23117         var val = parseFloat(String(s).replace(/,/g, ""));
23118         if(isNaN(val)) {
23119             val = 0;
23120         }
23121         return val;
23122     },
23123     
23124     /**
23125      * Integer sorting
23126      * @param {Mixed} s The value being converted
23127      * @return {Number} The comparison value
23128      */
23129     asInt : function(s) {
23130         var val = parseInt(String(s).replace(/,/g, ""));
23131         if(isNaN(val)) {
23132             val = 0;
23133         }
23134         return val;
23135     }
23136 };/*
23137  * Based on:
23138  * Ext JS Library 1.1.1
23139  * Copyright(c) 2006-2007, Ext JS, LLC.
23140  *
23141  * Originally Released Under LGPL - original licence link has changed is not relivant.
23142  *
23143  * Fork - LGPL
23144  * <script type="text/javascript">
23145  */
23146
23147 /**
23148 * @class Roo.data.Record
23149  * Instances of this class encapsulate both record <em>definition</em> information, and record
23150  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
23151  * to access Records cached in an {@link Roo.data.Store} object.<br>
23152  * <p>
23153  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
23154  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
23155  * objects.<br>
23156  * <p>
23157  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
23158  * @constructor
23159  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
23160  * {@link #create}. The parameters are the same.
23161  * @param {Array} data An associative Array of data values keyed by the field name.
23162  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
23163  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
23164  * not specified an integer id is generated.
23165  */
23166 Roo.data.Record = function(data, id){
23167     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
23168     this.data = data;
23169 };
23170
23171 /**
23172  * Generate a constructor for a specific record layout.
23173  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
23174  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
23175  * Each field definition object may contain the following properties: <ul>
23176  * <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,
23177  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
23178  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
23179  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
23180  * is being used, then this is a string containing the javascript expression to reference the data relative to 
23181  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
23182  * to the data item relative to the record element. If the mapping expression is the same as the field name,
23183  * this may be omitted.</p></li>
23184  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
23185  * <ul><li>auto (Default, implies no conversion)</li>
23186  * <li>string</li>
23187  * <li>int</li>
23188  * <li>float</li>
23189  * <li>boolean</li>
23190  * <li>date</li></ul></p></li>
23191  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
23192  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
23193  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
23194  * by the Reader into an object that will be stored in the Record. It is passed the
23195  * following parameters:<ul>
23196  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
23197  * </ul></p></li>
23198  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
23199  * </ul>
23200  * <br>usage:<br><pre><code>
23201 var TopicRecord = Roo.data.Record.create(
23202     {name: 'title', mapping: 'topic_title'},
23203     {name: 'author', mapping: 'username'},
23204     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
23205     {name: 'lastPost', mapping: 'post_time', type: 'date'},
23206     {name: 'lastPoster', mapping: 'user2'},
23207     {name: 'excerpt', mapping: 'post_text'}
23208 );
23209
23210 var myNewRecord = new TopicRecord({
23211     title: 'Do my job please',
23212     author: 'noobie',
23213     totalPosts: 1,
23214     lastPost: new Date(),
23215     lastPoster: 'Animal',
23216     excerpt: 'No way dude!'
23217 });
23218 myStore.add(myNewRecord);
23219 </code></pre>
23220  * @method create
23221  * @static
23222  */
23223 Roo.data.Record.create = function(o){
23224     var f = function(){
23225         f.superclass.constructor.apply(this, arguments);
23226     };
23227     Roo.extend(f, Roo.data.Record);
23228     var p = f.prototype;
23229     p.fields = new Roo.util.MixedCollection(false, function(field){
23230         return field.name;
23231     });
23232     for(var i = 0, len = o.length; i < len; i++){
23233         p.fields.add(new Roo.data.Field(o[i]));
23234     }
23235     f.getField = function(name){
23236         return p.fields.get(name);  
23237     };
23238     return f;
23239 };
23240
23241 Roo.data.Record.AUTO_ID = 1000;
23242 Roo.data.Record.EDIT = 'edit';
23243 Roo.data.Record.REJECT = 'reject';
23244 Roo.data.Record.COMMIT = 'commit';
23245
23246 Roo.data.Record.prototype = {
23247     /**
23248      * Readonly flag - true if this record has been modified.
23249      * @type Boolean
23250      */
23251     dirty : false,
23252     editing : false,
23253     error: null,
23254     modified: null,
23255
23256     // private
23257     join : function(store){
23258         this.store = store;
23259     },
23260
23261     /**
23262      * Set the named field to the specified value.
23263      * @param {String} name The name of the field to set.
23264      * @param {Object} value The value to set the field to.
23265      */
23266     set : function(name, value){
23267         if(this.data[name] == value){
23268             return;
23269         }
23270         this.dirty = true;
23271         if(!this.modified){
23272             this.modified = {};
23273         }
23274         if(typeof this.modified[name] == 'undefined'){
23275             this.modified[name] = this.data[name];
23276         }
23277         this.data[name] = value;
23278         if(!this.editing && this.store){
23279             this.store.afterEdit(this);
23280         }       
23281     },
23282
23283     /**
23284      * Get the value of the named field.
23285      * @param {String} name The name of the field to get the value of.
23286      * @return {Object} The value of the field.
23287      */
23288     get : function(name){
23289         return this.data[name]; 
23290     },
23291
23292     // private
23293     beginEdit : function(){
23294         this.editing = true;
23295         this.modified = {}; 
23296     },
23297
23298     // private
23299     cancelEdit : function(){
23300         this.editing = false;
23301         delete this.modified;
23302     },
23303
23304     // private
23305     endEdit : function(){
23306         this.editing = false;
23307         if(this.dirty && this.store){
23308             this.store.afterEdit(this);
23309         }
23310     },
23311
23312     /**
23313      * Usually called by the {@link Roo.data.Store} which owns the Record.
23314      * Rejects all changes made to the Record since either creation, or the last commit operation.
23315      * Modified fields are reverted to their original values.
23316      * <p>
23317      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
23318      * of reject operations.
23319      */
23320     reject : function(){
23321         var m = this.modified;
23322         for(var n in m){
23323             if(typeof m[n] != "function"){
23324                 this.data[n] = m[n];
23325             }
23326         }
23327         this.dirty = false;
23328         delete this.modified;
23329         this.editing = false;
23330         if(this.store){
23331             this.store.afterReject(this);
23332         }
23333     },
23334
23335     /**
23336      * Usually called by the {@link Roo.data.Store} which owns the Record.
23337      * Commits all changes made to the Record since either creation, or the last commit operation.
23338      * <p>
23339      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
23340      * of commit operations.
23341      */
23342     commit : function(){
23343         this.dirty = false;
23344         delete this.modified;
23345         this.editing = false;
23346         if(this.store){
23347             this.store.afterCommit(this);
23348         }
23349     },
23350
23351     // private
23352     hasError : function(){
23353         return this.error != null;
23354     },
23355
23356     // private
23357     clearError : function(){
23358         this.error = null;
23359     },
23360
23361     /**
23362      * Creates a copy of this record.
23363      * @param {String} id (optional) A new record id if you don't want to use this record's id
23364      * @return {Record}
23365      */
23366     copy : function(newId) {
23367         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
23368     }
23369 };/*
23370  * Based on:
23371  * Ext JS Library 1.1.1
23372  * Copyright(c) 2006-2007, Ext JS, LLC.
23373  *
23374  * Originally Released Under LGPL - original licence link has changed is not relivant.
23375  *
23376  * Fork - LGPL
23377  * <script type="text/javascript">
23378  */
23379
23380
23381
23382 /**
23383  * @class Roo.data.Store
23384  * @extends Roo.util.Observable
23385  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
23386  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
23387  * <p>
23388  * 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
23389  * has no knowledge of the format of the data returned by the Proxy.<br>
23390  * <p>
23391  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
23392  * instances from the data object. These records are cached and made available through accessor functions.
23393  * @constructor
23394  * Creates a new Store.
23395  * @param {Object} config A config object containing the objects needed for the Store to access data,
23396  * and read the data into Records.
23397  */
23398 Roo.data.Store = function(config){
23399     this.data = new Roo.util.MixedCollection(false);
23400     this.data.getKey = function(o){
23401         return o.id;
23402     };
23403     this.baseParams = {};
23404     // private
23405     this.paramNames = {
23406         "start" : "start",
23407         "limit" : "limit",
23408         "sort" : "sort",
23409         "dir" : "dir",
23410         "multisort" : "_multisort"
23411     };
23412
23413     if(config && config.data){
23414         this.inlineData = config.data;
23415         delete config.data;
23416     }
23417
23418     Roo.apply(this, config);
23419     
23420     if(this.reader){ // reader passed
23421         this.reader = Roo.factory(this.reader, Roo.data);
23422         this.reader.xmodule = this.xmodule || false;
23423         if(!this.recordType){
23424             this.recordType = this.reader.recordType;
23425         }
23426         if(this.reader.onMetaChange){
23427             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
23428         }
23429     }
23430
23431     if(this.recordType){
23432         this.fields = this.recordType.prototype.fields;
23433     }
23434     this.modified = [];
23435
23436     this.addEvents({
23437         /**
23438          * @event datachanged
23439          * Fires when the data cache has changed, and a widget which is using this Store
23440          * as a Record cache should refresh its view.
23441          * @param {Store} this
23442          */
23443         datachanged : true,
23444         /**
23445          * @event metachange
23446          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
23447          * @param {Store} this
23448          * @param {Object} meta The JSON metadata
23449          */
23450         metachange : true,
23451         /**
23452          * @event add
23453          * Fires when Records have been added to the Store
23454          * @param {Store} this
23455          * @param {Roo.data.Record[]} records The array of Records added
23456          * @param {Number} index The index at which the record(s) were added
23457          */
23458         add : true,
23459         /**
23460          * @event remove
23461          * Fires when a Record has been removed from the Store
23462          * @param {Store} this
23463          * @param {Roo.data.Record} record The Record that was removed
23464          * @param {Number} index The index at which the record was removed
23465          */
23466         remove : true,
23467         /**
23468          * @event update
23469          * Fires when a Record has been updated
23470          * @param {Store} this
23471          * @param {Roo.data.Record} record The Record that was updated
23472          * @param {String} operation The update operation being performed.  Value may be one of:
23473          * <pre><code>
23474  Roo.data.Record.EDIT
23475  Roo.data.Record.REJECT
23476  Roo.data.Record.COMMIT
23477          * </code></pre>
23478          */
23479         update : true,
23480         /**
23481          * @event clear
23482          * Fires when the data cache has been cleared.
23483          * @param {Store} this
23484          */
23485         clear : true,
23486         /**
23487          * @event beforeload
23488          * Fires before a request is made for a new data object.  If the beforeload handler returns false
23489          * the load action will be canceled.
23490          * @param {Store} this
23491          * @param {Object} options The loading options that were specified (see {@link #load} for details)
23492          */
23493         beforeload : true,
23494         /**
23495          * @event beforeloadadd
23496          * Fires after a new set of Records has been loaded.
23497          * @param {Store} this
23498          * @param {Roo.data.Record[]} records The Records that were loaded
23499          * @param {Object} options The loading options that were specified (see {@link #load} for details)
23500          */
23501         beforeloadadd : true,
23502         /**
23503          * @event load
23504          * Fires after a new set of Records has been loaded, before they are added to the store.
23505          * @param {Store} this
23506          * @param {Roo.data.Record[]} records The Records that were loaded
23507          * @param {Object} options The loading options that were specified (see {@link #load} for details)
23508          * @params {Object} return from reader
23509          */
23510         load : true,
23511         /**
23512          * @event loadexception
23513          * Fires if an exception occurs in the Proxy during loading.
23514          * Called with the signature of the Proxy's "loadexception" event.
23515          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
23516          * 
23517          * @param {Proxy} 
23518          * @param {Object} return from JsonData.reader() - success, totalRecords, records
23519          * @param {Object} load options 
23520          * @param {Object} jsonData from your request (normally this contains the Exception)
23521          */
23522         loadexception : true
23523     });
23524     
23525     if(this.proxy){
23526         this.proxy = Roo.factory(this.proxy, Roo.data);
23527         this.proxy.xmodule = this.xmodule || false;
23528         this.relayEvents(this.proxy,  ["loadexception"]);
23529     }
23530     this.sortToggle = {};
23531     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
23532
23533     Roo.data.Store.superclass.constructor.call(this);
23534
23535     if(this.inlineData){
23536         this.loadData(this.inlineData);
23537         delete this.inlineData;
23538     }
23539 };
23540
23541 Roo.extend(Roo.data.Store, Roo.util.Observable, {
23542      /**
23543     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
23544     * without a remote query - used by combo/forms at present.
23545     */
23546     
23547     /**
23548     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
23549     */
23550     /**
23551     * @cfg {Array} data Inline data to be loaded when the store is initialized.
23552     */
23553     /**
23554     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
23555     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
23556     */
23557     /**
23558     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
23559     * on any HTTP request
23560     */
23561     /**
23562     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
23563     */
23564     /**
23565     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
23566     */
23567     multiSort: false,
23568     /**
23569     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
23570     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
23571     */
23572     remoteSort : false,
23573
23574     /**
23575     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
23576      * loaded or when a record is removed. (defaults to false).
23577     */
23578     pruneModifiedRecords : false,
23579
23580     // private
23581     lastOptions : null,
23582
23583     /**
23584      * Add Records to the Store and fires the add event.
23585      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
23586      */
23587     add : function(records){
23588         records = [].concat(records);
23589         for(var i = 0, len = records.length; i < len; i++){
23590             records[i].join(this);
23591         }
23592         var index = this.data.length;
23593         this.data.addAll(records);
23594         this.fireEvent("add", this, records, index);
23595     },
23596
23597     /**
23598      * Remove a Record from the Store and fires the remove event.
23599      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
23600      */
23601     remove : function(record){
23602         var index = this.data.indexOf(record);
23603         this.data.removeAt(index);
23604  
23605         if(this.pruneModifiedRecords){
23606             this.modified.remove(record);
23607         }
23608         this.fireEvent("remove", this, record, index);
23609     },
23610
23611     /**
23612      * Remove all Records from the Store and fires the clear event.
23613      */
23614     removeAll : function(){
23615         this.data.clear();
23616         if(this.pruneModifiedRecords){
23617             this.modified = [];
23618         }
23619         this.fireEvent("clear", this);
23620     },
23621
23622     /**
23623      * Inserts Records to the Store at the given index and fires the add event.
23624      * @param {Number} index The start index at which to insert the passed Records.
23625      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
23626      */
23627     insert : function(index, records){
23628         records = [].concat(records);
23629         for(var i = 0, len = records.length; i < len; i++){
23630             this.data.insert(index, records[i]);
23631             records[i].join(this);
23632         }
23633         this.fireEvent("add", this, records, index);
23634     },
23635
23636     /**
23637      * Get the index within the cache of the passed Record.
23638      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
23639      * @return {Number} The index of the passed Record. Returns -1 if not found.
23640      */
23641     indexOf : function(record){
23642         return this.data.indexOf(record);
23643     },
23644
23645     /**
23646      * Get the index within the cache of the Record with the passed id.
23647      * @param {String} id The id of the Record to find.
23648      * @return {Number} The index of the Record. Returns -1 if not found.
23649      */
23650     indexOfId : function(id){
23651         return this.data.indexOfKey(id);
23652     },
23653
23654     /**
23655      * Get the Record with the specified id.
23656      * @param {String} id The id of the Record to find.
23657      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
23658      */
23659     getById : function(id){
23660         return this.data.key(id);
23661     },
23662
23663     /**
23664      * Get the Record at the specified index.
23665      * @param {Number} index The index of the Record to find.
23666      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
23667      */
23668     getAt : function(index){
23669         return this.data.itemAt(index);
23670     },
23671
23672     /**
23673      * Returns a range of Records between specified indices.
23674      * @param {Number} startIndex (optional) The starting index (defaults to 0)
23675      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
23676      * @return {Roo.data.Record[]} An array of Records
23677      */
23678     getRange : function(start, end){
23679         return this.data.getRange(start, end);
23680     },
23681
23682     // private
23683     storeOptions : function(o){
23684         o = Roo.apply({}, o);
23685         delete o.callback;
23686         delete o.scope;
23687         this.lastOptions = o;
23688     },
23689
23690     /**
23691      * Loads the Record cache from the configured Proxy using the configured Reader.
23692      * <p>
23693      * If using remote paging, then the first load call must specify the <em>start</em>
23694      * and <em>limit</em> properties in the options.params property to establish the initial
23695      * position within the dataset, and the number of Records to cache on each read from the Proxy.
23696      * <p>
23697      * <strong>It is important to note that for remote data sources, loading is asynchronous,
23698      * and this call will return before the new data has been loaded. Perform any post-processing
23699      * in a callback function, or in a "load" event handler.</strong>
23700      * <p>
23701      * @param {Object} options An object containing properties which control loading options:<ul>
23702      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
23703      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
23704      * passed the following arguments:<ul>
23705      * <li>r : Roo.data.Record[]</li>
23706      * <li>options: Options object from the load call</li>
23707      * <li>success: Boolean success indicator</li></ul></li>
23708      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
23709      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
23710      * </ul>
23711      */
23712     load : function(options){
23713         options = options || {};
23714         if(this.fireEvent("beforeload", this, options) !== false){
23715             this.storeOptions(options);
23716             var p = Roo.apply(options.params || {}, this.baseParams);
23717             // if meta was not loaded from remote source.. try requesting it.
23718             if (!this.reader.metaFromRemote) {
23719                 p._requestMeta = 1;
23720             }
23721             if(this.sortInfo && this.remoteSort){
23722                 var pn = this.paramNames;
23723                 p[pn["sort"]] = this.sortInfo.field;
23724                 p[pn["dir"]] = this.sortInfo.direction;
23725             }
23726             if (this.multiSort) {
23727                 var pn = this.paramNames;
23728                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
23729             }
23730             
23731             this.proxy.load(p, this.reader, this.loadRecords, this, options);
23732         }
23733     },
23734
23735     /**
23736      * Reloads the Record cache from the configured Proxy using the configured Reader and
23737      * the options from the last load operation performed.
23738      * @param {Object} options (optional) An object containing properties which may override the options
23739      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
23740      * the most recently used options are reused).
23741      */
23742     reload : function(options){
23743         this.load(Roo.applyIf(options||{}, this.lastOptions));
23744     },
23745
23746     // private
23747     // Called as a callback by the Reader during a load operation.
23748     loadRecords : function(o, options, success){
23749         if(!o || success === false){
23750             if(success !== false){
23751                 this.fireEvent("load", this, [], options, o);
23752             }
23753             if(options.callback){
23754                 options.callback.call(options.scope || this, [], options, false);
23755             }
23756             return;
23757         }
23758         // if data returned failure - throw an exception.
23759         if (o.success === false) {
23760             // show a message if no listener is registered.
23761             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
23762                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
23763             }
23764             // loadmask wil be hooked into this..
23765             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
23766             return;
23767         }
23768         var r = o.records, t = o.totalRecords || r.length;
23769         
23770         this.fireEvent("beforeloadadd", this, r, options, o);
23771         
23772         if(!options || options.add !== true){
23773             if(this.pruneModifiedRecords){
23774                 this.modified = [];
23775             }
23776             for(var i = 0, len = r.length; i < len; i++){
23777                 r[i].join(this);
23778             }
23779             if(this.snapshot){
23780                 this.data = this.snapshot;
23781                 delete this.snapshot;
23782             }
23783             this.data.clear();
23784             this.data.addAll(r);
23785             this.totalLength = t;
23786             this.applySort();
23787             this.fireEvent("datachanged", this);
23788         }else{
23789             this.totalLength = Math.max(t, this.data.length+r.length);
23790             this.add(r);
23791         }
23792         
23793         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
23794                 
23795             var e = new Roo.data.Record({});
23796
23797             e.set(this.parent.displayField, this.parent.emptyTitle);
23798             e.set(this.parent.valueField, '');
23799
23800             this.insert(0, e);
23801         }
23802             
23803         this.fireEvent("load", this, r, options, o);
23804         if(options.callback){
23805             options.callback.call(options.scope || this, r, options, true);
23806         }
23807     },
23808
23809
23810     /**
23811      * Loads data from a passed data block. A Reader which understands the format of the data
23812      * must have been configured in the constructor.
23813      * @param {Object} data The data block from which to read the Records.  The format of the data expected
23814      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
23815      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
23816      */
23817     loadData : function(o, append){
23818         var r = this.reader.readRecords(o);
23819         this.loadRecords(r, {add: append}, true);
23820     },
23821     
23822      /**
23823      * using 'cn' the nested child reader read the child array into it's child stores.
23824      * @param {Object} rec The record with a 'children array
23825      */
23826     loadDataFromChildren : function(rec)
23827     {
23828         this.loadData(this.reader.toLoadData(rec));
23829     },
23830     
23831
23832     /**
23833      * Gets the number of cached records.
23834      * <p>
23835      * <em>If using paging, this may not be the total size of the dataset. If the data object
23836      * used by the Reader contains the dataset size, then the getTotalCount() function returns
23837      * the data set size</em>
23838      */
23839     getCount : function(){
23840         return this.data.length || 0;
23841     },
23842
23843     /**
23844      * Gets the total number of records in the dataset as returned by the server.
23845      * <p>
23846      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
23847      * the dataset size</em>
23848      */
23849     getTotalCount : function(){
23850         return this.totalLength || 0;
23851     },
23852
23853     /**
23854      * Returns the sort state of the Store as an object with two properties:
23855      * <pre><code>
23856  field {String} The name of the field by which the Records are sorted
23857  direction {String} The sort order, "ASC" or "DESC"
23858      * </code></pre>
23859      */
23860     getSortState : function(){
23861         return this.sortInfo;
23862     },
23863
23864     // private
23865     applySort : function(){
23866         if(this.sortInfo && !this.remoteSort){
23867             var s = this.sortInfo, f = s.field;
23868             var st = this.fields.get(f).sortType;
23869             var fn = function(r1, r2){
23870                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
23871                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
23872             };
23873             this.data.sort(s.direction, fn);
23874             if(this.snapshot && this.snapshot != this.data){
23875                 this.snapshot.sort(s.direction, fn);
23876             }
23877         }
23878     },
23879
23880     /**
23881      * Sets the default sort column and order to be used by the next load operation.
23882      * @param {String} fieldName The name of the field to sort by.
23883      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
23884      */
23885     setDefaultSort : function(field, dir){
23886         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
23887     },
23888
23889     /**
23890      * Sort the Records.
23891      * If remote sorting is used, the sort is performed on the server, and the cache is
23892      * reloaded. If local sorting is used, the cache is sorted internally.
23893      * @param {String} fieldName The name of the field to sort by.
23894      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
23895      */
23896     sort : function(fieldName, dir){
23897         var f = this.fields.get(fieldName);
23898         if(!dir){
23899             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
23900             
23901             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
23902                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
23903             }else{
23904                 dir = f.sortDir;
23905             }
23906         }
23907         this.sortToggle[f.name] = dir;
23908         this.sortInfo = {field: f.name, direction: dir};
23909         if(!this.remoteSort){
23910             this.applySort();
23911             this.fireEvent("datachanged", this);
23912         }else{
23913             this.load(this.lastOptions);
23914         }
23915     },
23916
23917     /**
23918      * Calls the specified function for each of the Records in the cache.
23919      * @param {Function} fn The function to call. The Record is passed as the first parameter.
23920      * Returning <em>false</em> aborts and exits the iteration.
23921      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
23922      */
23923     each : function(fn, scope){
23924         this.data.each(fn, scope);
23925     },
23926
23927     /**
23928      * Gets all records modified since the last commit.  Modified records are persisted across load operations
23929      * (e.g., during paging).
23930      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
23931      */
23932     getModifiedRecords : function(){
23933         return this.modified;
23934     },
23935
23936     // private
23937     createFilterFn : function(property, value, anyMatch){
23938         if(!value.exec){ // not a regex
23939             value = String(value);
23940             if(value.length == 0){
23941                 return false;
23942             }
23943             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
23944         }
23945         return function(r){
23946             return value.test(r.data[property]);
23947         };
23948     },
23949
23950     /**
23951      * Sums the value of <i>property</i> for each record between start and end and returns the result.
23952      * @param {String} property A field on your records
23953      * @param {Number} start The record index to start at (defaults to 0)
23954      * @param {Number} end The last record index to include (defaults to length - 1)
23955      * @return {Number} The sum
23956      */
23957     sum : function(property, start, end){
23958         var rs = this.data.items, v = 0;
23959         start = start || 0;
23960         end = (end || end === 0) ? end : rs.length-1;
23961
23962         for(var i = start; i <= end; i++){
23963             v += (rs[i].data[property] || 0);
23964         }
23965         return v;
23966     },
23967
23968     /**
23969      * Filter the records by a specified property.
23970      * @param {String} field A field on your records
23971      * @param {String/RegExp} value Either a string that the field
23972      * should start with or a RegExp to test against the field
23973      * @param {Boolean} anyMatch True to match any part not just the beginning
23974      */
23975     filter : function(property, value, anyMatch){
23976         var fn = this.createFilterFn(property, value, anyMatch);
23977         return fn ? this.filterBy(fn) : this.clearFilter();
23978     },
23979
23980     /**
23981      * Filter by a function. The specified function will be called with each
23982      * record in this data source. If the function returns true the record is included,
23983      * otherwise it is filtered.
23984      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
23985      * @param {Object} scope (optional) The scope of the function (defaults to this)
23986      */
23987     filterBy : function(fn, scope){
23988         this.snapshot = this.snapshot || this.data;
23989         this.data = this.queryBy(fn, scope||this);
23990         this.fireEvent("datachanged", this);
23991     },
23992
23993     /**
23994      * Query the records by a specified property.
23995      * @param {String} field A field on your records
23996      * @param {String/RegExp} value Either a string that the field
23997      * should start with or a RegExp to test against the field
23998      * @param {Boolean} anyMatch True to match any part not just the beginning
23999      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
24000      */
24001     query : function(property, value, anyMatch){
24002         var fn = this.createFilterFn(property, value, anyMatch);
24003         return fn ? this.queryBy(fn) : this.data.clone();
24004     },
24005
24006     /**
24007      * Query by a function. The specified function will be called with each
24008      * record in this data source. If the function returns true the record is included
24009      * in the results.
24010      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
24011      * @param {Object} scope (optional) The scope of the function (defaults to this)
24012       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
24013      **/
24014     queryBy : function(fn, scope){
24015         var data = this.snapshot || this.data;
24016         return data.filterBy(fn, scope||this);
24017     },
24018
24019     /**
24020      * Collects unique values for a particular dataIndex from this store.
24021      * @param {String} dataIndex The property to collect
24022      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
24023      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
24024      * @return {Array} An array of the unique values
24025      **/
24026     collect : function(dataIndex, allowNull, bypassFilter){
24027         var d = (bypassFilter === true && this.snapshot) ?
24028                 this.snapshot.items : this.data.items;
24029         var v, sv, r = [], l = {};
24030         for(var i = 0, len = d.length; i < len; i++){
24031             v = d[i].data[dataIndex];
24032             sv = String(v);
24033             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
24034                 l[sv] = true;
24035                 r[r.length] = v;
24036             }
24037         }
24038         return r;
24039     },
24040
24041     /**
24042      * Revert to a view of the Record cache with no filtering applied.
24043      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
24044      */
24045     clearFilter : function(suppressEvent){
24046         if(this.snapshot && this.snapshot != this.data){
24047             this.data = this.snapshot;
24048             delete this.snapshot;
24049             if(suppressEvent !== true){
24050                 this.fireEvent("datachanged", this);
24051             }
24052         }
24053     },
24054
24055     // private
24056     afterEdit : function(record){
24057         if(this.modified.indexOf(record) == -1){
24058             this.modified.push(record);
24059         }
24060         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
24061     },
24062     
24063     // private
24064     afterReject : function(record){
24065         this.modified.remove(record);
24066         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
24067     },
24068
24069     // private
24070     afterCommit : function(record){
24071         this.modified.remove(record);
24072         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
24073     },
24074
24075     /**
24076      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
24077      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
24078      */
24079     commitChanges : function(){
24080         var m = this.modified.slice(0);
24081         this.modified = [];
24082         for(var i = 0, len = m.length; i < len; i++){
24083             m[i].commit();
24084         }
24085     },
24086
24087     /**
24088      * Cancel outstanding changes on all changed records.
24089      */
24090     rejectChanges : function(){
24091         var m = this.modified.slice(0);
24092         this.modified = [];
24093         for(var i = 0, len = m.length; i < len; i++){
24094             m[i].reject();
24095         }
24096     },
24097
24098     onMetaChange : function(meta, rtype, o){
24099         this.recordType = rtype;
24100         this.fields = rtype.prototype.fields;
24101         delete this.snapshot;
24102         this.sortInfo = meta.sortInfo || this.sortInfo;
24103         this.modified = [];
24104         this.fireEvent('metachange', this, this.reader.meta);
24105     },
24106     
24107     moveIndex : function(data, type)
24108     {
24109         var index = this.indexOf(data);
24110         
24111         var newIndex = index + type;
24112         
24113         this.remove(data);
24114         
24115         this.insert(newIndex, data);
24116         
24117     }
24118 });/*
24119  * Based on:
24120  * Ext JS Library 1.1.1
24121  * Copyright(c) 2006-2007, Ext JS, LLC.
24122  *
24123  * Originally Released Under LGPL - original licence link has changed is not relivant.
24124  *
24125  * Fork - LGPL
24126  * <script type="text/javascript">
24127  */
24128
24129 /**
24130  * @class Roo.data.SimpleStore
24131  * @extends Roo.data.Store
24132  * Small helper class to make creating Stores from Array data easier.
24133  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
24134  * @cfg {Array} fields An array of field definition objects, or field name strings.
24135  * @cfg {Object} an existing reader (eg. copied from another store)
24136  * @cfg {Array} data The multi-dimensional array of data
24137  * @constructor
24138  * @param {Object} config
24139  */
24140 Roo.data.SimpleStore = function(config)
24141 {
24142     Roo.data.SimpleStore.superclass.constructor.call(this, {
24143         isLocal : true,
24144         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
24145                 id: config.id
24146             },
24147             Roo.data.Record.create(config.fields)
24148         ),
24149         proxy : new Roo.data.MemoryProxy(config.data)
24150     });
24151     this.load();
24152 };
24153 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
24154  * Based on:
24155  * Ext JS Library 1.1.1
24156  * Copyright(c) 2006-2007, Ext JS, LLC.
24157  *
24158  * Originally Released Under LGPL - original licence link has changed is not relivant.
24159  *
24160  * Fork - LGPL
24161  * <script type="text/javascript">
24162  */
24163
24164 /**
24165 /**
24166  * @extends Roo.data.Store
24167  * @class Roo.data.JsonStore
24168  * Small helper class to make creating Stores for JSON data easier. <br/>
24169 <pre><code>
24170 var store = new Roo.data.JsonStore({
24171     url: 'get-images.php',
24172     root: 'images',
24173     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
24174 });
24175 </code></pre>
24176  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
24177  * JsonReader and HttpProxy (unless inline data is provided).</b>
24178  * @cfg {Array} fields An array of field definition objects, or field name strings.
24179  * @constructor
24180  * @param {Object} config
24181  */
24182 Roo.data.JsonStore = function(c){
24183     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
24184         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
24185         reader: new Roo.data.JsonReader(c, c.fields)
24186     }));
24187 };
24188 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
24189  * Based on:
24190  * Ext JS Library 1.1.1
24191  * Copyright(c) 2006-2007, Ext JS, LLC.
24192  *
24193  * Originally Released Under LGPL - original licence link has changed is not relivant.
24194  *
24195  * Fork - LGPL
24196  * <script type="text/javascript">
24197  */
24198
24199  
24200 Roo.data.Field = function(config){
24201     if(typeof config == "string"){
24202         config = {name: config};
24203     }
24204     Roo.apply(this, config);
24205     
24206     if(!this.type){
24207         this.type = "auto";
24208     }
24209     
24210     var st = Roo.data.SortTypes;
24211     // named sortTypes are supported, here we look them up
24212     if(typeof this.sortType == "string"){
24213         this.sortType = st[this.sortType];
24214     }
24215     
24216     // set default sortType for strings and dates
24217     if(!this.sortType){
24218         switch(this.type){
24219             case "string":
24220                 this.sortType = st.asUCString;
24221                 break;
24222             case "date":
24223                 this.sortType = st.asDate;
24224                 break;
24225             default:
24226                 this.sortType = st.none;
24227         }
24228     }
24229
24230     // define once
24231     var stripRe = /[\$,%]/g;
24232
24233     // prebuilt conversion function for this field, instead of
24234     // switching every time we're reading a value
24235     if(!this.convert){
24236         var cv, dateFormat = this.dateFormat;
24237         switch(this.type){
24238             case "":
24239             case "auto":
24240             case undefined:
24241                 cv = function(v){ return v; };
24242                 break;
24243             case "string":
24244                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
24245                 break;
24246             case "int":
24247                 cv = function(v){
24248                     return v !== undefined && v !== null && v !== '' ?
24249                            parseInt(String(v).replace(stripRe, ""), 10) : '';
24250                     };
24251                 break;
24252             case "float":
24253                 cv = function(v){
24254                     return v !== undefined && v !== null && v !== '' ?
24255                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
24256                     };
24257                 break;
24258             case "bool":
24259             case "boolean":
24260                 cv = function(v){ return v === true || v === "true" || v == 1; };
24261                 break;
24262             case "date":
24263                 cv = function(v){
24264                     if(!v){
24265                         return '';
24266                     }
24267                     if(v instanceof Date){
24268                         return v;
24269                     }
24270                     if(dateFormat){
24271                         if(dateFormat == "timestamp"){
24272                             return new Date(v*1000);
24273                         }
24274                         return Date.parseDate(v, dateFormat);
24275                     }
24276                     var parsed = Date.parse(v);
24277                     return parsed ? new Date(parsed) : null;
24278                 };
24279              break;
24280             
24281         }
24282         this.convert = cv;
24283     }
24284 };
24285
24286 Roo.data.Field.prototype = {
24287     dateFormat: null,
24288     defaultValue: "",
24289     mapping: null,
24290     sortType : null,
24291     sortDir : "ASC"
24292 };/*
24293  * Based on:
24294  * Ext JS Library 1.1.1
24295  * Copyright(c) 2006-2007, Ext JS, LLC.
24296  *
24297  * Originally Released Under LGPL - original licence link has changed is not relivant.
24298  *
24299  * Fork - LGPL
24300  * <script type="text/javascript">
24301  */
24302  
24303 // Base class for reading structured data from a data source.  This class is intended to be
24304 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
24305
24306 /**
24307  * @class Roo.data.DataReader
24308  * Base class for reading structured data from a data source.  This class is intended to be
24309  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
24310  */
24311
24312 Roo.data.DataReader = function(meta, recordType){
24313     
24314     this.meta = meta;
24315     
24316     this.recordType = recordType instanceof Array ? 
24317         Roo.data.Record.create(recordType) : recordType;
24318 };
24319
24320 Roo.data.DataReader.prototype = {
24321     
24322     
24323     readerType : 'Data',
24324      /**
24325      * Create an empty record
24326      * @param {Object} data (optional) - overlay some values
24327      * @return {Roo.data.Record} record created.
24328      */
24329     newRow :  function(d) {
24330         var da =  {};
24331         this.recordType.prototype.fields.each(function(c) {
24332             switch( c.type) {
24333                 case 'int' : da[c.name] = 0; break;
24334                 case 'date' : da[c.name] = new Date(); break;
24335                 case 'float' : da[c.name] = 0.0; break;
24336                 case 'boolean' : da[c.name] = false; break;
24337                 default : da[c.name] = ""; break;
24338             }
24339             
24340         });
24341         return new this.recordType(Roo.apply(da, d));
24342     }
24343     
24344     
24345 };/*
24346  * Based on:
24347  * Ext JS Library 1.1.1
24348  * Copyright(c) 2006-2007, Ext JS, LLC.
24349  *
24350  * Originally Released Under LGPL - original licence link has changed is not relivant.
24351  *
24352  * Fork - LGPL
24353  * <script type="text/javascript">
24354  */
24355
24356 /**
24357  * @class Roo.data.DataProxy
24358  * @extends Roo.data.Observable
24359  * This class is an abstract base class for implementations which provide retrieval of
24360  * unformatted data objects.<br>
24361  * <p>
24362  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
24363  * (of the appropriate type which knows how to parse the data object) to provide a block of
24364  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
24365  * <p>
24366  * Custom implementations must implement the load method as described in
24367  * {@link Roo.data.HttpProxy#load}.
24368  */
24369 Roo.data.DataProxy = function(){
24370     this.addEvents({
24371         /**
24372          * @event beforeload
24373          * Fires before a network request is made to retrieve a data object.
24374          * @param {Object} This DataProxy object.
24375          * @param {Object} params The params parameter to the load function.
24376          */
24377         beforeload : true,
24378         /**
24379          * @event load
24380          * Fires before the load method's callback is called.
24381          * @param {Object} This DataProxy object.
24382          * @param {Object} o The data object.
24383          * @param {Object} arg The callback argument object passed to the load function.
24384          */
24385         load : true,
24386         /**
24387          * @event loadexception
24388          * Fires if an Exception occurs during data retrieval.
24389          * @param {Object} This DataProxy object.
24390          * @param {Object} o The data object.
24391          * @param {Object} arg The callback argument object passed to the load function.
24392          * @param {Object} e The Exception.
24393          */
24394         loadexception : true
24395     });
24396     Roo.data.DataProxy.superclass.constructor.call(this);
24397 };
24398
24399 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
24400
24401     /**
24402      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
24403      */
24404 /*
24405  * Based on:
24406  * Ext JS Library 1.1.1
24407  * Copyright(c) 2006-2007, Ext JS, LLC.
24408  *
24409  * Originally Released Under LGPL - original licence link has changed is not relivant.
24410  *
24411  * Fork - LGPL
24412  * <script type="text/javascript">
24413  */
24414 /**
24415  * @class Roo.data.MemoryProxy
24416  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
24417  * to the Reader when its load method is called.
24418  * @constructor
24419  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
24420  */
24421 Roo.data.MemoryProxy = function(data){
24422     if (data.data) {
24423         data = data.data;
24424     }
24425     Roo.data.MemoryProxy.superclass.constructor.call(this);
24426     this.data = data;
24427 };
24428
24429 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
24430     
24431     /**
24432      * Load data from the requested source (in this case an in-memory
24433      * data object passed to the constructor), read the data object into
24434      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
24435      * process that block using the passed callback.
24436      * @param {Object} params This parameter is not used by the MemoryProxy class.
24437      * @param {Roo.data.DataReader} reader The Reader object which converts the data
24438      * object into a block of Roo.data.Records.
24439      * @param {Function} callback The function into which to pass the block of Roo.data.records.
24440      * The function must be passed <ul>
24441      * <li>The Record block object</li>
24442      * <li>The "arg" argument from the load function</li>
24443      * <li>A boolean success indicator</li>
24444      * </ul>
24445      * @param {Object} scope The scope in which to call the callback
24446      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
24447      */
24448     load : function(params, reader, callback, scope, arg){
24449         params = params || {};
24450         var result;
24451         try {
24452             result = reader.readRecords(params.data ? params.data :this.data);
24453         }catch(e){
24454             this.fireEvent("loadexception", this, arg, null, e);
24455             callback.call(scope, null, arg, false);
24456             return;
24457         }
24458         callback.call(scope, result, arg, true);
24459     },
24460     
24461     // private
24462     update : function(params, records){
24463         
24464     }
24465 });/*
24466  * Based on:
24467  * Ext JS Library 1.1.1
24468  * Copyright(c) 2006-2007, Ext JS, LLC.
24469  *
24470  * Originally Released Under LGPL - original licence link has changed is not relivant.
24471  *
24472  * Fork - LGPL
24473  * <script type="text/javascript">
24474  */
24475 /**
24476  * @class Roo.data.HttpProxy
24477  * @extends Roo.data.DataProxy
24478  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
24479  * configured to reference a certain URL.<br><br>
24480  * <p>
24481  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
24482  * from which the running page was served.<br><br>
24483  * <p>
24484  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
24485  * <p>
24486  * Be aware that to enable the browser to parse an XML document, the server must set
24487  * the Content-Type header in the HTTP response to "text/xml".
24488  * @constructor
24489  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
24490  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
24491  * will be used to make the request.
24492  */
24493 Roo.data.HttpProxy = function(conn){
24494     Roo.data.HttpProxy.superclass.constructor.call(this);
24495     // is conn a conn config or a real conn?
24496     this.conn = conn;
24497     this.useAjax = !conn || !conn.events;
24498   
24499 };
24500
24501 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
24502     // thse are take from connection...
24503     
24504     /**
24505      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
24506      */
24507     /**
24508      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
24509      * extra parameters to each request made by this object. (defaults to undefined)
24510      */
24511     /**
24512      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
24513      *  to each request made by this object. (defaults to undefined)
24514      */
24515     /**
24516      * @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)
24517      */
24518     /**
24519      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
24520      */
24521      /**
24522      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
24523      * @type Boolean
24524      */
24525   
24526
24527     /**
24528      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
24529      * @type Boolean
24530      */
24531     /**
24532      * Return the {@link Roo.data.Connection} object being used by this Proxy.
24533      * @return {Connection} The Connection object. This object may be used to subscribe to events on
24534      * a finer-grained basis than the DataProxy events.
24535      */
24536     getConnection : function(){
24537         return this.useAjax ? Roo.Ajax : this.conn;
24538     },
24539
24540     /**
24541      * Load data from the configured {@link Roo.data.Connection}, read the data object into
24542      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
24543      * process that block using the passed callback.
24544      * @param {Object} params An object containing properties which are to be used as HTTP parameters
24545      * for the request to the remote server.
24546      * @param {Roo.data.DataReader} reader The Reader object which converts the data
24547      * object into a block of Roo.data.Records.
24548      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
24549      * The function must be passed <ul>
24550      * <li>The Record block object</li>
24551      * <li>The "arg" argument from the load function</li>
24552      * <li>A boolean success indicator</li>
24553      * </ul>
24554      * @param {Object} scope The scope in which to call the callback
24555      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
24556      */
24557     load : function(params, reader, callback, scope, arg){
24558         if(this.fireEvent("beforeload", this, params) !== false){
24559             var  o = {
24560                 params : params || {},
24561                 request: {
24562                     callback : callback,
24563                     scope : scope,
24564                     arg : arg
24565                 },
24566                 reader: reader,
24567                 callback : this.loadResponse,
24568                 scope: this
24569             };
24570             if(this.useAjax){
24571                 Roo.applyIf(o, this.conn);
24572                 if(this.activeRequest){
24573                     Roo.Ajax.abort(this.activeRequest);
24574                 }
24575                 this.activeRequest = Roo.Ajax.request(o);
24576             }else{
24577                 this.conn.request(o);
24578             }
24579         }else{
24580             callback.call(scope||this, null, arg, false);
24581         }
24582     },
24583
24584     // private
24585     loadResponse : function(o, success, response){
24586         delete this.activeRequest;
24587         if(!success){
24588             this.fireEvent("loadexception", this, o, response);
24589             o.request.callback.call(o.request.scope, null, o.request.arg, false);
24590             return;
24591         }
24592         var result;
24593         try {
24594             result = o.reader.read(response);
24595         }catch(e){
24596             this.fireEvent("loadexception", this, o, response, e);
24597             o.request.callback.call(o.request.scope, null, o.request.arg, false);
24598             return;
24599         }
24600         
24601         this.fireEvent("load", this, o, o.request.arg);
24602         o.request.callback.call(o.request.scope, result, o.request.arg, true);
24603     },
24604
24605     // private
24606     update : function(dataSet){
24607
24608     },
24609
24610     // private
24611     updateResponse : function(dataSet){
24612
24613     }
24614 });/*
24615  * Based on:
24616  * Ext JS Library 1.1.1
24617  * Copyright(c) 2006-2007, Ext JS, LLC.
24618  *
24619  * Originally Released Under LGPL - original licence link has changed is not relivant.
24620  *
24621  * Fork - LGPL
24622  * <script type="text/javascript">
24623  */
24624
24625 /**
24626  * @class Roo.data.ScriptTagProxy
24627  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
24628  * other than the originating domain of the running page.<br><br>
24629  * <p>
24630  * <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
24631  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
24632  * <p>
24633  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
24634  * source code that is used as the source inside a &lt;script> tag.<br><br>
24635  * <p>
24636  * In order for the browser to process the returned data, the server must wrap the data object
24637  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
24638  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
24639  * depending on whether the callback name was passed:
24640  * <p>
24641  * <pre><code>
24642 boolean scriptTag = false;
24643 String cb = request.getParameter("callback");
24644 if (cb != null) {
24645     scriptTag = true;
24646     response.setContentType("text/javascript");
24647 } else {
24648     response.setContentType("application/x-json");
24649 }
24650 Writer out = response.getWriter();
24651 if (scriptTag) {
24652     out.write(cb + "(");
24653 }
24654 out.print(dataBlock.toJsonString());
24655 if (scriptTag) {
24656     out.write(");");
24657 }
24658 </pre></code>
24659  *
24660  * @constructor
24661  * @param {Object} config A configuration object.
24662  */
24663 Roo.data.ScriptTagProxy = function(config){
24664     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
24665     Roo.apply(this, config);
24666     this.head = document.getElementsByTagName("head")[0];
24667 };
24668
24669 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
24670
24671 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
24672     /**
24673      * @cfg {String} url The URL from which to request the data object.
24674      */
24675     /**
24676      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
24677      */
24678     timeout : 30000,
24679     /**
24680      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
24681      * the server the name of the callback function set up by the load call to process the returned data object.
24682      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
24683      * javascript output which calls this named function passing the data object as its only parameter.
24684      */
24685     callbackParam : "callback",
24686     /**
24687      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
24688      * name to the request.
24689      */
24690     nocache : true,
24691
24692     /**
24693      * Load data from the configured URL, read the data object into
24694      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
24695      * process that block using the passed callback.
24696      * @param {Object} params An object containing properties which are to be used as HTTP parameters
24697      * for the request to the remote server.
24698      * @param {Roo.data.DataReader} reader The Reader object which converts the data
24699      * object into a block of Roo.data.Records.
24700      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
24701      * The function must be passed <ul>
24702      * <li>The Record block object</li>
24703      * <li>The "arg" argument from the load function</li>
24704      * <li>A boolean success indicator</li>
24705      * </ul>
24706      * @param {Object} scope The scope in which to call the callback
24707      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
24708      */
24709     load : function(params, reader, callback, scope, arg){
24710         if(this.fireEvent("beforeload", this, params) !== false){
24711
24712             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
24713
24714             var url = this.url;
24715             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
24716             if(this.nocache){
24717                 url += "&_dc=" + (new Date().getTime());
24718             }
24719             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
24720             var trans = {
24721                 id : transId,
24722                 cb : "stcCallback"+transId,
24723                 scriptId : "stcScript"+transId,
24724                 params : params,
24725                 arg : arg,
24726                 url : url,
24727                 callback : callback,
24728                 scope : scope,
24729                 reader : reader
24730             };
24731             var conn = this;
24732
24733             window[trans.cb] = function(o){
24734                 conn.handleResponse(o, trans);
24735             };
24736
24737             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
24738
24739             if(this.autoAbort !== false){
24740                 this.abort();
24741             }
24742
24743             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
24744
24745             var script = document.createElement("script");
24746             script.setAttribute("src", url);
24747             script.setAttribute("type", "text/javascript");
24748             script.setAttribute("id", trans.scriptId);
24749             this.head.appendChild(script);
24750
24751             this.trans = trans;
24752         }else{
24753             callback.call(scope||this, null, arg, false);
24754         }
24755     },
24756
24757     // private
24758     isLoading : function(){
24759         return this.trans ? true : false;
24760     },
24761
24762     /**
24763      * Abort the current server request.
24764      */
24765     abort : function(){
24766         if(this.isLoading()){
24767             this.destroyTrans(this.trans);
24768         }
24769     },
24770
24771     // private
24772     destroyTrans : function(trans, isLoaded){
24773         this.head.removeChild(document.getElementById(trans.scriptId));
24774         clearTimeout(trans.timeoutId);
24775         if(isLoaded){
24776             window[trans.cb] = undefined;
24777             try{
24778                 delete window[trans.cb];
24779             }catch(e){}
24780         }else{
24781             // if hasn't been loaded, wait for load to remove it to prevent script error
24782             window[trans.cb] = function(){
24783                 window[trans.cb] = undefined;
24784                 try{
24785                     delete window[trans.cb];
24786                 }catch(e){}
24787             };
24788         }
24789     },
24790
24791     // private
24792     handleResponse : function(o, trans){
24793         this.trans = false;
24794         this.destroyTrans(trans, true);
24795         var result;
24796         try {
24797             result = trans.reader.readRecords(o);
24798         }catch(e){
24799             this.fireEvent("loadexception", this, o, trans.arg, e);
24800             trans.callback.call(trans.scope||window, null, trans.arg, false);
24801             return;
24802         }
24803         this.fireEvent("load", this, o, trans.arg);
24804         trans.callback.call(trans.scope||window, result, trans.arg, true);
24805     },
24806
24807     // private
24808     handleFailure : function(trans){
24809         this.trans = false;
24810         this.destroyTrans(trans, false);
24811         this.fireEvent("loadexception", this, null, trans.arg);
24812         trans.callback.call(trans.scope||window, null, trans.arg, false);
24813     }
24814 });/*
24815  * Based on:
24816  * Ext JS Library 1.1.1
24817  * Copyright(c) 2006-2007, Ext JS, LLC.
24818  *
24819  * Originally Released Under LGPL - original licence link has changed is not relivant.
24820  *
24821  * Fork - LGPL
24822  * <script type="text/javascript">
24823  */
24824
24825 /**
24826  * @class Roo.data.JsonReader
24827  * @extends Roo.data.DataReader
24828  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
24829  * based on mappings in a provided Roo.data.Record constructor.
24830  * 
24831  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
24832  * in the reply previously. 
24833  * 
24834  * <p>
24835  * Example code:
24836  * <pre><code>
24837 var RecordDef = Roo.data.Record.create([
24838     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
24839     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
24840 ]);
24841 var myReader = new Roo.data.JsonReader({
24842     totalProperty: "results",    // The property which contains the total dataset size (optional)
24843     root: "rows",                // The property which contains an Array of row objects
24844     id: "id"                     // The property within each row object that provides an ID for the record (optional)
24845 }, RecordDef);
24846 </code></pre>
24847  * <p>
24848  * This would consume a JSON file like this:
24849  * <pre><code>
24850 { 'results': 2, 'rows': [
24851     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
24852     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
24853 }
24854 </code></pre>
24855  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
24856  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
24857  * paged from the remote server.
24858  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
24859  * @cfg {String} root name of the property which contains the Array of row objects.
24860  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
24861  * @cfg {Array} fields Array of field definition objects
24862  * @constructor
24863  * Create a new JsonReader
24864  * @param {Object} meta Metadata configuration options
24865  * @param {Object} recordType Either an Array of field definition objects,
24866  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
24867  */
24868 Roo.data.JsonReader = function(meta, recordType){
24869     
24870     meta = meta || {};
24871     // set some defaults:
24872     Roo.applyIf(meta, {
24873         totalProperty: 'total',
24874         successProperty : 'success',
24875         root : 'data',
24876         id : 'id'
24877     });
24878     
24879     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
24880 };
24881 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
24882     
24883     readerType : 'Json',
24884     
24885     /**
24886      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
24887      * Used by Store query builder to append _requestMeta to params.
24888      * 
24889      */
24890     metaFromRemote : false,
24891     /**
24892      * This method is only used by a DataProxy which has retrieved data from a remote server.
24893      * @param {Object} response The XHR object which contains the JSON data in its responseText.
24894      * @return {Object} data A data block which is used by an Roo.data.Store object as
24895      * a cache of Roo.data.Records.
24896      */
24897     read : function(response){
24898         var json = response.responseText;
24899        
24900         var o = /* eval:var:o */ eval("("+json+")");
24901         if(!o) {
24902             throw {message: "JsonReader.read: Json object not found"};
24903         }
24904         
24905         if(o.metaData){
24906             
24907             delete this.ef;
24908             this.metaFromRemote = true;
24909             this.meta = o.metaData;
24910             this.recordType = Roo.data.Record.create(o.metaData.fields);
24911             this.onMetaChange(this.meta, this.recordType, o);
24912         }
24913         return this.readRecords(o);
24914     },
24915
24916     // private function a store will implement
24917     onMetaChange : function(meta, recordType, o){
24918
24919     },
24920
24921     /**
24922          * @ignore
24923          */
24924     simpleAccess: function(obj, subsc) {
24925         return obj[subsc];
24926     },
24927
24928         /**
24929          * @ignore
24930          */
24931     getJsonAccessor: function(){
24932         var re = /[\[\.]/;
24933         return function(expr) {
24934             try {
24935                 return(re.test(expr))
24936                     ? new Function("obj", "return obj." + expr)
24937                     : function(obj){
24938                         return obj[expr];
24939                     };
24940             } catch(e){}
24941             return Roo.emptyFn;
24942         };
24943     }(),
24944
24945     /**
24946      * Create a data block containing Roo.data.Records from an XML document.
24947      * @param {Object} o An object which contains an Array of row objects in the property specified
24948      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
24949      * which contains the total size of the dataset.
24950      * @return {Object} data A data block which is used by an Roo.data.Store object as
24951      * a cache of Roo.data.Records.
24952      */
24953     readRecords : function(o){
24954         /**
24955          * After any data loads, the raw JSON data is available for further custom processing.
24956          * @type Object
24957          */
24958         this.o = o;
24959         var s = this.meta, Record = this.recordType,
24960             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
24961
24962 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
24963         if (!this.ef) {
24964             if(s.totalProperty) {
24965                     this.getTotal = this.getJsonAccessor(s.totalProperty);
24966                 }
24967                 if(s.successProperty) {
24968                     this.getSuccess = this.getJsonAccessor(s.successProperty);
24969                 }
24970                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
24971                 if (s.id) {
24972                         var g = this.getJsonAccessor(s.id);
24973                         this.getId = function(rec) {
24974                                 var r = g(rec);  
24975                                 return (r === undefined || r === "") ? null : r;
24976                         };
24977                 } else {
24978                         this.getId = function(){return null;};
24979                 }
24980             this.ef = [];
24981             for(var jj = 0; jj < fl; jj++){
24982                 f = fi[jj];
24983                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
24984                 this.ef[jj] = this.getJsonAccessor(map);
24985             }
24986         }
24987
24988         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
24989         if(s.totalProperty){
24990             var vt = parseInt(this.getTotal(o), 10);
24991             if(!isNaN(vt)){
24992                 totalRecords = vt;
24993             }
24994         }
24995         if(s.successProperty){
24996             var vs = this.getSuccess(o);
24997             if(vs === false || vs === 'false'){
24998                 success = false;
24999             }
25000         }
25001         var records = [];
25002         for(var i = 0; i < c; i++){
25003                 var n = root[i];
25004             var values = {};
25005             var id = this.getId(n);
25006             for(var j = 0; j < fl; j++){
25007                 f = fi[j];
25008             var v = this.ef[j](n);
25009             if (!f.convert) {
25010                 Roo.log('missing convert for ' + f.name);
25011                 Roo.log(f);
25012                 continue;
25013             }
25014             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
25015             }
25016             var record = new Record(values, id);
25017             record.json = n;
25018             records[i] = record;
25019         }
25020         return {
25021             raw : o,
25022             success : success,
25023             records : records,
25024             totalRecords : totalRecords
25025         };
25026     },
25027     // used when loading children.. @see loadDataFromChildren
25028     toLoadData: function(rec)
25029     {
25030         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
25031         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
25032         return { data : data, total : data.length };
25033         
25034     }
25035 });/*
25036  * Based on:
25037  * Ext JS Library 1.1.1
25038  * Copyright(c) 2006-2007, Ext JS, LLC.
25039  *
25040  * Originally Released Under LGPL - original licence link has changed is not relivant.
25041  *
25042  * Fork - LGPL
25043  * <script type="text/javascript">
25044  */
25045
25046 /**
25047  * @class Roo.data.XmlReader
25048  * @extends Roo.data.DataReader
25049  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
25050  * based on mappings in a provided Roo.data.Record constructor.<br><br>
25051  * <p>
25052  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
25053  * header in the HTTP response must be set to "text/xml".</em>
25054  * <p>
25055  * Example code:
25056  * <pre><code>
25057 var RecordDef = Roo.data.Record.create([
25058    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
25059    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
25060 ]);
25061 var myReader = new Roo.data.XmlReader({
25062    totalRecords: "results", // The element which contains the total dataset size (optional)
25063    record: "row",           // The repeated element which contains row information
25064    id: "id"                 // The element within the row that provides an ID for the record (optional)
25065 }, RecordDef);
25066 </code></pre>
25067  * <p>
25068  * This would consume an XML file like this:
25069  * <pre><code>
25070 &lt;?xml?>
25071 &lt;dataset>
25072  &lt;results>2&lt;/results>
25073  &lt;row>
25074    &lt;id>1&lt;/id>
25075    &lt;name>Bill&lt;/name>
25076    &lt;occupation>Gardener&lt;/occupation>
25077  &lt;/row>
25078  &lt;row>
25079    &lt;id>2&lt;/id>
25080    &lt;name>Ben&lt;/name>
25081    &lt;occupation>Horticulturalist&lt;/occupation>
25082  &lt;/row>
25083 &lt;/dataset>
25084 </code></pre>
25085  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
25086  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
25087  * paged from the remote server.
25088  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
25089  * @cfg {String} success The DomQuery path to the success attribute used by forms.
25090  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
25091  * a record identifier value.
25092  * @constructor
25093  * Create a new XmlReader
25094  * @param {Object} meta Metadata configuration options
25095  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
25096  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
25097  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
25098  */
25099 Roo.data.XmlReader = function(meta, recordType){
25100     meta = meta || {};
25101     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
25102 };
25103 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
25104     
25105     readerType : 'Xml',
25106     
25107     /**
25108      * This method is only used by a DataProxy which has retrieved data from a remote server.
25109          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
25110          * to contain a method called 'responseXML' that returns an XML document object.
25111      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
25112      * a cache of Roo.data.Records.
25113      */
25114     read : function(response){
25115         var doc = response.responseXML;
25116         if(!doc) {
25117             throw {message: "XmlReader.read: XML Document not available"};
25118         }
25119         return this.readRecords(doc);
25120     },
25121
25122     /**
25123      * Create a data block containing Roo.data.Records from an XML document.
25124          * @param {Object} doc A parsed XML document.
25125      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
25126      * a cache of Roo.data.Records.
25127      */
25128     readRecords : function(doc){
25129         /**
25130          * After any data loads/reads, the raw XML Document is available for further custom processing.
25131          * @type XMLDocument
25132          */
25133         this.xmlData = doc;
25134         var root = doc.documentElement || doc;
25135         var q = Roo.DomQuery;
25136         var recordType = this.recordType, fields = recordType.prototype.fields;
25137         var sid = this.meta.id;
25138         var totalRecords = 0, success = true;
25139         if(this.meta.totalRecords){
25140             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
25141         }
25142         
25143         if(this.meta.success){
25144             var sv = q.selectValue(this.meta.success, root, true);
25145             success = sv !== false && sv !== 'false';
25146         }
25147         var records = [];
25148         var ns = q.select(this.meta.record, root);
25149         for(var i = 0, len = ns.length; i < len; i++) {
25150                 var n = ns[i];
25151                 var values = {};
25152                 var id = sid ? q.selectValue(sid, n) : undefined;
25153                 for(var j = 0, jlen = fields.length; j < jlen; j++){
25154                     var f = fields.items[j];
25155                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
25156                     v = f.convert(v);
25157                     values[f.name] = v;
25158                 }
25159                 var record = new recordType(values, id);
25160                 record.node = n;
25161                 records[records.length] = record;
25162             }
25163
25164             return {
25165                 success : success,
25166                 records : records,
25167                 totalRecords : totalRecords || records.length
25168             };
25169     }
25170 });/*
25171  * Based on:
25172  * Ext JS Library 1.1.1
25173  * Copyright(c) 2006-2007, Ext JS, LLC.
25174  *
25175  * Originally Released Under LGPL - original licence link has changed is not relivant.
25176  *
25177  * Fork - LGPL
25178  * <script type="text/javascript">
25179  */
25180
25181 /**
25182  * @class Roo.data.ArrayReader
25183  * @extends Roo.data.DataReader
25184  * Data reader class to create an Array of Roo.data.Record objects from an Array.
25185  * Each element of that Array represents a row of data fields. The
25186  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
25187  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
25188  * <p>
25189  * Example code:.
25190  * <pre><code>
25191 var RecordDef = Roo.data.Record.create([
25192     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
25193     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
25194 ]);
25195 var myReader = new Roo.data.ArrayReader({
25196     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
25197 }, RecordDef);
25198 </code></pre>
25199  * <p>
25200  * This would consume an Array like this:
25201  * <pre><code>
25202 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
25203   </code></pre>
25204  
25205  * @constructor
25206  * Create a new JsonReader
25207  * @param {Object} meta Metadata configuration options.
25208  * @param {Object|Array} recordType Either an Array of field definition objects
25209  * 
25210  * @cfg {Array} fields Array of field definition objects
25211  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
25212  * as specified to {@link Roo.data.Record#create},
25213  * or an {@link Roo.data.Record} object
25214  *
25215  * 
25216  * created using {@link Roo.data.Record#create}.
25217  */
25218 Roo.data.ArrayReader = function(meta, recordType)
25219 {    
25220     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
25221 };
25222
25223 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
25224     
25225       /**
25226      * Create a data block containing Roo.data.Records from an XML document.
25227      * @param {Object} o An Array of row objects which represents the dataset.
25228      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
25229      * a cache of Roo.data.Records.
25230      */
25231     readRecords : function(o)
25232     {
25233         var sid = this.meta ? this.meta.id : null;
25234         var recordType = this.recordType, fields = recordType.prototype.fields;
25235         var records = [];
25236         var root = o;
25237         for(var i = 0; i < root.length; i++){
25238                 var n = root[i];
25239             var values = {};
25240             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
25241             for(var j = 0, jlen = fields.length; j < jlen; j++){
25242                 var f = fields.items[j];
25243                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
25244                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
25245                 v = f.convert(v);
25246                 values[f.name] = v;
25247             }
25248             var record = new recordType(values, id);
25249             record.json = n;
25250             records[records.length] = record;
25251         }
25252         return {
25253             records : records,
25254             totalRecords : records.length
25255         };
25256     },
25257     // used when loading children.. @see loadDataFromChildren
25258     toLoadData: function(rec)
25259     {
25260         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
25261         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
25262         
25263     }
25264     
25265     
25266 });/*
25267  * Based on:
25268  * Ext JS Library 1.1.1
25269  * Copyright(c) 2006-2007, Ext JS, LLC.
25270  *
25271  * Originally Released Under LGPL - original licence link has changed is not relivant.
25272  *
25273  * Fork - LGPL
25274  * <script type="text/javascript">
25275  */
25276
25277
25278 /**
25279  * @class Roo.data.Tree
25280  * @extends Roo.util.Observable
25281  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
25282  * in the tree have most standard DOM functionality.
25283  * @constructor
25284  * @param {Node} root (optional) The root node
25285  */
25286 Roo.data.Tree = function(root){
25287    this.nodeHash = {};
25288    /**
25289     * The root node for this tree
25290     * @type Node
25291     */
25292    this.root = null;
25293    if(root){
25294        this.setRootNode(root);
25295    }
25296    this.addEvents({
25297        /**
25298         * @event append
25299         * Fires when a new child node is appended to a node in this tree.
25300         * @param {Tree} tree The owner tree
25301         * @param {Node} parent The parent node
25302         * @param {Node} node The newly appended node
25303         * @param {Number} index The index of the newly appended node
25304         */
25305        "append" : true,
25306        /**
25307         * @event remove
25308         * Fires when a child node is removed from a node in this tree.
25309         * @param {Tree} tree The owner tree
25310         * @param {Node} parent The parent node
25311         * @param {Node} node The child node removed
25312         */
25313        "remove" : true,
25314        /**
25315         * @event move
25316         * Fires when a node is moved to a new location in the tree
25317         * @param {Tree} tree The owner tree
25318         * @param {Node} node The node moved
25319         * @param {Node} oldParent The old parent of this node
25320         * @param {Node} newParent The new parent of this node
25321         * @param {Number} index The index it was moved to
25322         */
25323        "move" : true,
25324        /**
25325         * @event insert
25326         * Fires when a new child node is inserted in a node in this tree.
25327         * @param {Tree} tree The owner tree
25328         * @param {Node} parent The parent node
25329         * @param {Node} node The child node inserted
25330         * @param {Node} refNode The child node the node was inserted before
25331         */
25332        "insert" : true,
25333        /**
25334         * @event beforeappend
25335         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
25336         * @param {Tree} tree The owner tree
25337         * @param {Node} parent The parent node
25338         * @param {Node} node The child node to be appended
25339         */
25340        "beforeappend" : true,
25341        /**
25342         * @event beforeremove
25343         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
25344         * @param {Tree} tree The owner tree
25345         * @param {Node} parent The parent node
25346         * @param {Node} node The child node to be removed
25347         */
25348        "beforeremove" : true,
25349        /**
25350         * @event beforemove
25351         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
25352         * @param {Tree} tree The owner tree
25353         * @param {Node} node The node being moved
25354         * @param {Node} oldParent The parent of the node
25355         * @param {Node} newParent The new parent the node is moving to
25356         * @param {Number} index The index it is being moved to
25357         */
25358        "beforemove" : true,
25359        /**
25360         * @event beforeinsert
25361         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
25362         * @param {Tree} tree The owner tree
25363         * @param {Node} parent The parent node
25364         * @param {Node} node The child node to be inserted
25365         * @param {Node} refNode The child node the node is being inserted before
25366         */
25367        "beforeinsert" : true
25368    });
25369
25370     Roo.data.Tree.superclass.constructor.call(this);
25371 };
25372
25373 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
25374     pathSeparator: "/",
25375
25376     proxyNodeEvent : function(){
25377         return this.fireEvent.apply(this, arguments);
25378     },
25379
25380     /**
25381      * Returns the root node for this tree.
25382      * @return {Node}
25383      */
25384     getRootNode : function(){
25385         return this.root;
25386     },
25387
25388     /**
25389      * Sets the root node for this tree.
25390      * @param {Node} node
25391      * @return {Node}
25392      */
25393     setRootNode : function(node){
25394         this.root = node;
25395         node.ownerTree = this;
25396         node.isRoot = true;
25397         this.registerNode(node);
25398         return node;
25399     },
25400
25401     /**
25402      * Gets a node in this tree by its id.
25403      * @param {String} id
25404      * @return {Node}
25405      */
25406     getNodeById : function(id){
25407         return this.nodeHash[id];
25408     },
25409
25410     registerNode : function(node){
25411         this.nodeHash[node.id] = node;
25412     },
25413
25414     unregisterNode : function(node){
25415         delete this.nodeHash[node.id];
25416     },
25417
25418     toString : function(){
25419         return "[Tree"+(this.id?" "+this.id:"")+"]";
25420     }
25421 });
25422
25423 /**
25424  * @class Roo.data.Node
25425  * @extends Roo.util.Observable
25426  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
25427  * @cfg {String} id The id for this node. If one is not specified, one is generated.
25428  * @constructor
25429  * @param {Object} attributes The attributes/config for the node
25430  */
25431 Roo.data.Node = function(attributes){
25432     /**
25433      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
25434      * @type {Object}
25435      */
25436     this.attributes = attributes || {};
25437     this.leaf = this.attributes.leaf;
25438     /**
25439      * The node id. @type String
25440      */
25441     this.id = this.attributes.id;
25442     if(!this.id){
25443         this.id = Roo.id(null, "ynode-");
25444         this.attributes.id = this.id;
25445     }
25446      
25447     
25448     /**
25449      * All child nodes of this node. @type Array
25450      */
25451     this.childNodes = [];
25452     if(!this.childNodes.indexOf){ // indexOf is a must
25453         this.childNodes.indexOf = function(o){
25454             for(var i = 0, len = this.length; i < len; i++){
25455                 if(this[i] == o) {
25456                     return i;
25457                 }
25458             }
25459             return -1;
25460         };
25461     }
25462     /**
25463      * The parent node for this node. @type Node
25464      */
25465     this.parentNode = null;
25466     /**
25467      * The first direct child node of this node, or null if this node has no child nodes. @type Node
25468      */
25469     this.firstChild = null;
25470     /**
25471      * The last direct child node of this node, or null if this node has no child nodes. @type Node
25472      */
25473     this.lastChild = null;
25474     /**
25475      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
25476      */
25477     this.previousSibling = null;
25478     /**
25479      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
25480      */
25481     this.nextSibling = null;
25482
25483     this.addEvents({
25484        /**
25485         * @event append
25486         * Fires when a new child node is appended
25487         * @param {Tree} tree The owner tree
25488         * @param {Node} this This node
25489         * @param {Node} node The newly appended node
25490         * @param {Number} index The index of the newly appended node
25491         */
25492        "append" : true,
25493        /**
25494         * @event remove
25495         * Fires when a child node is removed
25496         * @param {Tree} tree The owner tree
25497         * @param {Node} this This node
25498         * @param {Node} node The removed node
25499         */
25500        "remove" : true,
25501        /**
25502         * @event move
25503         * Fires when this node is moved to a new location in the tree
25504         * @param {Tree} tree The owner tree
25505         * @param {Node} this This node
25506         * @param {Node} oldParent The old parent of this node
25507         * @param {Node} newParent The new parent of this node
25508         * @param {Number} index The index it was moved to
25509         */
25510        "move" : true,
25511        /**
25512         * @event insert
25513         * Fires when a new child node is inserted.
25514         * @param {Tree} tree The owner tree
25515         * @param {Node} this This node
25516         * @param {Node} node The child node inserted
25517         * @param {Node} refNode The child node the node was inserted before
25518         */
25519        "insert" : true,
25520        /**
25521         * @event beforeappend
25522         * Fires before a new child is appended, return false to cancel the append.
25523         * @param {Tree} tree The owner tree
25524         * @param {Node} this This node
25525         * @param {Node} node The child node to be appended
25526         */
25527        "beforeappend" : true,
25528        /**
25529         * @event beforeremove
25530         * Fires before a child is removed, return false to cancel the remove.
25531         * @param {Tree} tree The owner tree
25532         * @param {Node} this This node
25533         * @param {Node} node The child node to be removed
25534         */
25535        "beforeremove" : true,
25536        /**
25537         * @event beforemove
25538         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
25539         * @param {Tree} tree The owner tree
25540         * @param {Node} this This node
25541         * @param {Node} oldParent The parent of this node
25542         * @param {Node} newParent The new parent this node is moving to
25543         * @param {Number} index The index it is being moved to
25544         */
25545        "beforemove" : true,
25546        /**
25547         * @event beforeinsert
25548         * Fires before a new child is inserted, return false to cancel the insert.
25549         * @param {Tree} tree The owner tree
25550         * @param {Node} this This node
25551         * @param {Node} node The child node to be inserted
25552         * @param {Node} refNode The child node the node is being inserted before
25553         */
25554        "beforeinsert" : true
25555    });
25556     this.listeners = this.attributes.listeners;
25557     Roo.data.Node.superclass.constructor.call(this);
25558 };
25559
25560 Roo.extend(Roo.data.Node, Roo.util.Observable, {
25561     fireEvent : function(evtName){
25562         // first do standard event for this node
25563         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
25564             return false;
25565         }
25566         // then bubble it up to the tree if the event wasn't cancelled
25567         var ot = this.getOwnerTree();
25568         if(ot){
25569             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
25570                 return false;
25571             }
25572         }
25573         return true;
25574     },
25575
25576     /**
25577      * Returns true if this node is a leaf
25578      * @return {Boolean}
25579      */
25580     isLeaf : function(){
25581         return this.leaf === true;
25582     },
25583
25584     // private
25585     setFirstChild : function(node){
25586         this.firstChild = node;
25587     },
25588
25589     //private
25590     setLastChild : function(node){
25591         this.lastChild = node;
25592     },
25593
25594
25595     /**
25596      * Returns true if this node is the last child of its parent
25597      * @return {Boolean}
25598      */
25599     isLast : function(){
25600        return (!this.parentNode ? true : this.parentNode.lastChild == this);
25601     },
25602
25603     /**
25604      * Returns true if this node is the first child of its parent
25605      * @return {Boolean}
25606      */
25607     isFirst : function(){
25608        return (!this.parentNode ? true : this.parentNode.firstChild == this);
25609     },
25610
25611     hasChildNodes : function(){
25612         return !this.isLeaf() && this.childNodes.length > 0;
25613     },
25614
25615     /**
25616      * Insert node(s) as the last child node of this node.
25617      * @param {Node/Array} node The node or Array of nodes to append
25618      * @return {Node} The appended node if single append, or null if an array was passed
25619      */
25620     appendChild : function(node){
25621         var multi = false;
25622         if(node instanceof Array){
25623             multi = node;
25624         }else if(arguments.length > 1){
25625             multi = arguments;
25626         }
25627         
25628         // if passed an array or multiple args do them one by one
25629         if(multi){
25630             for(var i = 0, len = multi.length; i < len; i++) {
25631                 this.appendChild(multi[i]);
25632             }
25633         }else{
25634             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
25635                 return false;
25636             }
25637             var index = this.childNodes.length;
25638             var oldParent = node.parentNode;
25639             // it's a move, make sure we move it cleanly
25640             if(oldParent){
25641                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
25642                     return false;
25643                 }
25644                 oldParent.removeChild(node);
25645             }
25646             
25647             index = this.childNodes.length;
25648             if(index == 0){
25649                 this.setFirstChild(node);
25650             }
25651             this.childNodes.push(node);
25652             node.parentNode = this;
25653             var ps = this.childNodes[index-1];
25654             if(ps){
25655                 node.previousSibling = ps;
25656                 ps.nextSibling = node;
25657             }else{
25658                 node.previousSibling = null;
25659             }
25660             node.nextSibling = null;
25661             this.setLastChild(node);
25662             node.setOwnerTree(this.getOwnerTree());
25663             this.fireEvent("append", this.ownerTree, this, node, index);
25664             if(this.ownerTree) {
25665                 this.ownerTree.fireEvent("appendnode", this, node, index);
25666             }
25667             if(oldParent){
25668                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
25669             }
25670             return node;
25671         }
25672     },
25673
25674     /**
25675      * Removes a child node from this node.
25676      * @param {Node} node The node to remove
25677      * @return {Node} The removed node
25678      */
25679     removeChild : function(node){
25680         var index = this.childNodes.indexOf(node);
25681         if(index == -1){
25682             return false;
25683         }
25684         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
25685             return false;
25686         }
25687
25688         // remove it from childNodes collection
25689         this.childNodes.splice(index, 1);
25690
25691         // update siblings
25692         if(node.previousSibling){
25693             node.previousSibling.nextSibling = node.nextSibling;
25694         }
25695         if(node.nextSibling){
25696             node.nextSibling.previousSibling = node.previousSibling;
25697         }
25698
25699         // update child refs
25700         if(this.firstChild == node){
25701             this.setFirstChild(node.nextSibling);
25702         }
25703         if(this.lastChild == node){
25704             this.setLastChild(node.previousSibling);
25705         }
25706
25707         node.setOwnerTree(null);
25708         // clear any references from the node
25709         node.parentNode = null;
25710         node.previousSibling = null;
25711         node.nextSibling = null;
25712         this.fireEvent("remove", this.ownerTree, this, node);
25713         return node;
25714     },
25715
25716     /**
25717      * Inserts the first node before the second node in this nodes childNodes collection.
25718      * @param {Node} node The node to insert
25719      * @param {Node} refNode The node to insert before (if null the node is appended)
25720      * @return {Node} The inserted node
25721      */
25722     insertBefore : function(node, refNode){
25723         if(!refNode){ // like standard Dom, refNode can be null for append
25724             return this.appendChild(node);
25725         }
25726         // nothing to do
25727         if(node == refNode){
25728             return false;
25729         }
25730
25731         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
25732             return false;
25733         }
25734         var index = this.childNodes.indexOf(refNode);
25735         var oldParent = node.parentNode;
25736         var refIndex = index;
25737
25738         // when moving internally, indexes will change after remove
25739         if(oldParent == this && this.childNodes.indexOf(node) < index){
25740             refIndex--;
25741         }
25742
25743         // it's a move, make sure we move it cleanly
25744         if(oldParent){
25745             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
25746                 return false;
25747             }
25748             oldParent.removeChild(node);
25749         }
25750         if(refIndex == 0){
25751             this.setFirstChild(node);
25752         }
25753         this.childNodes.splice(refIndex, 0, node);
25754         node.parentNode = this;
25755         var ps = this.childNodes[refIndex-1];
25756         if(ps){
25757             node.previousSibling = ps;
25758             ps.nextSibling = node;
25759         }else{
25760             node.previousSibling = null;
25761         }
25762         node.nextSibling = refNode;
25763         refNode.previousSibling = node;
25764         node.setOwnerTree(this.getOwnerTree());
25765         this.fireEvent("insert", this.ownerTree, this, node, refNode);
25766         if(oldParent){
25767             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
25768         }
25769         return node;
25770     },
25771
25772     /**
25773      * Returns the child node at the specified index.
25774      * @param {Number} index
25775      * @return {Node}
25776      */
25777     item : function(index){
25778         return this.childNodes[index];
25779     },
25780
25781     /**
25782      * Replaces one child node in this node with another.
25783      * @param {Node} newChild The replacement node
25784      * @param {Node} oldChild The node to replace
25785      * @return {Node} The replaced node
25786      */
25787     replaceChild : function(newChild, oldChild){
25788         this.insertBefore(newChild, oldChild);
25789         this.removeChild(oldChild);
25790         return oldChild;
25791     },
25792
25793     /**
25794      * Returns the index of a child node
25795      * @param {Node} node
25796      * @return {Number} The index of the node or -1 if it was not found
25797      */
25798     indexOf : function(child){
25799         return this.childNodes.indexOf(child);
25800     },
25801
25802     /**
25803      * Returns the tree this node is in.
25804      * @return {Tree}
25805      */
25806     getOwnerTree : function(){
25807         // if it doesn't have one, look for one
25808         if(!this.ownerTree){
25809             var p = this;
25810             while(p){
25811                 if(p.ownerTree){
25812                     this.ownerTree = p.ownerTree;
25813                     break;
25814                 }
25815                 p = p.parentNode;
25816             }
25817         }
25818         return this.ownerTree;
25819     },
25820
25821     /**
25822      * Returns depth of this node (the root node has a depth of 0)
25823      * @return {Number}
25824      */
25825     getDepth : function(){
25826         var depth = 0;
25827         var p = this;
25828         while(p.parentNode){
25829             ++depth;
25830             p = p.parentNode;
25831         }
25832         return depth;
25833     },
25834
25835     // private
25836     setOwnerTree : function(tree){
25837         // if it's move, we need to update everyone
25838         if(tree != this.ownerTree){
25839             if(this.ownerTree){
25840                 this.ownerTree.unregisterNode(this);
25841             }
25842             this.ownerTree = tree;
25843             var cs = this.childNodes;
25844             for(var i = 0, len = cs.length; i < len; i++) {
25845                 cs[i].setOwnerTree(tree);
25846             }
25847             if(tree){
25848                 tree.registerNode(this);
25849             }
25850         }
25851     },
25852
25853     /**
25854      * Returns the path for this node. The path can be used to expand or select this node programmatically.
25855      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
25856      * @return {String} The path
25857      */
25858     getPath : function(attr){
25859         attr = attr || "id";
25860         var p = this.parentNode;
25861         var b = [this.attributes[attr]];
25862         while(p){
25863             b.unshift(p.attributes[attr]);
25864             p = p.parentNode;
25865         }
25866         var sep = this.getOwnerTree().pathSeparator;
25867         return sep + b.join(sep);
25868     },
25869
25870     /**
25871      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
25872      * function call will be the scope provided or the current node. The arguments to the function
25873      * will be the args provided or the current node. If the function returns false at any point,
25874      * the bubble is stopped.
25875      * @param {Function} fn The function to call
25876      * @param {Object} scope (optional) The scope of the function (defaults to current node)
25877      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
25878      */
25879     bubble : function(fn, scope, args){
25880         var p = this;
25881         while(p){
25882             if(fn.call(scope || p, args || p) === false){
25883                 break;
25884             }
25885             p = p.parentNode;
25886         }
25887     },
25888
25889     /**
25890      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
25891      * function call will be the scope provided or the current node. The arguments to the function
25892      * will be the args provided or the current node. If the function returns false at any point,
25893      * the cascade is stopped on that branch.
25894      * @param {Function} fn The function to call
25895      * @param {Object} scope (optional) The scope of the function (defaults to current node)
25896      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
25897      */
25898     cascade : function(fn, scope, args){
25899         if(fn.call(scope || this, args || this) !== false){
25900             var cs = this.childNodes;
25901             for(var i = 0, len = cs.length; i < len; i++) {
25902                 cs[i].cascade(fn, scope, args);
25903             }
25904         }
25905     },
25906
25907     /**
25908      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
25909      * function call will be the scope provided or the current node. The arguments to the function
25910      * will be the args provided or the current node. If the function returns false at any point,
25911      * the iteration stops.
25912      * @param {Function} fn The function to call
25913      * @param {Object} scope (optional) The scope of the function (defaults to current node)
25914      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
25915      */
25916     eachChild : function(fn, scope, args){
25917         var cs = this.childNodes;
25918         for(var i = 0, len = cs.length; i < len; i++) {
25919                 if(fn.call(scope || this, args || cs[i]) === false){
25920                     break;
25921                 }
25922         }
25923     },
25924
25925     /**
25926      * Finds the first child that has the attribute with the specified value.
25927      * @param {String} attribute The attribute name
25928      * @param {Mixed} value The value to search for
25929      * @return {Node} The found child or null if none was found
25930      */
25931     findChild : function(attribute, value){
25932         var cs = this.childNodes;
25933         for(var i = 0, len = cs.length; i < len; i++) {
25934                 if(cs[i].attributes[attribute] == value){
25935                     return cs[i];
25936                 }
25937         }
25938         return null;
25939     },
25940
25941     /**
25942      * Finds the first child by a custom function. The child matches if the function passed
25943      * returns true.
25944      * @param {Function} fn
25945      * @param {Object} scope (optional)
25946      * @return {Node} The found child or null if none was found
25947      */
25948     findChildBy : function(fn, scope){
25949         var cs = this.childNodes;
25950         for(var i = 0, len = cs.length; i < len; i++) {
25951                 if(fn.call(scope||cs[i], cs[i]) === true){
25952                     return cs[i];
25953                 }
25954         }
25955         return null;
25956     },
25957
25958     /**
25959      * Sorts this nodes children using the supplied sort function
25960      * @param {Function} fn
25961      * @param {Object} scope (optional)
25962      */
25963     sort : function(fn, scope){
25964         var cs = this.childNodes;
25965         var len = cs.length;
25966         if(len > 0){
25967             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
25968             cs.sort(sortFn);
25969             for(var i = 0; i < len; i++){
25970                 var n = cs[i];
25971                 n.previousSibling = cs[i-1];
25972                 n.nextSibling = cs[i+1];
25973                 if(i == 0){
25974                     this.setFirstChild(n);
25975                 }
25976                 if(i == len-1){
25977                     this.setLastChild(n);
25978                 }
25979             }
25980         }
25981     },
25982
25983     /**
25984      * Returns true if this node is an ancestor (at any point) of the passed node.
25985      * @param {Node} node
25986      * @return {Boolean}
25987      */
25988     contains : function(node){
25989         return node.isAncestor(this);
25990     },
25991
25992     /**
25993      * Returns true if the passed node is an ancestor (at any point) of this node.
25994      * @param {Node} node
25995      * @return {Boolean}
25996      */
25997     isAncestor : function(node){
25998         var p = this.parentNode;
25999         while(p){
26000             if(p == node){
26001                 return true;
26002             }
26003             p = p.parentNode;
26004         }
26005         return false;
26006     },
26007
26008     toString : function(){
26009         return "[Node"+(this.id?" "+this.id:"")+"]";
26010     }
26011 });/*
26012  * Based on:
26013  * Ext JS Library 1.1.1
26014  * Copyright(c) 2006-2007, Ext JS, LLC.
26015  *
26016  * Originally Released Under LGPL - original licence link has changed is not relivant.
26017  *
26018  * Fork - LGPL
26019  * <script type="text/javascript">
26020  */
26021
26022
26023 /**
26024  * @class Roo.Shadow
26025  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
26026  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
26027  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
26028  * @constructor
26029  * Create a new Shadow
26030  * @param {Object} config The config object
26031  */
26032 Roo.Shadow = function(config){
26033     Roo.apply(this, config);
26034     if(typeof this.mode != "string"){
26035         this.mode = this.defaultMode;
26036     }
26037     var o = this.offset, a = {h: 0};
26038     var rad = Math.floor(this.offset/2);
26039     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
26040         case "drop":
26041             a.w = 0;
26042             a.l = a.t = o;
26043             a.t -= 1;
26044             if(Roo.isIE){
26045                 a.l -= this.offset + rad;
26046                 a.t -= this.offset + rad;
26047                 a.w -= rad;
26048                 a.h -= rad;
26049                 a.t += 1;
26050             }
26051         break;
26052         case "sides":
26053             a.w = (o*2);
26054             a.l = -o;
26055             a.t = o-1;
26056             if(Roo.isIE){
26057                 a.l -= (this.offset - rad);
26058                 a.t -= this.offset + rad;
26059                 a.l += 1;
26060                 a.w -= (this.offset - rad)*2;
26061                 a.w -= rad + 1;
26062                 a.h -= 1;
26063             }
26064         break;
26065         case "frame":
26066             a.w = a.h = (o*2);
26067             a.l = a.t = -o;
26068             a.t += 1;
26069             a.h -= 2;
26070             if(Roo.isIE){
26071                 a.l -= (this.offset - rad);
26072                 a.t -= (this.offset - rad);
26073                 a.l += 1;
26074                 a.w -= (this.offset + rad + 1);
26075                 a.h -= (this.offset + rad);
26076                 a.h += 1;
26077             }
26078         break;
26079     };
26080
26081     this.adjusts = a;
26082 };
26083
26084 Roo.Shadow.prototype = {
26085     /**
26086      * @cfg {String} mode
26087      * The shadow display mode.  Supports the following options:<br />
26088      * sides: Shadow displays on both sides and bottom only<br />
26089      * frame: Shadow displays equally on all four sides<br />
26090      * drop: Traditional bottom-right drop shadow (default)
26091      */
26092     /**
26093      * @cfg {String} offset
26094      * The number of pixels to offset the shadow from the element (defaults to 4)
26095      */
26096     offset: 4,
26097
26098     // private
26099     defaultMode: "drop",
26100
26101     /**
26102      * Displays the shadow under the target element
26103      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
26104      */
26105     show : function(target){
26106         target = Roo.get(target);
26107         if(!this.el){
26108             this.el = Roo.Shadow.Pool.pull();
26109             if(this.el.dom.nextSibling != target.dom){
26110                 this.el.insertBefore(target);
26111             }
26112         }
26113         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
26114         if(Roo.isIE){
26115             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
26116         }
26117         this.realign(
26118             target.getLeft(true),
26119             target.getTop(true),
26120             target.getWidth(),
26121             target.getHeight()
26122         );
26123         this.el.dom.style.display = "block";
26124     },
26125
26126     /**
26127      * Returns true if the shadow is visible, else false
26128      */
26129     isVisible : function(){
26130         return this.el ? true : false;  
26131     },
26132
26133     /**
26134      * Direct alignment when values are already available. Show must be called at least once before
26135      * calling this method to ensure it is initialized.
26136      * @param {Number} left The target element left position
26137      * @param {Number} top The target element top position
26138      * @param {Number} width The target element width
26139      * @param {Number} height The target element height
26140      */
26141     realign : function(l, t, w, h){
26142         if(!this.el){
26143             return;
26144         }
26145         var a = this.adjusts, d = this.el.dom, s = d.style;
26146         var iea = 0;
26147         s.left = (l+a.l)+"px";
26148         s.top = (t+a.t)+"px";
26149         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
26150  
26151         if(s.width != sws || s.height != shs){
26152             s.width = sws;
26153             s.height = shs;
26154             if(!Roo.isIE){
26155                 var cn = d.childNodes;
26156                 var sww = Math.max(0, (sw-12))+"px";
26157                 cn[0].childNodes[1].style.width = sww;
26158                 cn[1].childNodes[1].style.width = sww;
26159                 cn[2].childNodes[1].style.width = sww;
26160                 cn[1].style.height = Math.max(0, (sh-12))+"px";
26161             }
26162         }
26163     },
26164
26165     /**
26166      * Hides this shadow
26167      */
26168     hide : function(){
26169         if(this.el){
26170             this.el.dom.style.display = "none";
26171             Roo.Shadow.Pool.push(this.el);
26172             delete this.el;
26173         }
26174     },
26175
26176     /**
26177      * Adjust the z-index of this shadow
26178      * @param {Number} zindex The new z-index
26179      */
26180     setZIndex : function(z){
26181         this.zIndex = z;
26182         if(this.el){
26183             this.el.setStyle("z-index", z);
26184         }
26185     }
26186 };
26187
26188 // Private utility class that manages the internal Shadow cache
26189 Roo.Shadow.Pool = function(){
26190     var p = [];
26191     var markup = Roo.isIE ?
26192                  '<div class="x-ie-shadow"></div>' :
26193                  '<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>';
26194     return {
26195         pull : function(){
26196             var sh = p.shift();
26197             if(!sh){
26198                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
26199                 sh.autoBoxAdjust = false;
26200             }
26201             return sh;
26202         },
26203
26204         push : function(sh){
26205             p.push(sh);
26206         }
26207     };
26208 }();/*
26209  * Based on:
26210  * Ext JS Library 1.1.1
26211  * Copyright(c) 2006-2007, Ext JS, LLC.
26212  *
26213  * Originally Released Under LGPL - original licence link has changed is not relivant.
26214  *
26215  * Fork - LGPL
26216  * <script type="text/javascript">
26217  */
26218
26219
26220 /**
26221  * @class Roo.SplitBar
26222  * @extends Roo.util.Observable
26223  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
26224  * <br><br>
26225  * Usage:
26226  * <pre><code>
26227 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
26228                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
26229 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
26230 split.minSize = 100;
26231 split.maxSize = 600;
26232 split.animate = true;
26233 split.on('moved', splitterMoved);
26234 </code></pre>
26235  * @constructor
26236  * Create a new SplitBar
26237  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
26238  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
26239  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
26240  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
26241                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
26242                         position of the SplitBar).
26243  */
26244 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
26245     
26246     /** @private */
26247     this.el = Roo.get(dragElement, true);
26248     this.el.dom.unselectable = "on";
26249     /** @private */
26250     this.resizingEl = Roo.get(resizingElement, true);
26251
26252     /**
26253      * @private
26254      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
26255      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
26256      * @type Number
26257      */
26258     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
26259     
26260     /**
26261      * The minimum size of the resizing element. (Defaults to 0)
26262      * @type Number
26263      */
26264     this.minSize = 0;
26265     
26266     /**
26267      * The maximum size of the resizing element. (Defaults to 2000)
26268      * @type Number
26269      */
26270     this.maxSize = 2000;
26271     
26272     /**
26273      * Whether to animate the transition to the new size
26274      * @type Boolean
26275      */
26276     this.animate = false;
26277     
26278     /**
26279      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
26280      * @type Boolean
26281      */
26282     this.useShim = false;
26283     
26284     /** @private */
26285     this.shim = null;
26286     
26287     if(!existingProxy){
26288         /** @private */
26289         this.proxy = Roo.SplitBar.createProxy(this.orientation);
26290     }else{
26291         this.proxy = Roo.get(existingProxy).dom;
26292     }
26293     /** @private */
26294     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
26295     
26296     /** @private */
26297     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
26298     
26299     /** @private */
26300     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
26301     
26302     /** @private */
26303     this.dragSpecs = {};
26304     
26305     /**
26306      * @private The adapter to use to positon and resize elements
26307      */
26308     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
26309     this.adapter.init(this);
26310     
26311     if(this.orientation == Roo.SplitBar.HORIZONTAL){
26312         /** @private */
26313         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
26314         this.el.addClass("x-splitbar-h");
26315     }else{
26316         /** @private */
26317         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
26318         this.el.addClass("x-splitbar-v");
26319     }
26320     
26321     this.addEvents({
26322         /**
26323          * @event resize
26324          * Fires when the splitter is moved (alias for {@link #event-moved})
26325          * @param {Roo.SplitBar} this
26326          * @param {Number} newSize the new width or height
26327          */
26328         "resize" : true,
26329         /**
26330          * @event moved
26331          * Fires when the splitter is moved
26332          * @param {Roo.SplitBar} this
26333          * @param {Number} newSize the new width or height
26334          */
26335         "moved" : true,
26336         /**
26337          * @event beforeresize
26338          * Fires before the splitter is dragged
26339          * @param {Roo.SplitBar} this
26340          */
26341         "beforeresize" : true,
26342
26343         "beforeapply" : true
26344     });
26345
26346     Roo.util.Observable.call(this);
26347 };
26348
26349 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
26350     onStartProxyDrag : function(x, y){
26351         this.fireEvent("beforeresize", this);
26352         if(!this.overlay){
26353             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
26354             o.unselectable();
26355             o.enableDisplayMode("block");
26356             // all splitbars share the same overlay
26357             Roo.SplitBar.prototype.overlay = o;
26358         }
26359         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
26360         this.overlay.show();
26361         Roo.get(this.proxy).setDisplayed("block");
26362         var size = this.adapter.getElementSize(this);
26363         this.activeMinSize = this.getMinimumSize();;
26364         this.activeMaxSize = this.getMaximumSize();;
26365         var c1 = size - this.activeMinSize;
26366         var c2 = Math.max(this.activeMaxSize - size, 0);
26367         if(this.orientation == Roo.SplitBar.HORIZONTAL){
26368             this.dd.resetConstraints();
26369             this.dd.setXConstraint(
26370                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
26371                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
26372             );
26373             this.dd.setYConstraint(0, 0);
26374         }else{
26375             this.dd.resetConstraints();
26376             this.dd.setXConstraint(0, 0);
26377             this.dd.setYConstraint(
26378                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
26379                 this.placement == Roo.SplitBar.TOP ? c2 : c1
26380             );
26381          }
26382         this.dragSpecs.startSize = size;
26383         this.dragSpecs.startPoint = [x, y];
26384         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
26385     },
26386     
26387     /** 
26388      * @private Called after the drag operation by the DDProxy
26389      */
26390     onEndProxyDrag : function(e){
26391         Roo.get(this.proxy).setDisplayed(false);
26392         var endPoint = Roo.lib.Event.getXY(e);
26393         if(this.overlay){
26394             this.overlay.hide();
26395         }
26396         var newSize;
26397         if(this.orientation == Roo.SplitBar.HORIZONTAL){
26398             newSize = this.dragSpecs.startSize + 
26399                 (this.placement == Roo.SplitBar.LEFT ?
26400                     endPoint[0] - this.dragSpecs.startPoint[0] :
26401                     this.dragSpecs.startPoint[0] - endPoint[0]
26402                 );
26403         }else{
26404             newSize = this.dragSpecs.startSize + 
26405                 (this.placement == Roo.SplitBar.TOP ?
26406                     endPoint[1] - this.dragSpecs.startPoint[1] :
26407                     this.dragSpecs.startPoint[1] - endPoint[1]
26408                 );
26409         }
26410         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
26411         if(newSize != this.dragSpecs.startSize){
26412             if(this.fireEvent('beforeapply', this, newSize) !== false){
26413                 this.adapter.setElementSize(this, newSize);
26414                 this.fireEvent("moved", this, newSize);
26415                 this.fireEvent("resize", this, newSize);
26416             }
26417         }
26418     },
26419     
26420     /**
26421      * Get the adapter this SplitBar uses
26422      * @return The adapter object
26423      */
26424     getAdapter : function(){
26425         return this.adapter;
26426     },
26427     
26428     /**
26429      * Set the adapter this SplitBar uses
26430      * @param {Object} adapter A SplitBar adapter object
26431      */
26432     setAdapter : function(adapter){
26433         this.adapter = adapter;
26434         this.adapter.init(this);
26435     },
26436     
26437     /**
26438      * Gets the minimum size for the resizing element
26439      * @return {Number} The minimum size
26440      */
26441     getMinimumSize : function(){
26442         return this.minSize;
26443     },
26444     
26445     /**
26446      * Sets the minimum size for the resizing element
26447      * @param {Number} minSize The minimum size
26448      */
26449     setMinimumSize : function(minSize){
26450         this.minSize = minSize;
26451     },
26452     
26453     /**
26454      * Gets the maximum size for the resizing element
26455      * @return {Number} The maximum size
26456      */
26457     getMaximumSize : function(){
26458         return this.maxSize;
26459     },
26460     
26461     /**
26462      * Sets the maximum size for the resizing element
26463      * @param {Number} maxSize The maximum size
26464      */
26465     setMaximumSize : function(maxSize){
26466         this.maxSize = maxSize;
26467     },
26468     
26469     /**
26470      * Sets the initialize size for the resizing element
26471      * @param {Number} size The initial size
26472      */
26473     setCurrentSize : function(size){
26474         var oldAnimate = this.animate;
26475         this.animate = false;
26476         this.adapter.setElementSize(this, size);
26477         this.animate = oldAnimate;
26478     },
26479     
26480     /**
26481      * Destroy this splitbar. 
26482      * @param {Boolean} removeEl True to remove the element
26483      */
26484     destroy : function(removeEl){
26485         if(this.shim){
26486             this.shim.remove();
26487         }
26488         this.dd.unreg();
26489         this.proxy.parentNode.removeChild(this.proxy);
26490         if(removeEl){
26491             this.el.remove();
26492         }
26493     }
26494 });
26495
26496 /**
26497  * @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.
26498  */
26499 Roo.SplitBar.createProxy = function(dir){
26500     var proxy = new Roo.Element(document.createElement("div"));
26501     proxy.unselectable();
26502     var cls = 'x-splitbar-proxy';
26503     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
26504     document.body.appendChild(proxy.dom);
26505     return proxy.dom;
26506 };
26507
26508 /** 
26509  * @class Roo.SplitBar.BasicLayoutAdapter
26510  * Default Adapter. It assumes the splitter and resizing element are not positioned
26511  * elements and only gets/sets the width of the element. Generally used for table based layouts.
26512  */
26513 Roo.SplitBar.BasicLayoutAdapter = function(){
26514 };
26515
26516 Roo.SplitBar.BasicLayoutAdapter.prototype = {
26517     // do nothing for now
26518     init : function(s){
26519     
26520     },
26521     /**
26522      * Called before drag operations to get the current size of the resizing element. 
26523      * @param {Roo.SplitBar} s The SplitBar using this adapter
26524      */
26525      getElementSize : function(s){
26526         if(s.orientation == Roo.SplitBar.HORIZONTAL){
26527             return s.resizingEl.getWidth();
26528         }else{
26529             return s.resizingEl.getHeight();
26530         }
26531     },
26532     
26533     /**
26534      * Called after drag operations to set the size of the resizing element.
26535      * @param {Roo.SplitBar} s The SplitBar using this adapter
26536      * @param {Number} newSize The new size to set
26537      * @param {Function} onComplete A function to be invoked when resizing is complete
26538      */
26539     setElementSize : function(s, newSize, onComplete){
26540         if(s.orientation == Roo.SplitBar.HORIZONTAL){
26541             if(!s.animate){
26542                 s.resizingEl.setWidth(newSize);
26543                 if(onComplete){
26544                     onComplete(s, newSize);
26545                 }
26546             }else{
26547                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
26548             }
26549         }else{
26550             
26551             if(!s.animate){
26552                 s.resizingEl.setHeight(newSize);
26553                 if(onComplete){
26554                     onComplete(s, newSize);
26555                 }
26556             }else{
26557                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
26558             }
26559         }
26560     }
26561 };
26562
26563 /** 
26564  *@class Roo.SplitBar.AbsoluteLayoutAdapter
26565  * @extends Roo.SplitBar.BasicLayoutAdapter
26566  * Adapter that  moves the splitter element to align with the resized sizing element. 
26567  * Used with an absolute positioned SplitBar.
26568  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
26569  * document.body, make sure you assign an id to the body element.
26570  */
26571 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
26572     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
26573     this.container = Roo.get(container);
26574 };
26575
26576 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
26577     init : function(s){
26578         this.basic.init(s);
26579     },
26580     
26581     getElementSize : function(s){
26582         return this.basic.getElementSize(s);
26583     },
26584     
26585     setElementSize : function(s, newSize, onComplete){
26586         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
26587     },
26588     
26589     moveSplitter : function(s){
26590         var yes = Roo.SplitBar;
26591         switch(s.placement){
26592             case yes.LEFT:
26593                 s.el.setX(s.resizingEl.getRight());
26594                 break;
26595             case yes.RIGHT:
26596                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
26597                 break;
26598             case yes.TOP:
26599                 s.el.setY(s.resizingEl.getBottom());
26600                 break;
26601             case yes.BOTTOM:
26602                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
26603                 break;
26604         }
26605     }
26606 };
26607
26608 /**
26609  * Orientation constant - Create a vertical SplitBar
26610  * @static
26611  * @type Number
26612  */
26613 Roo.SplitBar.VERTICAL = 1;
26614
26615 /**
26616  * Orientation constant - Create a horizontal SplitBar
26617  * @static
26618  * @type Number
26619  */
26620 Roo.SplitBar.HORIZONTAL = 2;
26621
26622 /**
26623  * Placement constant - The resizing element is to the left of the splitter element
26624  * @static
26625  * @type Number
26626  */
26627 Roo.SplitBar.LEFT = 1;
26628
26629 /**
26630  * Placement constant - The resizing element is to the right of the splitter element
26631  * @static
26632  * @type Number
26633  */
26634 Roo.SplitBar.RIGHT = 2;
26635
26636 /**
26637  * Placement constant - The resizing element is positioned above the splitter element
26638  * @static
26639  * @type Number
26640  */
26641 Roo.SplitBar.TOP = 3;
26642
26643 /**
26644  * Placement constant - The resizing element is positioned under splitter element
26645  * @static
26646  * @type Number
26647  */
26648 Roo.SplitBar.BOTTOM = 4;
26649 /*
26650  * Based on:
26651  * Ext JS Library 1.1.1
26652  * Copyright(c) 2006-2007, Ext JS, LLC.
26653  *
26654  * Originally Released Under LGPL - original licence link has changed is not relivant.
26655  *
26656  * Fork - LGPL
26657  * <script type="text/javascript">
26658  */
26659
26660 /**
26661  * @class Roo.View
26662  * @extends Roo.util.Observable
26663  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
26664  * This class also supports single and multi selection modes. <br>
26665  * Create a data model bound view:
26666  <pre><code>
26667  var store = new Roo.data.Store(...);
26668
26669  var view = new Roo.View({
26670     el : "my-element",
26671     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
26672  
26673     singleSelect: true,
26674     selectedClass: "ydataview-selected",
26675     store: store
26676  });
26677
26678  // listen for node click?
26679  view.on("click", function(vw, index, node, e){
26680  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
26681  });
26682
26683  // load XML data
26684  dataModel.load("foobar.xml");
26685  </code></pre>
26686  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
26687  * <br><br>
26688  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
26689  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
26690  * 
26691  * Note: old style constructor is still suported (container, template, config)
26692  * 
26693  * @constructor
26694  * Create a new View
26695  * @param {Object} config The config object
26696  * 
26697  */
26698 Roo.View = function(config, depreciated_tpl, depreciated_config){
26699     
26700     this.parent = false;
26701     
26702     if (typeof(depreciated_tpl) == 'undefined') {
26703         // new way.. - universal constructor.
26704         Roo.apply(this, config);
26705         this.el  = Roo.get(this.el);
26706     } else {
26707         // old format..
26708         this.el  = Roo.get(config);
26709         this.tpl = depreciated_tpl;
26710         Roo.apply(this, depreciated_config);
26711     }
26712     this.wrapEl  = this.el.wrap().wrap();
26713     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
26714     
26715     
26716     if(typeof(this.tpl) == "string"){
26717         this.tpl = new Roo.Template(this.tpl);
26718     } else {
26719         // support xtype ctors..
26720         this.tpl = new Roo.factory(this.tpl, Roo);
26721     }
26722     
26723     
26724     this.tpl.compile();
26725     
26726     /** @private */
26727     this.addEvents({
26728         /**
26729          * @event beforeclick
26730          * Fires before a click is processed. Returns false to cancel the default action.
26731          * @param {Roo.View} this
26732          * @param {Number} index The index of the target node
26733          * @param {HTMLElement} node The target node
26734          * @param {Roo.EventObject} e The raw event object
26735          */
26736             "beforeclick" : true,
26737         /**
26738          * @event click
26739          * Fires when a template node is clicked.
26740          * @param {Roo.View} this
26741          * @param {Number} index The index of the target node
26742          * @param {HTMLElement} node The target node
26743          * @param {Roo.EventObject} e The raw event object
26744          */
26745             "click" : true,
26746         /**
26747          * @event dblclick
26748          * Fires when a template node is double clicked.
26749          * @param {Roo.View} this
26750          * @param {Number} index The index of the target node
26751          * @param {HTMLElement} node The target node
26752          * @param {Roo.EventObject} e The raw event object
26753          */
26754             "dblclick" : true,
26755         /**
26756          * @event contextmenu
26757          * Fires when a template node is right clicked.
26758          * @param {Roo.View} this
26759          * @param {Number} index The index of the target node
26760          * @param {HTMLElement} node The target node
26761          * @param {Roo.EventObject} e The raw event object
26762          */
26763             "contextmenu" : true,
26764         /**
26765          * @event selectionchange
26766          * Fires when the selected nodes change.
26767          * @param {Roo.View} this
26768          * @param {Array} selections Array of the selected nodes
26769          */
26770             "selectionchange" : true,
26771     
26772         /**
26773          * @event beforeselect
26774          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
26775          * @param {Roo.View} this
26776          * @param {HTMLElement} node The node to be selected
26777          * @param {Array} selections Array of currently selected nodes
26778          */
26779             "beforeselect" : true,
26780         /**
26781          * @event preparedata
26782          * Fires on every row to render, to allow you to change the data.
26783          * @param {Roo.View} this
26784          * @param {Object} data to be rendered (change this)
26785          */
26786           "preparedata" : true
26787           
26788           
26789         });
26790
26791
26792
26793     this.el.on({
26794         "click": this.onClick,
26795         "dblclick": this.onDblClick,
26796         "contextmenu": this.onContextMenu,
26797         scope:this
26798     });
26799
26800     this.selections = [];
26801     this.nodes = [];
26802     this.cmp = new Roo.CompositeElementLite([]);
26803     if(this.store){
26804         this.store = Roo.factory(this.store, Roo.data);
26805         this.setStore(this.store, true);
26806     }
26807     
26808     if ( this.footer && this.footer.xtype) {
26809            
26810          var fctr = this.wrapEl.appendChild(document.createElement("div"));
26811         
26812         this.footer.dataSource = this.store;
26813         this.footer.container = fctr;
26814         this.footer = Roo.factory(this.footer, Roo);
26815         fctr.insertFirst(this.el);
26816         
26817         // this is a bit insane - as the paging toolbar seems to detach the el..
26818 //        dom.parentNode.parentNode.parentNode
26819          // they get detached?
26820     }
26821     
26822     
26823     Roo.View.superclass.constructor.call(this);
26824     
26825     
26826 };
26827
26828 Roo.extend(Roo.View, Roo.util.Observable, {
26829     
26830      /**
26831      * @cfg {Roo.data.Store} store Data store to load data from.
26832      */
26833     store : false,
26834     
26835     /**
26836      * @cfg {String|Roo.Element} el The container element.
26837      */
26838     el : '',
26839     
26840     /**
26841      * @cfg {String|Roo.Template} tpl The template used by this View 
26842      */
26843     tpl : false,
26844     /**
26845      * @cfg {String} dataName the named area of the template to use as the data area
26846      *                          Works with domtemplates roo-name="name"
26847      */
26848     dataName: false,
26849     /**
26850      * @cfg {String} selectedClass The css class to add to selected nodes
26851      */
26852     selectedClass : "x-view-selected",
26853      /**
26854      * @cfg {String} emptyText The empty text to show when nothing is loaded.
26855      */
26856     emptyText : "",
26857     
26858     /**
26859      * @cfg {String} text to display on mask (default Loading)
26860      */
26861     mask : false,
26862     /**
26863      * @cfg {Boolean} multiSelect Allow multiple selection
26864      */
26865     multiSelect : false,
26866     /**
26867      * @cfg {Boolean} singleSelect Allow single selection
26868      */
26869     singleSelect:  false,
26870     
26871     /**
26872      * @cfg {Boolean} toggleSelect - selecting 
26873      */
26874     toggleSelect : false,
26875     
26876     /**
26877      * @cfg {Boolean} tickable - selecting 
26878      */
26879     tickable : false,
26880     
26881     /**
26882      * Returns the element this view is bound to.
26883      * @return {Roo.Element}
26884      */
26885     getEl : function(){
26886         return this.wrapEl;
26887     },
26888     
26889     
26890
26891     /**
26892      * Refreshes the view. - called by datachanged on the store. - do not call directly.
26893      */
26894     refresh : function(){
26895         //Roo.log('refresh');
26896         var t = this.tpl;
26897         
26898         // if we are using something like 'domtemplate', then
26899         // the what gets used is:
26900         // t.applySubtemplate(NAME, data, wrapping data..)
26901         // the outer template then get' applied with
26902         //     the store 'extra data'
26903         // and the body get's added to the
26904         //      roo-name="data" node?
26905         //      <span class='roo-tpl-{name}'></span> ?????
26906         
26907         
26908         
26909         this.clearSelections();
26910         this.el.update("");
26911         var html = [];
26912         var records = this.store.getRange();
26913         if(records.length < 1) {
26914             
26915             // is this valid??  = should it render a template??
26916             
26917             this.el.update(this.emptyText);
26918             return;
26919         }
26920         var el = this.el;
26921         if (this.dataName) {
26922             this.el.update(t.apply(this.store.meta)); //????
26923             el = this.el.child('.roo-tpl-' + this.dataName);
26924         }
26925         
26926         for(var i = 0, len = records.length; i < len; i++){
26927             var data = this.prepareData(records[i].data, i, records[i]);
26928             this.fireEvent("preparedata", this, data, i, records[i]);
26929             
26930             var d = Roo.apply({}, data);
26931             
26932             if(this.tickable){
26933                 Roo.apply(d, {'roo-id' : Roo.id()});
26934                 
26935                 var _this = this;
26936             
26937                 Roo.each(this.parent.item, function(item){
26938                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
26939                         return;
26940                     }
26941                     Roo.apply(d, {'roo-data-checked' : 'checked'});
26942                 });
26943             }
26944             
26945             html[html.length] = Roo.util.Format.trim(
26946                 this.dataName ?
26947                     t.applySubtemplate(this.dataName, d, this.store.meta) :
26948                     t.apply(d)
26949             );
26950         }
26951         
26952         
26953         
26954         el.update(html.join(""));
26955         this.nodes = el.dom.childNodes;
26956         this.updateIndexes(0);
26957     },
26958     
26959
26960     /**
26961      * Function to override to reformat the data that is sent to
26962      * the template for each node.
26963      * DEPRICATED - use the preparedata event handler.
26964      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
26965      * a JSON object for an UpdateManager bound view).
26966      */
26967     prepareData : function(data, index, record)
26968     {
26969         this.fireEvent("preparedata", this, data, index, record);
26970         return data;
26971     },
26972
26973     onUpdate : function(ds, record){
26974         // Roo.log('on update');   
26975         this.clearSelections();
26976         var index = this.store.indexOf(record);
26977         var n = this.nodes[index];
26978         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
26979         n.parentNode.removeChild(n);
26980         this.updateIndexes(index, index);
26981     },
26982
26983     
26984     
26985 // --------- FIXME     
26986     onAdd : function(ds, records, index)
26987     {
26988         //Roo.log(['on Add', ds, records, index] );        
26989         this.clearSelections();
26990         if(this.nodes.length == 0){
26991             this.refresh();
26992             return;
26993         }
26994         var n = this.nodes[index];
26995         for(var i = 0, len = records.length; i < len; i++){
26996             var d = this.prepareData(records[i].data, i, records[i]);
26997             if(n){
26998                 this.tpl.insertBefore(n, d);
26999             }else{
27000                 
27001                 this.tpl.append(this.el, d);
27002             }
27003         }
27004         this.updateIndexes(index);
27005     },
27006
27007     onRemove : function(ds, record, index){
27008        // Roo.log('onRemove');
27009         this.clearSelections();
27010         var el = this.dataName  ?
27011             this.el.child('.roo-tpl-' + this.dataName) :
27012             this.el; 
27013         
27014         el.dom.removeChild(this.nodes[index]);
27015         this.updateIndexes(index);
27016     },
27017
27018     /**
27019      * Refresh an individual node.
27020      * @param {Number} index
27021      */
27022     refreshNode : function(index){
27023         this.onUpdate(this.store, this.store.getAt(index));
27024     },
27025
27026     updateIndexes : function(startIndex, endIndex){
27027         var ns = this.nodes;
27028         startIndex = startIndex || 0;
27029         endIndex = endIndex || ns.length - 1;
27030         for(var i = startIndex; i <= endIndex; i++){
27031             ns[i].nodeIndex = i;
27032         }
27033     },
27034
27035     /**
27036      * Changes the data store this view uses and refresh the view.
27037      * @param {Store} store
27038      */
27039     setStore : function(store, initial){
27040         if(!initial && this.store){
27041             this.store.un("datachanged", this.refresh);
27042             this.store.un("add", this.onAdd);
27043             this.store.un("remove", this.onRemove);
27044             this.store.un("update", this.onUpdate);
27045             this.store.un("clear", this.refresh);
27046             this.store.un("beforeload", this.onBeforeLoad);
27047             this.store.un("load", this.onLoad);
27048             this.store.un("loadexception", this.onLoad);
27049         }
27050         if(store){
27051           
27052             store.on("datachanged", this.refresh, this);
27053             store.on("add", this.onAdd, this);
27054             store.on("remove", this.onRemove, this);
27055             store.on("update", this.onUpdate, this);
27056             store.on("clear", this.refresh, this);
27057             store.on("beforeload", this.onBeforeLoad, this);
27058             store.on("load", this.onLoad, this);
27059             store.on("loadexception", this.onLoad, this);
27060         }
27061         
27062         if(store){
27063             this.refresh();
27064         }
27065     },
27066     /**
27067      * onbeforeLoad - masks the loading area.
27068      *
27069      */
27070     onBeforeLoad : function(store,opts)
27071     {
27072          //Roo.log('onBeforeLoad');   
27073         if (!opts.add) {
27074             this.el.update("");
27075         }
27076         this.el.mask(this.mask ? this.mask : "Loading" ); 
27077     },
27078     onLoad : function ()
27079     {
27080         this.el.unmask();
27081     },
27082     
27083
27084     /**
27085      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
27086      * @param {HTMLElement} node
27087      * @return {HTMLElement} The template node
27088      */
27089     findItemFromChild : function(node){
27090         var el = this.dataName  ?
27091             this.el.child('.roo-tpl-' + this.dataName,true) :
27092             this.el.dom; 
27093         
27094         if(!node || node.parentNode == el){
27095                     return node;
27096             }
27097             var p = node.parentNode;
27098             while(p && p != el){
27099             if(p.parentNode == el){
27100                 return p;
27101             }
27102             p = p.parentNode;
27103         }
27104             return null;
27105     },
27106
27107     /** @ignore */
27108     onClick : function(e){
27109         var item = this.findItemFromChild(e.getTarget());
27110         if(item){
27111             var index = this.indexOf(item);
27112             if(this.onItemClick(item, index, e) !== false){
27113                 this.fireEvent("click", this, index, item, e);
27114             }
27115         }else{
27116             this.clearSelections();
27117         }
27118     },
27119
27120     /** @ignore */
27121     onContextMenu : function(e){
27122         var item = this.findItemFromChild(e.getTarget());
27123         if(item){
27124             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
27125         }
27126     },
27127
27128     /** @ignore */
27129     onDblClick : function(e){
27130         var item = this.findItemFromChild(e.getTarget());
27131         if(item){
27132             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
27133         }
27134     },
27135
27136     onItemClick : function(item, index, e)
27137     {
27138         if(this.fireEvent("beforeclick", this, index, item, e) === false){
27139             return false;
27140         }
27141         if (this.toggleSelect) {
27142             var m = this.isSelected(item) ? 'unselect' : 'select';
27143             //Roo.log(m);
27144             var _t = this;
27145             _t[m](item, true, false);
27146             return true;
27147         }
27148         if(this.multiSelect || this.singleSelect){
27149             if(this.multiSelect && e.shiftKey && this.lastSelection){
27150                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
27151             }else{
27152                 this.select(item, this.multiSelect && e.ctrlKey);
27153                 this.lastSelection = item;
27154             }
27155             
27156             if(!this.tickable){
27157                 e.preventDefault();
27158             }
27159             
27160         }
27161         return true;
27162     },
27163
27164     /**
27165      * Get the number of selected nodes.
27166      * @return {Number}
27167      */
27168     getSelectionCount : function(){
27169         return this.selections.length;
27170     },
27171
27172     /**
27173      * Get the currently selected nodes.
27174      * @return {Array} An array of HTMLElements
27175      */
27176     getSelectedNodes : function(){
27177         return this.selections;
27178     },
27179
27180     /**
27181      * Get the indexes of the selected nodes.
27182      * @return {Array}
27183      */
27184     getSelectedIndexes : function(){
27185         var indexes = [], s = this.selections;
27186         for(var i = 0, len = s.length; i < len; i++){
27187             indexes.push(s[i].nodeIndex);
27188         }
27189         return indexes;
27190     },
27191
27192     /**
27193      * Clear all selections
27194      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
27195      */
27196     clearSelections : function(suppressEvent){
27197         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
27198             this.cmp.elements = this.selections;
27199             this.cmp.removeClass(this.selectedClass);
27200             this.selections = [];
27201             if(!suppressEvent){
27202                 this.fireEvent("selectionchange", this, this.selections);
27203             }
27204         }
27205     },
27206
27207     /**
27208      * Returns true if the passed node is selected
27209      * @param {HTMLElement/Number} node The node or node index
27210      * @return {Boolean}
27211      */
27212     isSelected : function(node){
27213         var s = this.selections;
27214         if(s.length < 1){
27215             return false;
27216         }
27217         node = this.getNode(node);
27218         return s.indexOf(node) !== -1;
27219     },
27220
27221     /**
27222      * Selects nodes.
27223      * @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
27224      * @param {Boolean} keepExisting (optional) true to keep existing selections
27225      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
27226      */
27227     select : function(nodeInfo, keepExisting, suppressEvent){
27228         if(nodeInfo instanceof Array){
27229             if(!keepExisting){
27230                 this.clearSelections(true);
27231             }
27232             for(var i = 0, len = nodeInfo.length; i < len; i++){
27233                 this.select(nodeInfo[i], true, true);
27234             }
27235             return;
27236         } 
27237         var node = this.getNode(nodeInfo);
27238         if(!node || this.isSelected(node)){
27239             return; // already selected.
27240         }
27241         if(!keepExisting){
27242             this.clearSelections(true);
27243         }
27244         
27245         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
27246             Roo.fly(node).addClass(this.selectedClass);
27247             this.selections.push(node);
27248             if(!suppressEvent){
27249                 this.fireEvent("selectionchange", this, this.selections);
27250             }
27251         }
27252         
27253         
27254     },
27255       /**
27256      * Unselects nodes.
27257      * @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
27258      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
27259      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
27260      */
27261     unselect : function(nodeInfo, keepExisting, suppressEvent)
27262     {
27263         if(nodeInfo instanceof Array){
27264             Roo.each(this.selections, function(s) {
27265                 this.unselect(s, nodeInfo);
27266             }, this);
27267             return;
27268         }
27269         var node = this.getNode(nodeInfo);
27270         if(!node || !this.isSelected(node)){
27271             //Roo.log("not selected");
27272             return; // not selected.
27273         }
27274         // fireevent???
27275         var ns = [];
27276         Roo.each(this.selections, function(s) {
27277             if (s == node ) {
27278                 Roo.fly(node).removeClass(this.selectedClass);
27279
27280                 return;
27281             }
27282             ns.push(s);
27283         },this);
27284         
27285         this.selections= ns;
27286         this.fireEvent("selectionchange", this, this.selections);
27287     },
27288
27289     /**
27290      * Gets a template node.
27291      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
27292      * @return {HTMLElement} The node or null if it wasn't found
27293      */
27294     getNode : function(nodeInfo){
27295         if(typeof nodeInfo == "string"){
27296             return document.getElementById(nodeInfo);
27297         }else if(typeof nodeInfo == "number"){
27298             return this.nodes[nodeInfo];
27299         }
27300         return nodeInfo;
27301     },
27302
27303     /**
27304      * Gets a range template nodes.
27305      * @param {Number} startIndex
27306      * @param {Number} endIndex
27307      * @return {Array} An array of nodes
27308      */
27309     getNodes : function(start, end){
27310         var ns = this.nodes;
27311         start = start || 0;
27312         end = typeof end == "undefined" ? ns.length - 1 : end;
27313         var nodes = [];
27314         if(start <= end){
27315             for(var i = start; i <= end; i++){
27316                 nodes.push(ns[i]);
27317             }
27318         } else{
27319             for(var i = start; i >= end; i--){
27320                 nodes.push(ns[i]);
27321             }
27322         }
27323         return nodes;
27324     },
27325
27326     /**
27327      * Finds the index of the passed node
27328      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
27329      * @return {Number} The index of the node or -1
27330      */
27331     indexOf : function(node){
27332         node = this.getNode(node);
27333         if(typeof node.nodeIndex == "number"){
27334             return node.nodeIndex;
27335         }
27336         var ns = this.nodes;
27337         for(var i = 0, len = ns.length; i < len; i++){
27338             if(ns[i] == node){
27339                 return i;
27340             }
27341         }
27342         return -1;
27343     }
27344 });
27345 /*
27346  * Based on:
27347  * Ext JS Library 1.1.1
27348  * Copyright(c) 2006-2007, Ext JS, LLC.
27349  *
27350  * Originally Released Under LGPL - original licence link has changed is not relivant.
27351  *
27352  * Fork - LGPL
27353  * <script type="text/javascript">
27354  */
27355
27356 /**
27357  * @class Roo.JsonView
27358  * @extends Roo.View
27359  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
27360 <pre><code>
27361 var view = new Roo.JsonView({
27362     container: "my-element",
27363     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
27364     multiSelect: true, 
27365     jsonRoot: "data" 
27366 });
27367
27368 // listen for node click?
27369 view.on("click", function(vw, index, node, e){
27370     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
27371 });
27372
27373 // direct load of JSON data
27374 view.load("foobar.php");
27375
27376 // Example from my blog list
27377 var tpl = new Roo.Template(
27378     '&lt;div class="entry"&gt;' +
27379     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
27380     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
27381     "&lt;/div&gt;&lt;hr /&gt;"
27382 );
27383
27384 var moreView = new Roo.JsonView({
27385     container :  "entry-list", 
27386     template : tpl,
27387     jsonRoot: "posts"
27388 });
27389 moreView.on("beforerender", this.sortEntries, this);
27390 moreView.load({
27391     url: "/blog/get-posts.php",
27392     params: "allposts=true",
27393     text: "Loading Blog Entries..."
27394 });
27395 </code></pre>
27396
27397 * Note: old code is supported with arguments : (container, template, config)
27398
27399
27400  * @constructor
27401  * Create a new JsonView
27402  * 
27403  * @param {Object} config The config object
27404  * 
27405  */
27406 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
27407     
27408     
27409     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
27410
27411     var um = this.el.getUpdateManager();
27412     um.setRenderer(this);
27413     um.on("update", this.onLoad, this);
27414     um.on("failure", this.onLoadException, this);
27415
27416     /**
27417      * @event beforerender
27418      * Fires before rendering of the downloaded JSON data.
27419      * @param {Roo.JsonView} this
27420      * @param {Object} data The JSON data loaded
27421      */
27422     /**
27423      * @event load
27424      * Fires when data is loaded.
27425      * @param {Roo.JsonView} this
27426      * @param {Object} data The JSON data loaded
27427      * @param {Object} response The raw Connect response object
27428      */
27429     /**
27430      * @event loadexception
27431      * Fires when loading fails.
27432      * @param {Roo.JsonView} this
27433      * @param {Object} response The raw Connect response object
27434      */
27435     this.addEvents({
27436         'beforerender' : true,
27437         'load' : true,
27438         'loadexception' : true
27439     });
27440 };
27441 Roo.extend(Roo.JsonView, Roo.View, {
27442     /**
27443      * @type {String} The root property in the loaded JSON object that contains the data
27444      */
27445     jsonRoot : "",
27446
27447     /**
27448      * Refreshes the view.
27449      */
27450     refresh : function(){
27451         this.clearSelections();
27452         this.el.update("");
27453         var html = [];
27454         var o = this.jsonData;
27455         if(o && o.length > 0){
27456             for(var i = 0, len = o.length; i < len; i++){
27457                 var data = this.prepareData(o[i], i, o);
27458                 html[html.length] = this.tpl.apply(data);
27459             }
27460         }else{
27461             html.push(this.emptyText);
27462         }
27463         this.el.update(html.join(""));
27464         this.nodes = this.el.dom.childNodes;
27465         this.updateIndexes(0);
27466     },
27467
27468     /**
27469      * 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.
27470      * @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:
27471      <pre><code>
27472      view.load({
27473          url: "your-url.php",
27474          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
27475          callback: yourFunction,
27476          scope: yourObject, //(optional scope)
27477          discardUrl: false,
27478          nocache: false,
27479          text: "Loading...",
27480          timeout: 30,
27481          scripts: false
27482      });
27483      </code></pre>
27484      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
27485      * 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.
27486      * @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}
27487      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
27488      * @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.
27489      */
27490     load : function(){
27491         var um = this.el.getUpdateManager();
27492         um.update.apply(um, arguments);
27493     },
27494
27495     // note - render is a standard framework call...
27496     // using it for the response is really flaky... - it's called by UpdateManager normally, except when called by the XComponent/addXtype.
27497     render : function(el, response){
27498         
27499         this.clearSelections();
27500         this.el.update("");
27501         var o;
27502         try{
27503             if (response != '') {
27504                 o = Roo.util.JSON.decode(response.responseText);
27505                 if(this.jsonRoot){
27506                     
27507                     o = o[this.jsonRoot];
27508                 }
27509             }
27510         } catch(e){
27511         }
27512         /**
27513          * The current JSON data or null
27514          */
27515         this.jsonData = o;
27516         this.beforeRender();
27517         this.refresh();
27518     },
27519
27520 /**
27521  * Get the number of records in the current JSON dataset
27522  * @return {Number}
27523  */
27524     getCount : function(){
27525         return this.jsonData ? this.jsonData.length : 0;
27526     },
27527
27528 /**
27529  * Returns the JSON object for the specified node(s)
27530  * @param {HTMLElement/Array} node The node or an array of nodes
27531  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
27532  * you get the JSON object for the node
27533  */
27534     getNodeData : function(node){
27535         if(node instanceof Array){
27536             var data = [];
27537             for(var i = 0, len = node.length; i < len; i++){
27538                 data.push(this.getNodeData(node[i]));
27539             }
27540             return data;
27541         }
27542         return this.jsonData[this.indexOf(node)] || null;
27543     },
27544
27545     beforeRender : function(){
27546         this.snapshot = this.jsonData;
27547         if(this.sortInfo){
27548             this.sort.apply(this, this.sortInfo);
27549         }
27550         this.fireEvent("beforerender", this, this.jsonData);
27551     },
27552
27553     onLoad : function(el, o){
27554         this.fireEvent("load", this, this.jsonData, o);
27555     },
27556
27557     onLoadException : function(el, o){
27558         this.fireEvent("loadexception", this, o);
27559     },
27560
27561 /**
27562  * Filter the data by a specific property.
27563  * @param {String} property A property on your JSON objects
27564  * @param {String/RegExp} value Either string that the property values
27565  * should start with, or a RegExp to test against the property
27566  */
27567     filter : function(property, value){
27568         if(this.jsonData){
27569             var data = [];
27570             var ss = this.snapshot;
27571             if(typeof value == "string"){
27572                 var vlen = value.length;
27573                 if(vlen == 0){
27574                     this.clearFilter();
27575                     return;
27576                 }
27577                 value = value.toLowerCase();
27578                 for(var i = 0, len = ss.length; i < len; i++){
27579                     var o = ss[i];
27580                     if(o[property].substr(0, vlen).toLowerCase() == value){
27581                         data.push(o);
27582                     }
27583                 }
27584             } else if(value.exec){ // regex?
27585                 for(var i = 0, len = ss.length; i < len; i++){
27586                     var o = ss[i];
27587                     if(value.test(o[property])){
27588                         data.push(o);
27589                     }
27590                 }
27591             } else{
27592                 return;
27593             }
27594             this.jsonData = data;
27595             this.refresh();
27596         }
27597     },
27598
27599 /**
27600  * Filter by a function. The passed function will be called with each
27601  * object in the current dataset. If the function returns true the value is kept,
27602  * otherwise it is filtered.
27603  * @param {Function} fn
27604  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
27605  */
27606     filterBy : function(fn, scope){
27607         if(this.jsonData){
27608             var data = [];
27609             var ss = this.snapshot;
27610             for(var i = 0, len = ss.length; i < len; i++){
27611                 var o = ss[i];
27612                 if(fn.call(scope || this, o)){
27613                     data.push(o);
27614                 }
27615             }
27616             this.jsonData = data;
27617             this.refresh();
27618         }
27619     },
27620
27621 /**
27622  * Clears the current filter.
27623  */
27624     clearFilter : function(){
27625         if(this.snapshot && this.jsonData != this.snapshot){
27626             this.jsonData = this.snapshot;
27627             this.refresh();
27628         }
27629     },
27630
27631
27632 /**
27633  * Sorts the data for this view and refreshes it.
27634  * @param {String} property A property on your JSON objects to sort on
27635  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
27636  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
27637  */
27638     sort : function(property, dir, sortType){
27639         this.sortInfo = Array.prototype.slice.call(arguments, 0);
27640         if(this.jsonData){
27641             var p = property;
27642             var dsc = dir && dir.toLowerCase() == "desc";
27643             var f = function(o1, o2){
27644                 var v1 = sortType ? sortType(o1[p]) : o1[p];
27645                 var v2 = sortType ? sortType(o2[p]) : o2[p];
27646                 ;
27647                 if(v1 < v2){
27648                     return dsc ? +1 : -1;
27649                 } else if(v1 > v2){
27650                     return dsc ? -1 : +1;
27651                 } else{
27652                     return 0;
27653                 }
27654             };
27655             this.jsonData.sort(f);
27656             this.refresh();
27657             if(this.jsonData != this.snapshot){
27658                 this.snapshot.sort(f);
27659             }
27660         }
27661     }
27662 });/*
27663  * Based on:
27664  * Ext JS Library 1.1.1
27665  * Copyright(c) 2006-2007, Ext JS, LLC.
27666  *
27667  * Originally Released Under LGPL - original licence link has changed is not relivant.
27668  *
27669  * Fork - LGPL
27670  * <script type="text/javascript">
27671  */
27672  
27673
27674 /**
27675  * @class Roo.ColorPalette
27676  * @extends Roo.Component
27677  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
27678  * Here's an example of typical usage:
27679  * <pre><code>
27680 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
27681 cp.render('my-div');
27682
27683 cp.on('select', function(palette, selColor){
27684     // do something with selColor
27685 });
27686 </code></pre>
27687  * @constructor
27688  * Create a new ColorPalette
27689  * @param {Object} config The config object
27690  */
27691 Roo.ColorPalette = function(config){
27692     Roo.ColorPalette.superclass.constructor.call(this, config);
27693     this.addEvents({
27694         /**
27695              * @event select
27696              * Fires when a color is selected
27697              * @param {ColorPalette} this
27698              * @param {String} color The 6-digit color hex code (without the # symbol)
27699              */
27700         select: true
27701     });
27702
27703     if(this.handler){
27704         this.on("select", this.handler, this.scope, true);
27705     }
27706 };
27707 Roo.extend(Roo.ColorPalette, Roo.Component, {
27708     /**
27709      * @cfg {String} itemCls
27710      * The CSS class to apply to the containing element (defaults to "x-color-palette")
27711      */
27712     itemCls : "x-color-palette",
27713     /**
27714      * @cfg {String} value
27715      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
27716      * the hex codes are case-sensitive.
27717      */
27718     value : null,
27719     clickEvent:'click',
27720     // private
27721     ctype: "Roo.ColorPalette",
27722
27723     /**
27724      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
27725      */
27726     allowReselect : false,
27727
27728     /**
27729      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
27730      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
27731      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
27732      * of colors with the width setting until the box is symmetrical.</p>
27733      * <p>You can override individual colors if needed:</p>
27734      * <pre><code>
27735 var cp = new Roo.ColorPalette();
27736 cp.colors[0] = "FF0000";  // change the first box to red
27737 </code></pre>
27738
27739 Or you can provide a custom array of your own for complete control:
27740 <pre><code>
27741 var cp = new Roo.ColorPalette();
27742 cp.colors = ["000000", "993300", "333300"];
27743 </code></pre>
27744      * @type Array
27745      */
27746     colors : [
27747         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
27748         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
27749         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
27750         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
27751         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
27752     ],
27753
27754     // private
27755     onRender : function(container, position){
27756         var t = new Roo.MasterTemplate(
27757             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
27758         );
27759         var c = this.colors;
27760         for(var i = 0, len = c.length; i < len; i++){
27761             t.add([c[i]]);
27762         }
27763         var el = document.createElement("div");
27764         el.className = this.itemCls;
27765         t.overwrite(el);
27766         container.dom.insertBefore(el, position);
27767         this.el = Roo.get(el);
27768         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
27769         if(this.clickEvent != 'click'){
27770             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
27771         }
27772     },
27773
27774     // private
27775     afterRender : function(){
27776         Roo.ColorPalette.superclass.afterRender.call(this);
27777         if(this.value){
27778             var s = this.value;
27779             this.value = null;
27780             this.select(s);
27781         }
27782     },
27783
27784     // private
27785     handleClick : function(e, t){
27786         e.preventDefault();
27787         if(!this.disabled){
27788             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
27789             this.select(c.toUpperCase());
27790         }
27791     },
27792
27793     /**
27794      * Selects the specified color in the palette (fires the select event)
27795      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
27796      */
27797     select : function(color){
27798         color = color.replace("#", "");
27799         if(color != this.value || this.allowReselect){
27800             var el = this.el;
27801             if(this.value){
27802                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
27803             }
27804             el.child("a.color-"+color).addClass("x-color-palette-sel");
27805             this.value = color;
27806             this.fireEvent("select", this, color);
27807         }
27808     }
27809 });/*
27810  * Based on:
27811  * Ext JS Library 1.1.1
27812  * Copyright(c) 2006-2007, Ext JS, LLC.
27813  *
27814  * Originally Released Under LGPL - original licence link has changed is not relivant.
27815  *
27816  * Fork - LGPL
27817  * <script type="text/javascript">
27818  */
27819  
27820 /**
27821  * @class Roo.DatePicker
27822  * @extends Roo.Component
27823  * Simple date picker class.
27824  * @constructor
27825  * Create a new DatePicker
27826  * @param {Object} config The config object
27827  */
27828 Roo.DatePicker = function(config){
27829     Roo.DatePicker.superclass.constructor.call(this, config);
27830
27831     this.value = config && config.value ?
27832                  config.value.clearTime() : new Date().clearTime();
27833
27834     this.addEvents({
27835         /**
27836              * @event select
27837              * Fires when a date is selected
27838              * @param {DatePicker} this
27839              * @param {Date} date The selected date
27840              */
27841         'select': true,
27842         /**
27843              * @event monthchange
27844              * Fires when the displayed month changes 
27845              * @param {DatePicker} this
27846              * @param {Date} date The selected month
27847              */
27848         'monthchange': true
27849     });
27850
27851     if(this.handler){
27852         this.on("select", this.handler,  this.scope || this);
27853     }
27854     // build the disabledDatesRE
27855     if(!this.disabledDatesRE && this.disabledDates){
27856         var dd = this.disabledDates;
27857         var re = "(?:";
27858         for(var i = 0; i < dd.length; i++){
27859             re += dd[i];
27860             if(i != dd.length-1) {
27861                 re += "|";
27862             }
27863         }
27864         this.disabledDatesRE = new RegExp(re + ")");
27865     }
27866 };
27867
27868 Roo.extend(Roo.DatePicker, Roo.Component, {
27869     /**
27870      * @cfg {String} todayText
27871      * The text to display on the button that selects the current date (defaults to "Today")
27872      */
27873     todayText : "Today",
27874     /**
27875      * @cfg {String} okText
27876      * The text to display on the ok button
27877      */
27878     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
27879     /**
27880      * @cfg {String} cancelText
27881      * The text to display on the cancel button
27882      */
27883     cancelText : "Cancel",
27884     /**
27885      * @cfg {String} todayTip
27886      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
27887      */
27888     todayTip : "{0} (Spacebar)",
27889     /**
27890      * @cfg {Date} minDate
27891      * Minimum allowable date (JavaScript date object, defaults to null)
27892      */
27893     minDate : null,
27894     /**
27895      * @cfg {Date} maxDate
27896      * Maximum allowable date (JavaScript date object, defaults to null)
27897      */
27898     maxDate : null,
27899     /**
27900      * @cfg {String} minText
27901      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
27902      */
27903     minText : "This date is before the minimum date",
27904     /**
27905      * @cfg {String} maxText
27906      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
27907      */
27908     maxText : "This date is after the maximum date",
27909     /**
27910      * @cfg {String} format
27911      * The default date format string which can be overriden for localization support.  The format must be
27912      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
27913      */
27914     format : "m/d/y",
27915     /**
27916      * @cfg {Array} disabledDays
27917      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
27918      */
27919     disabledDays : null,
27920     /**
27921      * @cfg {String} disabledDaysText
27922      * The tooltip to display when the date falls on a disabled day (defaults to "")
27923      */
27924     disabledDaysText : "",
27925     /**
27926      * @cfg {RegExp} disabledDatesRE
27927      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
27928      */
27929     disabledDatesRE : null,
27930     /**
27931      * @cfg {String} disabledDatesText
27932      * The tooltip text to display when the date falls on a disabled date (defaults to "")
27933      */
27934     disabledDatesText : "",
27935     /**
27936      * @cfg {Boolean} constrainToViewport
27937      * True to constrain the date picker to the viewport (defaults to true)
27938      */
27939     constrainToViewport : true,
27940     /**
27941      * @cfg {Array} monthNames
27942      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
27943      */
27944     monthNames : Date.monthNames,
27945     /**
27946      * @cfg {Array} dayNames
27947      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
27948      */
27949     dayNames : Date.dayNames,
27950     /**
27951      * @cfg {String} nextText
27952      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
27953      */
27954     nextText: 'Next Month (Control+Right)',
27955     /**
27956      * @cfg {String} prevText
27957      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
27958      */
27959     prevText: 'Previous Month (Control+Left)',
27960     /**
27961      * @cfg {String} monthYearText
27962      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
27963      */
27964     monthYearText: 'Choose a month (Control+Up/Down to move years)',
27965     /**
27966      * @cfg {Number} startDay
27967      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
27968      */
27969     startDay : 0,
27970     /**
27971      * @cfg {Bool} showClear
27972      * Show a clear button (usefull for date form elements that can be blank.)
27973      */
27974     
27975     showClear: false,
27976     
27977     /**
27978      * Sets the value of the date field
27979      * @param {Date} value The date to set
27980      */
27981     setValue : function(value){
27982         var old = this.value;
27983         
27984         if (typeof(value) == 'string') {
27985          
27986             value = Date.parseDate(value, this.format);
27987         }
27988         if (!value) {
27989             value = new Date();
27990         }
27991         
27992         this.value = value.clearTime(true);
27993         if(this.el){
27994             this.update(this.value);
27995         }
27996     },
27997
27998     /**
27999      * Gets the current selected value of the date field
28000      * @return {Date} The selected date
28001      */
28002     getValue : function(){
28003         return this.value;
28004     },
28005
28006     // private
28007     focus : function(){
28008         if(this.el){
28009             this.update(this.activeDate);
28010         }
28011     },
28012
28013     // privateval
28014     onRender : function(container, position){
28015         
28016         var m = [
28017              '<table cellspacing="0">',
28018                 '<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>',
28019                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
28020         var dn = this.dayNames;
28021         for(var i = 0; i < 7; i++){
28022             var d = this.startDay+i;
28023             if(d > 6){
28024                 d = d-7;
28025             }
28026             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
28027         }
28028         m[m.length] = "</tr></thead><tbody><tr>";
28029         for(var i = 0; i < 42; i++) {
28030             if(i % 7 == 0 && i != 0){
28031                 m[m.length] = "</tr><tr>";
28032             }
28033             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
28034         }
28035         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
28036             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
28037
28038         var el = document.createElement("div");
28039         el.className = "x-date-picker";
28040         el.innerHTML = m.join("");
28041
28042         container.dom.insertBefore(el, position);
28043
28044         this.el = Roo.get(el);
28045         this.eventEl = Roo.get(el.firstChild);
28046
28047         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
28048             handler: this.showPrevMonth,
28049             scope: this,
28050             preventDefault:true,
28051             stopDefault:true
28052         });
28053
28054         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
28055             handler: this.showNextMonth,
28056             scope: this,
28057             preventDefault:true,
28058             stopDefault:true
28059         });
28060
28061         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
28062
28063         this.monthPicker = this.el.down('div.x-date-mp');
28064         this.monthPicker.enableDisplayMode('block');
28065         
28066         var kn = new Roo.KeyNav(this.eventEl, {
28067             "left" : function(e){
28068                 e.ctrlKey ?
28069                     this.showPrevMonth() :
28070                     this.update(this.activeDate.add("d", -1));
28071             },
28072
28073             "right" : function(e){
28074                 e.ctrlKey ?
28075                     this.showNextMonth() :
28076                     this.update(this.activeDate.add("d", 1));
28077             },
28078
28079             "up" : function(e){
28080                 e.ctrlKey ?
28081                     this.showNextYear() :
28082                     this.update(this.activeDate.add("d", -7));
28083             },
28084
28085             "down" : function(e){
28086                 e.ctrlKey ?
28087                     this.showPrevYear() :
28088                     this.update(this.activeDate.add("d", 7));
28089             },
28090
28091             "pageUp" : function(e){
28092                 this.showNextMonth();
28093             },
28094
28095             "pageDown" : function(e){
28096                 this.showPrevMonth();
28097             },
28098
28099             "enter" : function(e){
28100                 e.stopPropagation();
28101                 return true;
28102             },
28103
28104             scope : this
28105         });
28106
28107         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
28108
28109         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
28110
28111         this.el.unselectable();
28112         
28113         this.cells = this.el.select("table.x-date-inner tbody td");
28114         this.textNodes = this.el.query("table.x-date-inner tbody span");
28115
28116         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
28117             text: "&#160;",
28118             tooltip: this.monthYearText
28119         });
28120
28121         this.mbtn.on('click', this.showMonthPicker, this);
28122         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
28123
28124
28125         var today = (new Date()).dateFormat(this.format);
28126         
28127         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
28128         if (this.showClear) {
28129             baseTb.add( new Roo.Toolbar.Fill());
28130         }
28131         baseTb.add({
28132             text: String.format(this.todayText, today),
28133             tooltip: String.format(this.todayTip, today),
28134             handler: this.selectToday,
28135             scope: this
28136         });
28137         
28138         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
28139             
28140         //});
28141         if (this.showClear) {
28142             
28143             baseTb.add( new Roo.Toolbar.Fill());
28144             baseTb.add({
28145                 text: '&#160;',
28146                 cls: 'x-btn-icon x-btn-clear',
28147                 handler: function() {
28148                     //this.value = '';
28149                     this.fireEvent("select", this, '');
28150                 },
28151                 scope: this
28152             });
28153         }
28154         
28155         
28156         if(Roo.isIE){
28157             this.el.repaint();
28158         }
28159         this.update(this.value);
28160     },
28161
28162     createMonthPicker : function(){
28163         if(!this.monthPicker.dom.firstChild){
28164             var buf = ['<table border="0" cellspacing="0">'];
28165             for(var i = 0; i < 6; i++){
28166                 buf.push(
28167                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
28168                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
28169                     i == 0 ?
28170                     '<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>' :
28171                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
28172                 );
28173             }
28174             buf.push(
28175                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
28176                     this.okText,
28177                     '</button><button type="button" class="x-date-mp-cancel">',
28178                     this.cancelText,
28179                     '</button></td></tr>',
28180                 '</table>'
28181             );
28182             this.monthPicker.update(buf.join(''));
28183             this.monthPicker.on('click', this.onMonthClick, this);
28184             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
28185
28186             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
28187             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
28188
28189             this.mpMonths.each(function(m, a, i){
28190                 i += 1;
28191                 if((i%2) == 0){
28192                     m.dom.xmonth = 5 + Math.round(i * .5);
28193                 }else{
28194                     m.dom.xmonth = Math.round((i-1) * .5);
28195                 }
28196             });
28197         }
28198     },
28199
28200     showMonthPicker : function(){
28201         this.createMonthPicker();
28202         var size = this.el.getSize();
28203         this.monthPicker.setSize(size);
28204         this.monthPicker.child('table').setSize(size);
28205
28206         this.mpSelMonth = (this.activeDate || this.value).getMonth();
28207         this.updateMPMonth(this.mpSelMonth);
28208         this.mpSelYear = (this.activeDate || this.value).getFullYear();
28209         this.updateMPYear(this.mpSelYear);
28210
28211         this.monthPicker.slideIn('t', {duration:.2});
28212     },
28213
28214     updateMPYear : function(y){
28215         this.mpyear = y;
28216         var ys = this.mpYears.elements;
28217         for(var i = 1; i <= 10; i++){
28218             var td = ys[i-1], y2;
28219             if((i%2) == 0){
28220                 y2 = y + Math.round(i * .5);
28221                 td.firstChild.innerHTML = y2;
28222                 td.xyear = y2;
28223             }else{
28224                 y2 = y - (5-Math.round(i * .5));
28225                 td.firstChild.innerHTML = y2;
28226                 td.xyear = y2;
28227             }
28228             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
28229         }
28230     },
28231
28232     updateMPMonth : function(sm){
28233         this.mpMonths.each(function(m, a, i){
28234             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
28235         });
28236     },
28237
28238     selectMPMonth: function(m){
28239         
28240     },
28241
28242     onMonthClick : function(e, t){
28243         e.stopEvent();
28244         var el = new Roo.Element(t), pn;
28245         if(el.is('button.x-date-mp-cancel')){
28246             this.hideMonthPicker();
28247         }
28248         else if(el.is('button.x-date-mp-ok')){
28249             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
28250             this.hideMonthPicker();
28251         }
28252         else if(pn = el.up('td.x-date-mp-month', 2)){
28253             this.mpMonths.removeClass('x-date-mp-sel');
28254             pn.addClass('x-date-mp-sel');
28255             this.mpSelMonth = pn.dom.xmonth;
28256         }
28257         else if(pn = el.up('td.x-date-mp-year', 2)){
28258             this.mpYears.removeClass('x-date-mp-sel');
28259             pn.addClass('x-date-mp-sel');
28260             this.mpSelYear = pn.dom.xyear;
28261         }
28262         else if(el.is('a.x-date-mp-prev')){
28263             this.updateMPYear(this.mpyear-10);
28264         }
28265         else if(el.is('a.x-date-mp-next')){
28266             this.updateMPYear(this.mpyear+10);
28267         }
28268     },
28269
28270     onMonthDblClick : function(e, t){
28271         e.stopEvent();
28272         var el = new Roo.Element(t), pn;
28273         if(pn = el.up('td.x-date-mp-month', 2)){
28274             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
28275             this.hideMonthPicker();
28276         }
28277         else if(pn = el.up('td.x-date-mp-year', 2)){
28278             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
28279             this.hideMonthPicker();
28280         }
28281     },
28282
28283     hideMonthPicker : function(disableAnim){
28284         if(this.monthPicker){
28285             if(disableAnim === true){
28286                 this.monthPicker.hide();
28287             }else{
28288                 this.monthPicker.slideOut('t', {duration:.2});
28289             }
28290         }
28291     },
28292
28293     // private
28294     showPrevMonth : function(e){
28295         this.update(this.activeDate.add("mo", -1));
28296     },
28297
28298     // private
28299     showNextMonth : function(e){
28300         this.update(this.activeDate.add("mo", 1));
28301     },
28302
28303     // private
28304     showPrevYear : function(){
28305         this.update(this.activeDate.add("y", -1));
28306     },
28307
28308     // private
28309     showNextYear : function(){
28310         this.update(this.activeDate.add("y", 1));
28311     },
28312
28313     // private
28314     handleMouseWheel : function(e){
28315         var delta = e.getWheelDelta();
28316         if(delta > 0){
28317             this.showPrevMonth();
28318             e.stopEvent();
28319         } else if(delta < 0){
28320             this.showNextMonth();
28321             e.stopEvent();
28322         }
28323     },
28324
28325     // private
28326     handleDateClick : function(e, t){
28327         e.stopEvent();
28328         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
28329             this.setValue(new Date(t.dateValue));
28330             this.fireEvent("select", this, this.value);
28331         }
28332     },
28333
28334     // private
28335     selectToday : function(){
28336         this.setValue(new Date().clearTime());
28337         this.fireEvent("select", this, this.value);
28338     },
28339
28340     // private
28341     update : function(date)
28342     {
28343         var vd = this.activeDate;
28344         this.activeDate = date;
28345         if(vd && this.el){
28346             var t = date.getTime();
28347             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
28348                 this.cells.removeClass("x-date-selected");
28349                 this.cells.each(function(c){
28350                    if(c.dom.firstChild.dateValue == t){
28351                        c.addClass("x-date-selected");
28352                        setTimeout(function(){
28353                             try{c.dom.firstChild.focus();}catch(e){}
28354                        }, 50);
28355                        return false;
28356                    }
28357                 });
28358                 return;
28359             }
28360         }
28361         
28362         var days = date.getDaysInMonth();
28363         var firstOfMonth = date.getFirstDateOfMonth();
28364         var startingPos = firstOfMonth.getDay()-this.startDay;
28365
28366         if(startingPos <= this.startDay){
28367             startingPos += 7;
28368         }
28369
28370         var pm = date.add("mo", -1);
28371         var prevStart = pm.getDaysInMonth()-startingPos;
28372
28373         var cells = this.cells.elements;
28374         var textEls = this.textNodes;
28375         days += startingPos;
28376
28377         // convert everything to numbers so it's fast
28378         var day = 86400000;
28379         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
28380         var today = new Date().clearTime().getTime();
28381         var sel = date.clearTime().getTime();
28382         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
28383         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
28384         var ddMatch = this.disabledDatesRE;
28385         var ddText = this.disabledDatesText;
28386         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
28387         var ddaysText = this.disabledDaysText;
28388         var format = this.format;
28389
28390         var setCellClass = function(cal, cell){
28391             cell.title = "";
28392             var t = d.getTime();
28393             cell.firstChild.dateValue = t;
28394             if(t == today){
28395                 cell.className += " x-date-today";
28396                 cell.title = cal.todayText;
28397             }
28398             if(t == sel){
28399                 cell.className += " x-date-selected";
28400                 setTimeout(function(){
28401                     try{cell.firstChild.focus();}catch(e){}
28402                 }, 50);
28403             }
28404             // disabling
28405             if(t < min) {
28406                 cell.className = " x-date-disabled";
28407                 cell.title = cal.minText;
28408                 return;
28409             }
28410             if(t > max) {
28411                 cell.className = " x-date-disabled";
28412                 cell.title = cal.maxText;
28413                 return;
28414             }
28415             if(ddays){
28416                 if(ddays.indexOf(d.getDay()) != -1){
28417                     cell.title = ddaysText;
28418                     cell.className = " x-date-disabled";
28419                 }
28420             }
28421             if(ddMatch && format){
28422                 var fvalue = d.dateFormat(format);
28423                 if(ddMatch.test(fvalue)){
28424                     cell.title = ddText.replace("%0", fvalue);
28425                     cell.className = " x-date-disabled";
28426                 }
28427             }
28428         };
28429
28430         var i = 0;
28431         for(; i < startingPos; i++) {
28432             textEls[i].innerHTML = (++prevStart);
28433             d.setDate(d.getDate()+1);
28434             cells[i].className = "x-date-prevday";
28435             setCellClass(this, cells[i]);
28436         }
28437         for(; i < days; i++){
28438             intDay = i - startingPos + 1;
28439             textEls[i].innerHTML = (intDay);
28440             d.setDate(d.getDate()+1);
28441             cells[i].className = "x-date-active";
28442             setCellClass(this, cells[i]);
28443         }
28444         var extraDays = 0;
28445         for(; i < 42; i++) {
28446              textEls[i].innerHTML = (++extraDays);
28447              d.setDate(d.getDate()+1);
28448              cells[i].className = "x-date-nextday";
28449              setCellClass(this, cells[i]);
28450         }
28451
28452         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
28453         this.fireEvent('monthchange', this, date);
28454         
28455         if(!this.internalRender){
28456             var main = this.el.dom.firstChild;
28457             var w = main.offsetWidth;
28458             this.el.setWidth(w + this.el.getBorderWidth("lr"));
28459             Roo.fly(main).setWidth(w);
28460             this.internalRender = true;
28461             // opera does not respect the auto grow header center column
28462             // then, after it gets a width opera refuses to recalculate
28463             // without a second pass
28464             if(Roo.isOpera && !this.secondPass){
28465                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
28466                 this.secondPass = true;
28467                 this.update.defer(10, this, [date]);
28468             }
28469         }
28470         
28471         
28472     }
28473 });        /*
28474  * Based on:
28475  * Ext JS Library 1.1.1
28476  * Copyright(c) 2006-2007, Ext JS, LLC.
28477  *
28478  * Originally Released Under LGPL - original licence link has changed is not relivant.
28479  *
28480  * Fork - LGPL
28481  * <script type="text/javascript">
28482  */
28483 /**
28484  * @class Roo.TabPanel
28485  * @extends Roo.util.Observable
28486  * A lightweight tab container.
28487  * <br><br>
28488  * Usage:
28489  * <pre><code>
28490 // basic tabs 1, built from existing content
28491 var tabs = new Roo.TabPanel("tabs1");
28492 tabs.addTab("script", "View Script");
28493 tabs.addTab("markup", "View Markup");
28494 tabs.activate("script");
28495
28496 // more advanced tabs, built from javascript
28497 var jtabs = new Roo.TabPanel("jtabs");
28498 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
28499
28500 // set up the UpdateManager
28501 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
28502 var updater = tab2.getUpdateManager();
28503 updater.setDefaultUrl("ajax1.htm");
28504 tab2.on('activate', updater.refresh, updater, true);
28505
28506 // Use setUrl for Ajax loading
28507 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
28508 tab3.setUrl("ajax2.htm", null, true);
28509
28510 // Disabled tab
28511 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
28512 tab4.disable();
28513
28514 jtabs.activate("jtabs-1");
28515  * </code></pre>
28516  * @constructor
28517  * Create a new TabPanel.
28518  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
28519  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
28520  */
28521 Roo.TabPanel = function(container, config){
28522     /**
28523     * The container element for this TabPanel.
28524     * @type Roo.Element
28525     */
28526     this.el = Roo.get(container, true);
28527     if(config){
28528         if(typeof config == "boolean"){
28529             this.tabPosition = config ? "bottom" : "top";
28530         }else{
28531             Roo.apply(this, config);
28532         }
28533     }
28534     if(this.tabPosition == "bottom"){
28535         this.bodyEl = Roo.get(this.createBody(this.el.dom));
28536         this.el.addClass("x-tabs-bottom");
28537     }
28538     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
28539     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
28540     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
28541     if(Roo.isIE){
28542         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
28543     }
28544     if(this.tabPosition != "bottom"){
28545         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
28546          * @type Roo.Element
28547          */
28548         this.bodyEl = Roo.get(this.createBody(this.el.dom));
28549         this.el.addClass("x-tabs-top");
28550     }
28551     this.items = [];
28552
28553     this.bodyEl.setStyle("position", "relative");
28554
28555     this.active = null;
28556     this.activateDelegate = this.activate.createDelegate(this);
28557
28558     this.addEvents({
28559         /**
28560          * @event tabchange
28561          * Fires when the active tab changes
28562          * @param {Roo.TabPanel} this
28563          * @param {Roo.TabPanelItem} activePanel The new active tab
28564          */
28565         "tabchange": true,
28566         /**
28567          * @event beforetabchange
28568          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
28569          * @param {Roo.TabPanel} this
28570          * @param {Object} e Set cancel to true on this object to cancel the tab change
28571          * @param {Roo.TabPanelItem} tab The tab being changed to
28572          */
28573         "beforetabchange" : true
28574     });
28575
28576     Roo.EventManager.onWindowResize(this.onResize, this);
28577     this.cpad = this.el.getPadding("lr");
28578     this.hiddenCount = 0;
28579
28580
28581     // toolbar on the tabbar support...
28582     if (this.toolbar) {
28583         var tcfg = this.toolbar;
28584         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
28585         this.toolbar = new Roo.Toolbar(tcfg);
28586         if (Roo.isSafari) {
28587             var tbl = tcfg.container.child('table', true);
28588             tbl.setAttribute('width', '100%');
28589         }
28590         
28591     }
28592    
28593
28594
28595     Roo.TabPanel.superclass.constructor.call(this);
28596 };
28597
28598 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
28599     /*
28600      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
28601      */
28602     tabPosition : "top",
28603     /*
28604      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
28605      */
28606     currentTabWidth : 0,
28607     /*
28608      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
28609      */
28610     minTabWidth : 40,
28611     /*
28612      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
28613      */
28614     maxTabWidth : 250,
28615     /*
28616      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
28617      */
28618     preferredTabWidth : 175,
28619     /*
28620      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
28621      */
28622     resizeTabs : false,
28623     /*
28624      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
28625      */
28626     monitorResize : true,
28627     /*
28628      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
28629      */
28630     toolbar : false,
28631
28632     /**
28633      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
28634      * @param {String} id The id of the div to use <b>or create</b>
28635      * @param {String} text The text for the tab
28636      * @param {String} content (optional) Content to put in the TabPanelItem body
28637      * @param {Boolean} closable (optional) True to create a close icon on the tab
28638      * @return {Roo.TabPanelItem} The created TabPanelItem
28639      */
28640     addTab : function(id, text, content, closable){
28641         var item = new Roo.TabPanelItem(this, id, text, closable);
28642         this.addTabItem(item);
28643         if(content){
28644             item.setContent(content);
28645         }
28646         return item;
28647     },
28648
28649     /**
28650      * Returns the {@link Roo.TabPanelItem} with the specified id/index
28651      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
28652      * @return {Roo.TabPanelItem}
28653      */
28654     getTab : function(id){
28655         return this.items[id];
28656     },
28657
28658     /**
28659      * Hides the {@link Roo.TabPanelItem} with the specified id/index
28660      * @param {String/Number} id The id or index of the TabPanelItem to hide.
28661      */
28662     hideTab : function(id){
28663         var t = this.items[id];
28664         if(!t.isHidden()){
28665            t.setHidden(true);
28666            this.hiddenCount++;
28667            this.autoSizeTabs();
28668         }
28669     },
28670
28671     /**
28672      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
28673      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
28674      */
28675     unhideTab : function(id){
28676         var t = this.items[id];
28677         if(t.isHidden()){
28678            t.setHidden(false);
28679            this.hiddenCount--;
28680            this.autoSizeTabs();
28681         }
28682     },
28683
28684     /**
28685      * Adds an existing {@link Roo.TabPanelItem}.
28686      * @param {Roo.TabPanelItem} item The TabPanelItem to add
28687      */
28688     addTabItem : function(item){
28689         this.items[item.id] = item;
28690         this.items.push(item);
28691         if(this.resizeTabs){
28692            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
28693            this.autoSizeTabs();
28694         }else{
28695             item.autoSize();
28696         }
28697     },
28698
28699     /**
28700      * Removes a {@link Roo.TabPanelItem}.
28701      * @param {String/Number} id The id or index of the TabPanelItem to remove.
28702      */
28703     removeTab : function(id){
28704         var items = this.items;
28705         var tab = items[id];
28706         if(!tab) { return; }
28707         var index = items.indexOf(tab);
28708         if(this.active == tab && items.length > 1){
28709             var newTab = this.getNextAvailable(index);
28710             if(newTab) {
28711                 newTab.activate();
28712             }
28713         }
28714         this.stripEl.dom.removeChild(tab.pnode.dom);
28715         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
28716             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
28717         }
28718         items.splice(index, 1);
28719         delete this.items[tab.id];
28720         tab.fireEvent("close", tab);
28721         tab.purgeListeners();
28722         this.autoSizeTabs();
28723     },
28724
28725     getNextAvailable : function(start){
28726         var items = this.items;
28727         var index = start;
28728         // look for a next tab that will slide over to
28729         // replace the one being removed
28730         while(index < items.length){
28731             var item = items[++index];
28732             if(item && !item.isHidden()){
28733                 return item;
28734             }
28735         }
28736         // if one isn't found select the previous tab (on the left)
28737         index = start;
28738         while(index >= 0){
28739             var item = items[--index];
28740             if(item && !item.isHidden()){
28741                 return item;
28742             }
28743         }
28744         return null;
28745     },
28746
28747     /**
28748      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
28749      * @param {String/Number} id The id or index of the TabPanelItem to disable.
28750      */
28751     disableTab : function(id){
28752         var tab = this.items[id];
28753         if(tab && this.active != tab){
28754             tab.disable();
28755         }
28756     },
28757
28758     /**
28759      * Enables a {@link Roo.TabPanelItem} that is disabled.
28760      * @param {String/Number} id The id or index of the TabPanelItem to enable.
28761      */
28762     enableTab : function(id){
28763         var tab = this.items[id];
28764         tab.enable();
28765     },
28766
28767     /**
28768      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
28769      * @param {String/Number} id The id or index of the TabPanelItem to activate.
28770      * @return {Roo.TabPanelItem} The TabPanelItem.
28771      */
28772     activate : function(id){
28773         var tab = this.items[id];
28774         if(!tab){
28775             return null;
28776         }
28777         if(tab == this.active || tab.disabled){
28778             return tab;
28779         }
28780         var e = {};
28781         this.fireEvent("beforetabchange", this, e, tab);
28782         if(e.cancel !== true && !tab.disabled){
28783             if(this.active){
28784                 this.active.hide();
28785             }
28786             this.active = this.items[id];
28787             this.active.show();
28788             this.fireEvent("tabchange", this, this.active);
28789         }
28790         return tab;
28791     },
28792
28793     /**
28794      * Gets the active {@link Roo.TabPanelItem}.
28795      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
28796      */
28797     getActiveTab : function(){
28798         return this.active;
28799     },
28800
28801     /**
28802      * Updates the tab body element to fit the height of the container element
28803      * for overflow scrolling
28804      * @param {Number} targetHeight (optional) Override the starting height from the elements height
28805      */
28806     syncHeight : function(targetHeight){
28807         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
28808         var bm = this.bodyEl.getMargins();
28809         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
28810         this.bodyEl.setHeight(newHeight);
28811         return newHeight;
28812     },
28813
28814     onResize : function(){
28815         if(this.monitorResize){
28816             this.autoSizeTabs();
28817         }
28818     },
28819
28820     /**
28821      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
28822      */
28823     beginUpdate : function(){
28824         this.updating = true;
28825     },
28826
28827     /**
28828      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
28829      */
28830     endUpdate : function(){
28831         this.updating = false;
28832         this.autoSizeTabs();
28833     },
28834
28835     /**
28836      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
28837      */
28838     autoSizeTabs : function(){
28839         var count = this.items.length;
28840         var vcount = count - this.hiddenCount;
28841         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
28842             return;
28843         }
28844         var w = Math.max(this.el.getWidth() - this.cpad, 10);
28845         var availWidth = Math.floor(w / vcount);
28846         var b = this.stripBody;
28847         if(b.getWidth() > w){
28848             var tabs = this.items;
28849             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
28850             if(availWidth < this.minTabWidth){
28851                 /*if(!this.sleft){    // incomplete scrolling code
28852                     this.createScrollButtons();
28853                 }
28854                 this.showScroll();
28855                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
28856             }
28857         }else{
28858             if(this.currentTabWidth < this.preferredTabWidth){
28859                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
28860             }
28861         }
28862     },
28863
28864     /**
28865      * Returns the number of tabs in this TabPanel.
28866      * @return {Number}
28867      */
28868      getCount : function(){
28869          return this.items.length;
28870      },
28871
28872     /**
28873      * Resizes all the tabs to the passed width
28874      * @param {Number} The new width
28875      */
28876     setTabWidth : function(width){
28877         this.currentTabWidth = width;
28878         for(var i = 0, len = this.items.length; i < len; i++) {
28879                 if(!this.items[i].isHidden()) {
28880                 this.items[i].setWidth(width);
28881             }
28882         }
28883     },
28884
28885     /**
28886      * Destroys this TabPanel
28887      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
28888      */
28889     destroy : function(removeEl){
28890         Roo.EventManager.removeResizeListener(this.onResize, this);
28891         for(var i = 0, len = this.items.length; i < len; i++){
28892             this.items[i].purgeListeners();
28893         }
28894         if(removeEl === true){
28895             this.el.update("");
28896             this.el.remove();
28897         }
28898     }
28899 });
28900
28901 /**
28902  * @class Roo.TabPanelItem
28903  * @extends Roo.util.Observable
28904  * Represents an individual item (tab plus body) in a TabPanel.
28905  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
28906  * @param {String} id The id of this TabPanelItem
28907  * @param {String} text The text for the tab of this TabPanelItem
28908  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
28909  */
28910 Roo.TabPanelItem = function(tabPanel, id, text, closable){
28911     /**
28912      * The {@link Roo.TabPanel} this TabPanelItem belongs to
28913      * @type Roo.TabPanel
28914      */
28915     this.tabPanel = tabPanel;
28916     /**
28917      * The id for this TabPanelItem
28918      * @type String
28919      */
28920     this.id = id;
28921     /** @private */
28922     this.disabled = false;
28923     /** @private */
28924     this.text = text;
28925     /** @private */
28926     this.loaded = false;
28927     this.closable = closable;
28928
28929     /**
28930      * The body element for this TabPanelItem.
28931      * @type Roo.Element
28932      */
28933     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
28934     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
28935     this.bodyEl.setStyle("display", "block");
28936     this.bodyEl.setStyle("zoom", "1");
28937     this.hideAction();
28938
28939     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
28940     /** @private */
28941     this.el = Roo.get(els.el, true);
28942     this.inner = Roo.get(els.inner, true);
28943     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
28944     this.pnode = Roo.get(els.el.parentNode, true);
28945     this.el.on("mousedown", this.onTabMouseDown, this);
28946     this.el.on("click", this.onTabClick, this);
28947     /** @private */
28948     if(closable){
28949         var c = Roo.get(els.close, true);
28950         c.dom.title = this.closeText;
28951         c.addClassOnOver("close-over");
28952         c.on("click", this.closeClick, this);
28953      }
28954
28955     this.addEvents({
28956          /**
28957          * @event activate
28958          * Fires when this tab becomes the active tab.
28959          * @param {Roo.TabPanel} tabPanel The parent TabPanel
28960          * @param {Roo.TabPanelItem} this
28961          */
28962         "activate": true,
28963         /**
28964          * @event beforeclose
28965          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
28966          * @param {Roo.TabPanelItem} this
28967          * @param {Object} e Set cancel to true on this object to cancel the close.
28968          */
28969         "beforeclose": true,
28970         /**
28971          * @event close
28972          * Fires when this tab is closed.
28973          * @param {Roo.TabPanelItem} this
28974          */
28975          "close": true,
28976         /**
28977          * @event deactivate
28978          * Fires when this tab is no longer the active tab.
28979          * @param {Roo.TabPanel} tabPanel The parent TabPanel
28980          * @param {Roo.TabPanelItem} this
28981          */
28982          "deactivate" : true
28983     });
28984     this.hidden = false;
28985
28986     Roo.TabPanelItem.superclass.constructor.call(this);
28987 };
28988
28989 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
28990     purgeListeners : function(){
28991        Roo.util.Observable.prototype.purgeListeners.call(this);
28992        this.el.removeAllListeners();
28993     },
28994     /**
28995      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
28996      */
28997     show : function(){
28998         this.pnode.addClass("on");
28999         this.showAction();
29000         if(Roo.isOpera){
29001             this.tabPanel.stripWrap.repaint();
29002         }
29003         this.fireEvent("activate", this.tabPanel, this);
29004     },
29005
29006     /**
29007      * Returns true if this tab is the active tab.
29008      * @return {Boolean}
29009      */
29010     isActive : function(){
29011         return this.tabPanel.getActiveTab() == this;
29012     },
29013
29014     /**
29015      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
29016      */
29017     hide : function(){
29018         this.pnode.removeClass("on");
29019         this.hideAction();
29020         this.fireEvent("deactivate", this.tabPanel, this);
29021     },
29022
29023     hideAction : function(){
29024         this.bodyEl.hide();
29025         this.bodyEl.setStyle("position", "absolute");
29026         this.bodyEl.setLeft("-20000px");
29027         this.bodyEl.setTop("-20000px");
29028     },
29029
29030     showAction : function(){
29031         this.bodyEl.setStyle("position", "relative");
29032         this.bodyEl.setTop("");
29033         this.bodyEl.setLeft("");
29034         this.bodyEl.show();
29035     },
29036
29037     /**
29038      * Set the tooltip for the tab.
29039      * @param {String} tooltip The tab's tooltip
29040      */
29041     setTooltip : function(text){
29042         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
29043             this.textEl.dom.qtip = text;
29044             this.textEl.dom.removeAttribute('title');
29045         }else{
29046             this.textEl.dom.title = text;
29047         }
29048     },
29049
29050     onTabClick : function(e){
29051         e.preventDefault();
29052         this.tabPanel.activate(this.id);
29053     },
29054
29055     onTabMouseDown : function(e){
29056         e.preventDefault();
29057         this.tabPanel.activate(this.id);
29058     },
29059
29060     getWidth : function(){
29061         return this.inner.getWidth();
29062     },
29063
29064     setWidth : function(width){
29065         var iwidth = width - this.pnode.getPadding("lr");
29066         this.inner.setWidth(iwidth);
29067         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
29068         this.pnode.setWidth(width);
29069     },
29070
29071     /**
29072      * Show or hide the tab
29073      * @param {Boolean} hidden True to hide or false to show.
29074      */
29075     setHidden : function(hidden){
29076         this.hidden = hidden;
29077         this.pnode.setStyle("display", hidden ? "none" : "");
29078     },
29079
29080     /**
29081      * Returns true if this tab is "hidden"
29082      * @return {Boolean}
29083      */
29084     isHidden : function(){
29085         return this.hidden;
29086     },
29087
29088     /**
29089      * Returns the text for this tab
29090      * @return {String}
29091      */
29092     getText : function(){
29093         return this.text;
29094     },
29095
29096     autoSize : function(){
29097         //this.el.beginMeasure();
29098         this.textEl.setWidth(1);
29099         /*
29100          *  #2804 [new] Tabs in Roojs
29101          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
29102          */
29103         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
29104         //this.el.endMeasure();
29105     },
29106
29107     /**
29108      * Sets the text for the tab (Note: this also sets the tooltip text)
29109      * @param {String} text The tab's text and tooltip
29110      */
29111     setText : function(text){
29112         this.text = text;
29113         this.textEl.update(text);
29114         this.setTooltip(text);
29115         if(!this.tabPanel.resizeTabs){
29116             this.autoSize();
29117         }
29118     },
29119     /**
29120      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
29121      */
29122     activate : function(){
29123         this.tabPanel.activate(this.id);
29124     },
29125
29126     /**
29127      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
29128      */
29129     disable : function(){
29130         if(this.tabPanel.active != this){
29131             this.disabled = true;
29132             this.pnode.addClass("disabled");
29133         }
29134     },
29135
29136     /**
29137      * Enables this TabPanelItem if it was previously disabled.
29138      */
29139     enable : function(){
29140         this.disabled = false;
29141         this.pnode.removeClass("disabled");
29142     },
29143
29144     /**
29145      * Sets the content for this TabPanelItem.
29146      * @param {String} content The content
29147      * @param {Boolean} loadScripts true to look for and load scripts
29148      */
29149     setContent : function(content, loadScripts){
29150         this.bodyEl.update(content, loadScripts);
29151     },
29152
29153     /**
29154      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
29155      * @return {Roo.UpdateManager} The UpdateManager
29156      */
29157     getUpdateManager : function(){
29158         return this.bodyEl.getUpdateManager();
29159     },
29160
29161     /**
29162      * Set a URL to be used to load the content for this TabPanelItem.
29163      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
29164      * @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)
29165      * @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)
29166      * @return {Roo.UpdateManager} The UpdateManager
29167      */
29168     setUrl : function(url, params, loadOnce){
29169         if(this.refreshDelegate){
29170             this.un('activate', this.refreshDelegate);
29171         }
29172         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
29173         this.on("activate", this.refreshDelegate);
29174         return this.bodyEl.getUpdateManager();
29175     },
29176
29177     /** @private */
29178     _handleRefresh : function(url, params, loadOnce){
29179         if(!loadOnce || !this.loaded){
29180             var updater = this.bodyEl.getUpdateManager();
29181             updater.update(url, params, this._setLoaded.createDelegate(this));
29182         }
29183     },
29184
29185     /**
29186      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
29187      *   Will fail silently if the setUrl method has not been called.
29188      *   This does not activate the panel, just updates its content.
29189      */
29190     refresh : function(){
29191         if(this.refreshDelegate){
29192            this.loaded = false;
29193            this.refreshDelegate();
29194         }
29195     },
29196
29197     /** @private */
29198     _setLoaded : function(){
29199         this.loaded = true;
29200     },
29201
29202     /** @private */
29203     closeClick : function(e){
29204         var o = {};
29205         e.stopEvent();
29206         this.fireEvent("beforeclose", this, o);
29207         if(o.cancel !== true){
29208             this.tabPanel.removeTab(this.id);
29209         }
29210     },
29211     /**
29212      * The text displayed in the tooltip for the close icon.
29213      * @type String
29214      */
29215     closeText : "Close this tab"
29216 });
29217
29218 /** @private */
29219 Roo.TabPanel.prototype.createStrip = function(container){
29220     var strip = document.createElement("div");
29221     strip.className = "x-tabs-wrap";
29222     container.appendChild(strip);
29223     return strip;
29224 };
29225 /** @private */
29226 Roo.TabPanel.prototype.createStripList = function(strip){
29227     // div wrapper for retard IE
29228     // returns the "tr" element.
29229     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
29230         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
29231         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
29232     return strip.firstChild.firstChild.firstChild.firstChild;
29233 };
29234 /** @private */
29235 Roo.TabPanel.prototype.createBody = function(container){
29236     var body = document.createElement("div");
29237     Roo.id(body, "tab-body");
29238     Roo.fly(body).addClass("x-tabs-body");
29239     container.appendChild(body);
29240     return body;
29241 };
29242 /** @private */
29243 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
29244     var body = Roo.getDom(id);
29245     if(!body){
29246         body = document.createElement("div");
29247         body.id = id;
29248     }
29249     Roo.fly(body).addClass("x-tabs-item-body");
29250     bodyEl.insertBefore(body, bodyEl.firstChild);
29251     return body;
29252 };
29253 /** @private */
29254 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
29255     var td = document.createElement("td");
29256     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
29257     //stripEl.appendChild(td);
29258     if(closable){
29259         td.className = "x-tabs-closable";
29260         if(!this.closeTpl){
29261             this.closeTpl = new Roo.Template(
29262                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
29263                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
29264                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
29265             );
29266         }
29267         var el = this.closeTpl.overwrite(td, {"text": text});
29268         var close = el.getElementsByTagName("div")[0];
29269         var inner = el.getElementsByTagName("em")[0];
29270         return {"el": el, "close": close, "inner": inner};
29271     } else {
29272         if(!this.tabTpl){
29273             this.tabTpl = new Roo.Template(
29274                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
29275                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
29276             );
29277         }
29278         var el = this.tabTpl.overwrite(td, {"text": text});
29279         var inner = el.getElementsByTagName("em")[0];
29280         return {"el": el, "inner": inner};
29281     }
29282 };/*
29283  * Based on:
29284  * Ext JS Library 1.1.1
29285  * Copyright(c) 2006-2007, Ext JS, LLC.
29286  *
29287  * Originally Released Under LGPL - original licence link has changed is not relivant.
29288  *
29289  * Fork - LGPL
29290  * <script type="text/javascript">
29291  */
29292
29293 /**
29294  * @class Roo.Button
29295  * @extends Roo.util.Observable
29296  * Simple Button class
29297  * @cfg {String} text The button text
29298  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
29299  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
29300  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
29301  * @cfg {Object} scope The scope of the handler
29302  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
29303  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
29304  * @cfg {Boolean} hidden True to start hidden (defaults to false)
29305  * @cfg {Boolean} disabled True to start disabled (defaults to false)
29306  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
29307  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
29308    applies if enableToggle = true)
29309  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
29310  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
29311   an {@link Roo.util.ClickRepeater} config object (defaults to false).
29312  * @constructor
29313  * Create a new button
29314  * @param {Object} config The config object
29315  */
29316 Roo.Button = function(renderTo, config)
29317 {
29318     if (!config) {
29319         config = renderTo;
29320         renderTo = config.renderTo || false;
29321     }
29322     
29323     Roo.apply(this, config);
29324     this.addEvents({
29325         /**
29326              * @event click
29327              * Fires when this button is clicked
29328              * @param {Button} this
29329              * @param {EventObject} e The click event
29330              */
29331             "click" : true,
29332         /**
29333              * @event toggle
29334              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
29335              * @param {Button} this
29336              * @param {Boolean} pressed
29337              */
29338             "toggle" : true,
29339         /**
29340              * @event mouseover
29341              * Fires when the mouse hovers over the button
29342              * @param {Button} this
29343              * @param {Event} e The event object
29344              */
29345         'mouseover' : true,
29346         /**
29347              * @event mouseout
29348              * Fires when the mouse exits the button
29349              * @param {Button} this
29350              * @param {Event} e The event object
29351              */
29352         'mouseout': true,
29353          /**
29354              * @event render
29355              * Fires when the button is rendered
29356              * @param {Button} this
29357              */
29358         'render': true
29359     });
29360     if(this.menu){
29361         this.menu = Roo.menu.MenuMgr.get(this.menu);
29362     }
29363     // register listeners first!!  - so render can be captured..
29364     Roo.util.Observable.call(this);
29365     if(renderTo){
29366         this.render(renderTo);
29367     }
29368     
29369   
29370 };
29371
29372 Roo.extend(Roo.Button, Roo.util.Observable, {
29373     /**
29374      * 
29375      */
29376     
29377     /**
29378      * Read-only. True if this button is hidden
29379      * @type Boolean
29380      */
29381     hidden : false,
29382     /**
29383      * Read-only. True if this button is disabled
29384      * @type Boolean
29385      */
29386     disabled : false,
29387     /**
29388      * Read-only. True if this button is pressed (only if enableToggle = true)
29389      * @type Boolean
29390      */
29391     pressed : false,
29392
29393     /**
29394      * @cfg {Number} tabIndex 
29395      * The DOM tabIndex for this button (defaults to undefined)
29396      */
29397     tabIndex : undefined,
29398
29399     /**
29400      * @cfg {Boolean} enableToggle
29401      * True to enable pressed/not pressed toggling (defaults to false)
29402      */
29403     enableToggle: false,
29404     /**
29405      * @cfg {Mixed} menu
29406      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
29407      */
29408     menu : undefined,
29409     /**
29410      * @cfg {String} menuAlign
29411      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
29412      */
29413     menuAlign : "tl-bl?",
29414
29415     /**
29416      * @cfg {String} iconCls
29417      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
29418      */
29419     iconCls : undefined,
29420     /**
29421      * @cfg {String} type
29422      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
29423      */
29424     type : 'button',
29425
29426     // private
29427     menuClassTarget: 'tr',
29428
29429     /**
29430      * @cfg {String} clickEvent
29431      * The type of event to map to the button's event handler (defaults to 'click')
29432      */
29433     clickEvent : 'click',
29434
29435     /**
29436      * @cfg {Boolean} handleMouseEvents
29437      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
29438      */
29439     handleMouseEvents : true,
29440
29441     /**
29442      * @cfg {String} tooltipType
29443      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
29444      */
29445     tooltipType : 'qtip',
29446
29447     /**
29448      * @cfg {String} cls
29449      * A CSS class to apply to the button's main element.
29450      */
29451     
29452     /**
29453      * @cfg {Roo.Template} template (Optional)
29454      * An {@link Roo.Template} with which to create the Button's main element. This Template must
29455      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
29456      * require code modifications if required elements (e.g. a button) aren't present.
29457      */
29458
29459     // private
29460     render : function(renderTo){
29461         var btn;
29462         if(this.hideParent){
29463             this.parentEl = Roo.get(renderTo);
29464         }
29465         if(!this.dhconfig){
29466             if(!this.template){
29467                 if(!Roo.Button.buttonTemplate){
29468                     // hideous table template
29469                     Roo.Button.buttonTemplate = new Roo.Template(
29470                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
29471                         '<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>',
29472                         "</tr></tbody></table>");
29473                 }
29474                 this.template = Roo.Button.buttonTemplate;
29475             }
29476             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
29477             var btnEl = btn.child("button:first");
29478             btnEl.on('focus', this.onFocus, this);
29479             btnEl.on('blur', this.onBlur, this);
29480             if(this.cls){
29481                 btn.addClass(this.cls);
29482             }
29483             if(this.icon){
29484                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
29485             }
29486             if(this.iconCls){
29487                 btnEl.addClass(this.iconCls);
29488                 if(!this.cls){
29489                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
29490                 }
29491             }
29492             if(this.tabIndex !== undefined){
29493                 btnEl.dom.tabIndex = this.tabIndex;
29494             }
29495             if(this.tooltip){
29496                 if(typeof this.tooltip == 'object'){
29497                     Roo.QuickTips.tips(Roo.apply({
29498                           target: btnEl.id
29499                     }, this.tooltip));
29500                 } else {
29501                     btnEl.dom[this.tooltipType] = this.tooltip;
29502                 }
29503             }
29504         }else{
29505             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
29506         }
29507         this.el = btn;
29508         if(this.id){
29509             this.el.dom.id = this.el.id = this.id;
29510         }
29511         if(this.menu){
29512             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
29513             this.menu.on("show", this.onMenuShow, this);
29514             this.menu.on("hide", this.onMenuHide, this);
29515         }
29516         btn.addClass("x-btn");
29517         if(Roo.isIE && !Roo.isIE7){
29518             this.autoWidth.defer(1, this);
29519         }else{
29520             this.autoWidth();
29521         }
29522         if(this.handleMouseEvents){
29523             btn.on("mouseover", this.onMouseOver, this);
29524             btn.on("mouseout", this.onMouseOut, this);
29525             btn.on("mousedown", this.onMouseDown, this);
29526         }
29527         btn.on(this.clickEvent, this.onClick, this);
29528         //btn.on("mouseup", this.onMouseUp, this);
29529         if(this.hidden){
29530             this.hide();
29531         }
29532         if(this.disabled){
29533             this.disable();
29534         }
29535         Roo.ButtonToggleMgr.register(this);
29536         if(this.pressed){
29537             this.el.addClass("x-btn-pressed");
29538         }
29539         if(this.repeat){
29540             var repeater = new Roo.util.ClickRepeater(btn,
29541                 typeof this.repeat == "object" ? this.repeat : {}
29542             );
29543             repeater.on("click", this.onClick,  this);
29544         }
29545         
29546         this.fireEvent('render', this);
29547         
29548     },
29549     /**
29550      * Returns the button's underlying element
29551      * @return {Roo.Element} The element
29552      */
29553     getEl : function(){
29554         return this.el;  
29555     },
29556     
29557     /**
29558      * Destroys this Button and removes any listeners.
29559      */
29560     destroy : function(){
29561         Roo.ButtonToggleMgr.unregister(this);
29562         this.el.removeAllListeners();
29563         this.purgeListeners();
29564         this.el.remove();
29565     },
29566
29567     // private
29568     autoWidth : function(){
29569         if(this.el){
29570             this.el.setWidth("auto");
29571             if(Roo.isIE7 && Roo.isStrict){
29572                 var ib = this.el.child('button');
29573                 if(ib && ib.getWidth() > 20){
29574                     ib.clip();
29575                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
29576                 }
29577             }
29578             if(this.minWidth){
29579                 if(this.hidden){
29580                     this.el.beginMeasure();
29581                 }
29582                 if(this.el.getWidth() < this.minWidth){
29583                     this.el.setWidth(this.minWidth);
29584                 }
29585                 if(this.hidden){
29586                     this.el.endMeasure();
29587                 }
29588             }
29589         }
29590     },
29591
29592     /**
29593      * Assigns this button's click handler
29594      * @param {Function} handler The function to call when the button is clicked
29595      * @param {Object} scope (optional) Scope for the function passed in
29596      */
29597     setHandler : function(handler, scope){
29598         this.handler = handler;
29599         this.scope = scope;  
29600     },
29601     
29602     /**
29603      * Sets this button's text
29604      * @param {String} text The button text
29605      */
29606     setText : function(text){
29607         this.text = text;
29608         if(this.el){
29609             this.el.child("td.x-btn-center button.x-btn-text").update(text);
29610         }
29611         this.autoWidth();
29612     },
29613     
29614     /**
29615      * Gets the text for this button
29616      * @return {String} The button text
29617      */
29618     getText : function(){
29619         return this.text;  
29620     },
29621     
29622     /**
29623      * Show this button
29624      */
29625     show: function(){
29626         this.hidden = false;
29627         if(this.el){
29628             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
29629         }
29630     },
29631     
29632     /**
29633      * Hide this button
29634      */
29635     hide: function(){
29636         this.hidden = true;
29637         if(this.el){
29638             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
29639         }
29640     },
29641     
29642     /**
29643      * Convenience function for boolean show/hide
29644      * @param {Boolean} visible True to show, false to hide
29645      */
29646     setVisible: function(visible){
29647         if(visible) {
29648             this.show();
29649         }else{
29650             this.hide();
29651         }
29652     },
29653     
29654     /**
29655      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
29656      * @param {Boolean} state (optional) Force a particular state
29657      */
29658     toggle : function(state){
29659         state = state === undefined ? !this.pressed : state;
29660         if(state != this.pressed){
29661             if(state){
29662                 this.el.addClass("x-btn-pressed");
29663                 this.pressed = true;
29664                 this.fireEvent("toggle", this, true);
29665             }else{
29666                 this.el.removeClass("x-btn-pressed");
29667                 this.pressed = false;
29668                 this.fireEvent("toggle", this, false);
29669             }
29670             if(this.toggleHandler){
29671                 this.toggleHandler.call(this.scope || this, this, state);
29672             }
29673         }
29674     },
29675     
29676     /**
29677      * Focus the button
29678      */
29679     focus : function(){
29680         this.el.child('button:first').focus();
29681     },
29682     
29683     /**
29684      * Disable this button
29685      */
29686     disable : function(){
29687         if(this.el){
29688             this.el.addClass("x-btn-disabled");
29689         }
29690         this.disabled = true;
29691     },
29692     
29693     /**
29694      * Enable this button
29695      */
29696     enable : function(){
29697         if(this.el){
29698             this.el.removeClass("x-btn-disabled");
29699         }
29700         this.disabled = false;
29701     },
29702
29703     /**
29704      * Convenience function for boolean enable/disable
29705      * @param {Boolean} enabled True to enable, false to disable
29706      */
29707     setDisabled : function(v){
29708         this[v !== true ? "enable" : "disable"]();
29709     },
29710
29711     // private
29712     onClick : function(e)
29713     {
29714         if(e){
29715             e.preventDefault();
29716         }
29717         if(e.button != 0){
29718             return;
29719         }
29720         if(!this.disabled){
29721             if(this.enableToggle){
29722                 this.toggle();
29723             }
29724             if(this.menu && !this.menu.isVisible()){
29725                 this.menu.show(this.el, this.menuAlign);
29726             }
29727             this.fireEvent("click", this, e);
29728             if(this.handler){
29729                 this.el.removeClass("x-btn-over");
29730                 this.handler.call(this.scope || this, this, e);
29731             }
29732         }
29733     },
29734     // private
29735     onMouseOver : function(e){
29736         if(!this.disabled){
29737             this.el.addClass("x-btn-over");
29738             this.fireEvent('mouseover', this, e);
29739         }
29740     },
29741     // private
29742     onMouseOut : function(e){
29743         if(!e.within(this.el,  true)){
29744             this.el.removeClass("x-btn-over");
29745             this.fireEvent('mouseout', this, e);
29746         }
29747     },
29748     // private
29749     onFocus : function(e){
29750         if(!this.disabled){
29751             this.el.addClass("x-btn-focus");
29752         }
29753     },
29754     // private
29755     onBlur : function(e){
29756         this.el.removeClass("x-btn-focus");
29757     },
29758     // private
29759     onMouseDown : function(e){
29760         if(!this.disabled && e.button == 0){
29761             this.el.addClass("x-btn-click");
29762             Roo.get(document).on('mouseup', this.onMouseUp, this);
29763         }
29764     },
29765     // private
29766     onMouseUp : function(e){
29767         if(e.button == 0){
29768             this.el.removeClass("x-btn-click");
29769             Roo.get(document).un('mouseup', this.onMouseUp, this);
29770         }
29771     },
29772     // private
29773     onMenuShow : function(e){
29774         this.el.addClass("x-btn-menu-active");
29775     },
29776     // private
29777     onMenuHide : function(e){
29778         this.el.removeClass("x-btn-menu-active");
29779     }   
29780 });
29781
29782 // Private utility class used by Button
29783 Roo.ButtonToggleMgr = function(){
29784    var groups = {};
29785    
29786    function toggleGroup(btn, state){
29787        if(state){
29788            var g = groups[btn.toggleGroup];
29789            for(var i = 0, l = g.length; i < l; i++){
29790                if(g[i] != btn){
29791                    g[i].toggle(false);
29792                }
29793            }
29794        }
29795    }
29796    
29797    return {
29798        register : function(btn){
29799            if(!btn.toggleGroup){
29800                return;
29801            }
29802            var g = groups[btn.toggleGroup];
29803            if(!g){
29804                g = groups[btn.toggleGroup] = [];
29805            }
29806            g.push(btn);
29807            btn.on("toggle", toggleGroup);
29808        },
29809        
29810        unregister : function(btn){
29811            if(!btn.toggleGroup){
29812                return;
29813            }
29814            var g = groups[btn.toggleGroup];
29815            if(g){
29816                g.remove(btn);
29817                btn.un("toggle", toggleGroup);
29818            }
29819        }
29820    };
29821 }();/*
29822  * Based on:
29823  * Ext JS Library 1.1.1
29824  * Copyright(c) 2006-2007, Ext JS, LLC.
29825  *
29826  * Originally Released Under LGPL - original licence link has changed is not relivant.
29827  *
29828  * Fork - LGPL
29829  * <script type="text/javascript">
29830  */
29831  
29832 /**
29833  * @class Roo.SplitButton
29834  * @extends Roo.Button
29835  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
29836  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
29837  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
29838  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
29839  * @cfg {String} arrowTooltip The title attribute of the arrow
29840  * @constructor
29841  * Create a new menu button
29842  * @param {String/HTMLElement/Element} renderTo The element to append the button to
29843  * @param {Object} config The config object
29844  */
29845 Roo.SplitButton = function(renderTo, config){
29846     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
29847     /**
29848      * @event arrowclick
29849      * Fires when this button's arrow is clicked
29850      * @param {SplitButton} this
29851      * @param {EventObject} e The click event
29852      */
29853     this.addEvents({"arrowclick":true});
29854 };
29855
29856 Roo.extend(Roo.SplitButton, Roo.Button, {
29857     render : function(renderTo){
29858         // this is one sweet looking template!
29859         var tpl = new Roo.Template(
29860             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
29861             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
29862             '<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>',
29863             "</tbody></table></td><td>",
29864             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
29865             '<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>',
29866             "</tbody></table></td></tr></table>"
29867         );
29868         var btn = tpl.append(renderTo, [this.text, this.type], true);
29869         var btnEl = btn.child("button");
29870         if(this.cls){
29871             btn.addClass(this.cls);
29872         }
29873         if(this.icon){
29874             btnEl.setStyle('background-image', 'url(' +this.icon +')');
29875         }
29876         if(this.iconCls){
29877             btnEl.addClass(this.iconCls);
29878             if(!this.cls){
29879                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
29880             }
29881         }
29882         this.el = btn;
29883         if(this.handleMouseEvents){
29884             btn.on("mouseover", this.onMouseOver, this);
29885             btn.on("mouseout", this.onMouseOut, this);
29886             btn.on("mousedown", this.onMouseDown, this);
29887             btn.on("mouseup", this.onMouseUp, this);
29888         }
29889         btn.on(this.clickEvent, this.onClick, this);
29890         if(this.tooltip){
29891             if(typeof this.tooltip == 'object'){
29892                 Roo.QuickTips.tips(Roo.apply({
29893                       target: btnEl.id
29894                 }, this.tooltip));
29895             } else {
29896                 btnEl.dom[this.tooltipType] = this.tooltip;
29897             }
29898         }
29899         if(this.arrowTooltip){
29900             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
29901         }
29902         if(this.hidden){
29903             this.hide();
29904         }
29905         if(this.disabled){
29906             this.disable();
29907         }
29908         if(this.pressed){
29909             this.el.addClass("x-btn-pressed");
29910         }
29911         if(Roo.isIE && !Roo.isIE7){
29912             this.autoWidth.defer(1, this);
29913         }else{
29914             this.autoWidth();
29915         }
29916         if(this.menu){
29917             this.menu.on("show", this.onMenuShow, this);
29918             this.menu.on("hide", this.onMenuHide, this);
29919         }
29920         this.fireEvent('render', this);
29921     },
29922
29923     // private
29924     autoWidth : function(){
29925         if(this.el){
29926             var tbl = this.el.child("table:first");
29927             var tbl2 = this.el.child("table:last");
29928             this.el.setWidth("auto");
29929             tbl.setWidth("auto");
29930             if(Roo.isIE7 && Roo.isStrict){
29931                 var ib = this.el.child('button:first');
29932                 if(ib && ib.getWidth() > 20){
29933                     ib.clip();
29934                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
29935                 }
29936             }
29937             if(this.minWidth){
29938                 if(this.hidden){
29939                     this.el.beginMeasure();
29940                 }
29941                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
29942                     tbl.setWidth(this.minWidth-tbl2.getWidth());
29943                 }
29944                 if(this.hidden){
29945                     this.el.endMeasure();
29946                 }
29947             }
29948             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
29949         } 
29950     },
29951     /**
29952      * Sets this button's click handler
29953      * @param {Function} handler The function to call when the button is clicked
29954      * @param {Object} scope (optional) Scope for the function passed above
29955      */
29956     setHandler : function(handler, scope){
29957         this.handler = handler;
29958         this.scope = scope;  
29959     },
29960     
29961     /**
29962      * Sets this button's arrow click handler
29963      * @param {Function} handler The function to call when the arrow is clicked
29964      * @param {Object} scope (optional) Scope for the function passed above
29965      */
29966     setArrowHandler : function(handler, scope){
29967         this.arrowHandler = handler;
29968         this.scope = scope;  
29969     },
29970     
29971     /**
29972      * Focus the button
29973      */
29974     focus : function(){
29975         if(this.el){
29976             this.el.child("button:first").focus();
29977         }
29978     },
29979
29980     // private
29981     onClick : function(e){
29982         e.preventDefault();
29983         if(!this.disabled){
29984             if(e.getTarget(".x-btn-menu-arrow-wrap")){
29985                 if(this.menu && !this.menu.isVisible()){
29986                     this.menu.show(this.el, this.menuAlign);
29987                 }
29988                 this.fireEvent("arrowclick", this, e);
29989                 if(this.arrowHandler){
29990                     this.arrowHandler.call(this.scope || this, this, e);
29991                 }
29992             }else{
29993                 this.fireEvent("click", this, e);
29994                 if(this.handler){
29995                     this.handler.call(this.scope || this, this, e);
29996                 }
29997             }
29998         }
29999     },
30000     // private
30001     onMouseDown : function(e){
30002         if(!this.disabled){
30003             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
30004         }
30005     },
30006     // private
30007     onMouseUp : function(e){
30008         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
30009     }   
30010 });
30011
30012
30013 // backwards compat
30014 Roo.MenuButton = Roo.SplitButton;/*
30015  * Based on:
30016  * Ext JS Library 1.1.1
30017  * Copyright(c) 2006-2007, Ext JS, LLC.
30018  *
30019  * Originally Released Under LGPL - original licence link has changed is not relivant.
30020  *
30021  * Fork - LGPL
30022  * <script type="text/javascript">
30023  */
30024
30025 /**
30026  * @class Roo.Toolbar
30027  * Basic Toolbar class.
30028  * @constructor
30029  * Creates a new Toolbar
30030  * @param {Object} container The config object
30031  */ 
30032 Roo.Toolbar = function(container, buttons, config)
30033 {
30034     /// old consturctor format still supported..
30035     if(container instanceof Array){ // omit the container for later rendering
30036         buttons = container;
30037         config = buttons;
30038         container = null;
30039     }
30040     if (typeof(container) == 'object' && container.xtype) {
30041         config = container;
30042         container = config.container;
30043         buttons = config.buttons || []; // not really - use items!!
30044     }
30045     var xitems = [];
30046     if (config && config.items) {
30047         xitems = config.items;
30048         delete config.items;
30049     }
30050     Roo.apply(this, config);
30051     this.buttons = buttons;
30052     
30053     if(container){
30054         this.render(container);
30055     }
30056     this.xitems = xitems;
30057     Roo.each(xitems, function(b) {
30058         this.add(b);
30059     }, this);
30060     
30061 };
30062
30063 Roo.Toolbar.prototype = {
30064     /**
30065      * @cfg {Array} items
30066      * array of button configs or elements to add (will be converted to a MixedCollection)
30067      */
30068     
30069     /**
30070      * @cfg {String/HTMLElement/Element} container
30071      * The id or element that will contain the toolbar
30072      */
30073     // private
30074     render : function(ct){
30075         this.el = Roo.get(ct);
30076         if(this.cls){
30077             this.el.addClass(this.cls);
30078         }
30079         // using a table allows for vertical alignment
30080         // 100% width is needed by Safari...
30081         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
30082         this.tr = this.el.child("tr", true);
30083         var autoId = 0;
30084         this.items = new Roo.util.MixedCollection(false, function(o){
30085             return o.id || ("item" + (++autoId));
30086         });
30087         if(this.buttons){
30088             this.add.apply(this, this.buttons);
30089             delete this.buttons;
30090         }
30091     },
30092
30093     /**
30094      * Adds element(s) to the toolbar -- this function takes a variable number of 
30095      * arguments of mixed type and adds them to the toolbar.
30096      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
30097      * <ul>
30098      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
30099      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
30100      * <li>Field: Any form field (equivalent to {@link #addField})</li>
30101      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
30102      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
30103      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
30104      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
30105      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
30106      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
30107      * </ul>
30108      * @param {Mixed} arg2
30109      * @param {Mixed} etc.
30110      */
30111     add : function(){
30112         var a = arguments, l = a.length;
30113         for(var i = 0; i < l; i++){
30114             this._add(a[i]);
30115         }
30116     },
30117     // private..
30118     _add : function(el) {
30119         
30120         if (el.xtype) {
30121             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
30122         }
30123         
30124         if (el.applyTo){ // some kind of form field
30125             return this.addField(el);
30126         } 
30127         if (el.render){ // some kind of Toolbar.Item
30128             return this.addItem(el);
30129         }
30130         if (typeof el == "string"){ // string
30131             if(el == "separator" || el == "-"){
30132                 return this.addSeparator();
30133             }
30134             if (el == " "){
30135                 return this.addSpacer();
30136             }
30137             if(el == "->"){
30138                 return this.addFill();
30139             }
30140             return this.addText(el);
30141             
30142         }
30143         if(el.tagName){ // element
30144             return this.addElement(el);
30145         }
30146         if(typeof el == "object"){ // must be button config?
30147             return this.addButton(el);
30148         }
30149         // and now what?!?!
30150         return false;
30151         
30152     },
30153     
30154     /**
30155      * Add an Xtype element
30156      * @param {Object} xtype Xtype Object
30157      * @return {Object} created Object
30158      */
30159     addxtype : function(e){
30160         return this.add(e);  
30161     },
30162     
30163     /**
30164      * Returns the Element for this toolbar.
30165      * @return {Roo.Element}
30166      */
30167     getEl : function(){
30168         return this.el;  
30169     },
30170     
30171     /**
30172      * Adds a separator
30173      * @return {Roo.Toolbar.Item} The separator item
30174      */
30175     addSeparator : function(){
30176         return this.addItem(new Roo.Toolbar.Separator());
30177     },
30178
30179     /**
30180      * Adds a spacer element
30181      * @return {Roo.Toolbar.Spacer} The spacer item
30182      */
30183     addSpacer : function(){
30184         return this.addItem(new Roo.Toolbar.Spacer());
30185     },
30186
30187     /**
30188      * Adds a fill element that forces subsequent additions to the right side of the toolbar
30189      * @return {Roo.Toolbar.Fill} The fill item
30190      */
30191     addFill : function(){
30192         return this.addItem(new Roo.Toolbar.Fill());
30193     },
30194
30195     /**
30196      * Adds any standard HTML element to the toolbar
30197      * @param {String/HTMLElement/Element} el The element or id of the element to add
30198      * @return {Roo.Toolbar.Item} The element's item
30199      */
30200     addElement : function(el){
30201         return this.addItem(new Roo.Toolbar.Item(el));
30202     },
30203     /**
30204      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
30205      * @type Roo.util.MixedCollection  
30206      */
30207     items : false,
30208      
30209     /**
30210      * Adds any Toolbar.Item or subclass
30211      * @param {Roo.Toolbar.Item} item
30212      * @return {Roo.Toolbar.Item} The item
30213      */
30214     addItem : function(item){
30215         var td = this.nextBlock();
30216         item.render(td);
30217         this.items.add(item);
30218         return item;
30219     },
30220     
30221     /**
30222      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
30223      * @param {Object/Array} config A button config or array of configs
30224      * @return {Roo.Toolbar.Button/Array}
30225      */
30226     addButton : function(config){
30227         if(config instanceof Array){
30228             var buttons = [];
30229             for(var i = 0, len = config.length; i < len; i++) {
30230                 buttons.push(this.addButton(config[i]));
30231             }
30232             return buttons;
30233         }
30234         var b = config;
30235         if(!(config instanceof Roo.Toolbar.Button)){
30236             b = config.split ?
30237                 new Roo.Toolbar.SplitButton(config) :
30238                 new Roo.Toolbar.Button(config);
30239         }
30240         var td = this.nextBlock();
30241         b.render(td);
30242         this.items.add(b);
30243         return b;
30244     },
30245     
30246     /**
30247      * Adds text to the toolbar
30248      * @param {String} text The text to add
30249      * @return {Roo.Toolbar.Item} The element's item
30250      */
30251     addText : function(text){
30252         return this.addItem(new Roo.Toolbar.TextItem(text));
30253     },
30254     
30255     /**
30256      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
30257      * @param {Number} index The index where the item is to be inserted
30258      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
30259      * @return {Roo.Toolbar.Button/Item}
30260      */
30261     insertButton : function(index, item){
30262         if(item instanceof Array){
30263             var buttons = [];
30264             for(var i = 0, len = item.length; i < len; i++) {
30265                buttons.push(this.insertButton(index + i, item[i]));
30266             }
30267             return buttons;
30268         }
30269         if (!(item instanceof Roo.Toolbar.Button)){
30270            item = new Roo.Toolbar.Button(item);
30271         }
30272         var td = document.createElement("td");
30273         this.tr.insertBefore(td, this.tr.childNodes[index]);
30274         item.render(td);
30275         this.items.insert(index, item);
30276         return item;
30277     },
30278     
30279     /**
30280      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
30281      * @param {Object} config
30282      * @return {Roo.Toolbar.Item} The element's item
30283      */
30284     addDom : function(config, returnEl){
30285         var td = this.nextBlock();
30286         Roo.DomHelper.overwrite(td, config);
30287         var ti = new Roo.Toolbar.Item(td.firstChild);
30288         ti.render(td);
30289         this.items.add(ti);
30290         return ti;
30291     },
30292
30293     /**
30294      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
30295      * @type Roo.util.MixedCollection  
30296      */
30297     fields : false,
30298     
30299     /**
30300      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
30301      * Note: the field should not have been rendered yet. For a field that has already been
30302      * rendered, use {@link #addElement}.
30303      * @param {Roo.form.Field} field
30304      * @return {Roo.ToolbarItem}
30305      */
30306      
30307       
30308     addField : function(field) {
30309         if (!this.fields) {
30310             var autoId = 0;
30311             this.fields = new Roo.util.MixedCollection(false, function(o){
30312                 return o.id || ("item" + (++autoId));
30313             });
30314
30315         }
30316         
30317         var td = this.nextBlock();
30318         field.render(td);
30319         var ti = new Roo.Toolbar.Item(td.firstChild);
30320         ti.render(td);
30321         this.items.add(ti);
30322         this.fields.add(field);
30323         return ti;
30324     },
30325     /**
30326      * Hide the toolbar
30327      * @method hide
30328      */
30329      
30330       
30331     hide : function()
30332     {
30333         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
30334         this.el.child('div').hide();
30335     },
30336     /**
30337      * Show the toolbar
30338      * @method show
30339      */
30340     show : function()
30341     {
30342         this.el.child('div').show();
30343     },
30344       
30345     // private
30346     nextBlock : function(){
30347         var td = document.createElement("td");
30348         this.tr.appendChild(td);
30349         return td;
30350     },
30351
30352     // private
30353     destroy : function(){
30354         if(this.items){ // rendered?
30355             Roo.destroy.apply(Roo, this.items.items);
30356         }
30357         if(this.fields){ // rendered?
30358             Roo.destroy.apply(Roo, this.fields.items);
30359         }
30360         Roo.Element.uncache(this.el, this.tr);
30361     }
30362 };
30363
30364 /**
30365  * @class Roo.Toolbar.Item
30366  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
30367  * @constructor
30368  * Creates a new Item
30369  * @param {HTMLElement} el 
30370  */
30371 Roo.Toolbar.Item = function(el){
30372     var cfg = {};
30373     if (typeof (el.xtype) != 'undefined') {
30374         cfg = el;
30375         el = cfg.el;
30376     }
30377     
30378     this.el = Roo.getDom(el);
30379     this.id = Roo.id(this.el);
30380     this.hidden = false;
30381     
30382     this.addEvents({
30383          /**
30384              * @event render
30385              * Fires when the button is rendered
30386              * @param {Button} this
30387              */
30388         'render': true
30389     });
30390     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
30391 };
30392 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
30393 //Roo.Toolbar.Item.prototype = {
30394     
30395     /**
30396      * Get this item's HTML Element
30397      * @return {HTMLElement}
30398      */
30399     getEl : function(){
30400        return this.el;  
30401     },
30402
30403     // private
30404     render : function(td){
30405         
30406          this.td = td;
30407         td.appendChild(this.el);
30408         
30409         this.fireEvent('render', this);
30410     },
30411     
30412     /**
30413      * Removes and destroys this item.
30414      */
30415     destroy : function(){
30416         this.td.parentNode.removeChild(this.td);
30417     },
30418     
30419     /**
30420      * Shows this item.
30421      */
30422     show: function(){
30423         this.hidden = false;
30424         this.td.style.display = "";
30425     },
30426     
30427     /**
30428      * Hides this item.
30429      */
30430     hide: function(){
30431         this.hidden = true;
30432         this.td.style.display = "none";
30433     },
30434     
30435     /**
30436      * Convenience function for boolean show/hide.
30437      * @param {Boolean} visible true to show/false to hide
30438      */
30439     setVisible: function(visible){
30440         if(visible) {
30441             this.show();
30442         }else{
30443             this.hide();
30444         }
30445     },
30446     
30447     /**
30448      * Try to focus this item.
30449      */
30450     focus : function(){
30451         Roo.fly(this.el).focus();
30452     },
30453     
30454     /**
30455      * Disables this item.
30456      */
30457     disable : function(){
30458         Roo.fly(this.td).addClass("x-item-disabled");
30459         this.disabled = true;
30460         this.el.disabled = true;
30461     },
30462     
30463     /**
30464      * Enables this item.
30465      */
30466     enable : function(){
30467         Roo.fly(this.td).removeClass("x-item-disabled");
30468         this.disabled = false;
30469         this.el.disabled = false;
30470     }
30471 });
30472
30473
30474 /**
30475  * @class Roo.Toolbar.Separator
30476  * @extends Roo.Toolbar.Item
30477  * A simple toolbar separator class
30478  * @constructor
30479  * Creates a new Separator
30480  */
30481 Roo.Toolbar.Separator = function(cfg){
30482     
30483     var s = document.createElement("span");
30484     s.className = "ytb-sep";
30485     if (cfg) {
30486         cfg.el = s;
30487     }
30488     
30489     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
30490 };
30491 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
30492     enable:Roo.emptyFn,
30493     disable:Roo.emptyFn,
30494     focus:Roo.emptyFn
30495 });
30496
30497 /**
30498  * @class Roo.Toolbar.Spacer
30499  * @extends Roo.Toolbar.Item
30500  * A simple element that adds extra horizontal space to a toolbar.
30501  * @constructor
30502  * Creates a new Spacer
30503  */
30504 Roo.Toolbar.Spacer = function(cfg){
30505     var s = document.createElement("div");
30506     s.className = "ytb-spacer";
30507     if (cfg) {
30508         cfg.el = s;
30509     }
30510     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
30511 };
30512 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
30513     enable:Roo.emptyFn,
30514     disable:Roo.emptyFn,
30515     focus:Roo.emptyFn
30516 });
30517
30518 /**
30519  * @class Roo.Toolbar.Fill
30520  * @extends Roo.Toolbar.Spacer
30521  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
30522  * @constructor
30523  * Creates a new Spacer
30524  */
30525 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
30526     // private
30527     render : function(td){
30528         td.style.width = '100%';
30529         Roo.Toolbar.Fill.superclass.render.call(this, td);
30530     }
30531 });
30532
30533 /**
30534  * @class Roo.Toolbar.TextItem
30535  * @extends Roo.Toolbar.Item
30536  * A simple class that renders text directly into a toolbar.
30537  * @constructor
30538  * Creates a new TextItem
30539  * @cfg {string} text 
30540  */
30541 Roo.Toolbar.TextItem = function(cfg){
30542     var  text = cfg || "";
30543     if (typeof(cfg) == 'object') {
30544         text = cfg.text || "";
30545     }  else {
30546         cfg = null;
30547     }
30548     var s = document.createElement("span");
30549     s.className = "ytb-text";
30550     s.innerHTML = text;
30551     if (cfg) {
30552         cfg.el  = s;
30553     }
30554     
30555     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
30556 };
30557 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
30558     
30559      
30560     enable:Roo.emptyFn,
30561     disable:Roo.emptyFn,
30562     focus:Roo.emptyFn
30563 });
30564
30565 /**
30566  * @class Roo.Toolbar.Button
30567  * @extends Roo.Button
30568  * A button that renders into a toolbar.
30569  * @constructor
30570  * Creates a new Button
30571  * @param {Object} config A standard {@link Roo.Button} config object
30572  */
30573 Roo.Toolbar.Button = function(config){
30574     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
30575 };
30576 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
30577     render : function(td){
30578         this.td = td;
30579         Roo.Toolbar.Button.superclass.render.call(this, td);
30580     },
30581     
30582     /**
30583      * Removes and destroys this button
30584      */
30585     destroy : function(){
30586         Roo.Toolbar.Button.superclass.destroy.call(this);
30587         this.td.parentNode.removeChild(this.td);
30588     },
30589     
30590     /**
30591      * Shows this button
30592      */
30593     show: function(){
30594         this.hidden = false;
30595         this.td.style.display = "";
30596     },
30597     
30598     /**
30599      * Hides this button
30600      */
30601     hide: function(){
30602         this.hidden = true;
30603         this.td.style.display = "none";
30604     },
30605
30606     /**
30607      * Disables this item
30608      */
30609     disable : function(){
30610         Roo.fly(this.td).addClass("x-item-disabled");
30611         this.disabled = true;
30612     },
30613
30614     /**
30615      * Enables this item
30616      */
30617     enable : function(){
30618         Roo.fly(this.td).removeClass("x-item-disabled");
30619         this.disabled = false;
30620     }
30621 });
30622 // backwards compat
30623 Roo.ToolbarButton = Roo.Toolbar.Button;
30624
30625 /**
30626  * @class Roo.Toolbar.SplitButton
30627  * @extends Roo.SplitButton
30628  * A menu button that renders into a toolbar.
30629  * @constructor
30630  * Creates a new SplitButton
30631  * @param {Object} config A standard {@link Roo.SplitButton} config object
30632  */
30633 Roo.Toolbar.SplitButton = function(config){
30634     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
30635 };
30636 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
30637     render : function(td){
30638         this.td = td;
30639         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
30640     },
30641     
30642     /**
30643      * Removes and destroys this button
30644      */
30645     destroy : function(){
30646         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
30647         this.td.parentNode.removeChild(this.td);
30648     },
30649     
30650     /**
30651      * Shows this button
30652      */
30653     show: function(){
30654         this.hidden = false;
30655         this.td.style.display = "";
30656     },
30657     
30658     /**
30659      * Hides this button
30660      */
30661     hide: function(){
30662         this.hidden = true;
30663         this.td.style.display = "none";
30664     }
30665 });
30666
30667 // backwards compat
30668 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
30669  * Based on:
30670  * Ext JS Library 1.1.1
30671  * Copyright(c) 2006-2007, Ext JS, LLC.
30672  *
30673  * Originally Released Under LGPL - original licence link has changed is not relivant.
30674  *
30675  * Fork - LGPL
30676  * <script type="text/javascript">
30677  */
30678  
30679 /**
30680  * @class Roo.PagingToolbar
30681  * @extends Roo.Toolbar
30682  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
30683  * @constructor
30684  * Create a new PagingToolbar
30685  * @param {Object} config The config object
30686  */
30687 Roo.PagingToolbar = function(el, ds, config)
30688 {
30689     // old args format still supported... - xtype is prefered..
30690     if (typeof(el) == 'object' && el.xtype) {
30691         // created from xtype...
30692         config = el;
30693         ds = el.dataSource;
30694         el = config.container;
30695     }
30696     var items = [];
30697     if (config.items) {
30698         items = config.items;
30699         config.items = [];
30700     }
30701     
30702     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
30703     this.ds = ds;
30704     this.cursor = 0;
30705     this.renderButtons(this.el);
30706     this.bind(ds);
30707     
30708     // supprot items array.
30709    
30710     Roo.each(items, function(e) {
30711         this.add(Roo.factory(e));
30712     },this);
30713     
30714 };
30715
30716 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
30717     /**
30718      * @cfg {Roo.data.Store} dataSource
30719      * The underlying data store providing the paged data
30720      */
30721     /**
30722      * @cfg {String/HTMLElement/Element} container
30723      * container The id or element that will contain the toolbar
30724      */
30725     /**
30726      * @cfg {Boolean} displayInfo
30727      * True to display the displayMsg (defaults to false)
30728      */
30729     /**
30730      * @cfg {Number} pageSize
30731      * The number of records to display per page (defaults to 20)
30732      */
30733     pageSize: 20,
30734     /**
30735      * @cfg {String} displayMsg
30736      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
30737      */
30738     displayMsg : 'Displaying {0} - {1} of {2}',
30739     /**
30740      * @cfg {String} emptyMsg
30741      * The message to display when no records are found (defaults to "No data to display")
30742      */
30743     emptyMsg : 'No data to display',
30744     /**
30745      * Customizable piece of the default paging text (defaults to "Page")
30746      * @type String
30747      */
30748     beforePageText : "Page",
30749     /**
30750      * Customizable piece of the default paging text (defaults to "of %0")
30751      * @type String
30752      */
30753     afterPageText : "of {0}",
30754     /**
30755      * Customizable piece of the default paging text (defaults to "First Page")
30756      * @type String
30757      */
30758     firstText : "First Page",
30759     /**
30760      * Customizable piece of the default paging text (defaults to "Previous Page")
30761      * @type String
30762      */
30763     prevText : "Previous Page",
30764     /**
30765      * Customizable piece of the default paging text (defaults to "Next Page")
30766      * @type String
30767      */
30768     nextText : "Next Page",
30769     /**
30770      * Customizable piece of the default paging text (defaults to "Last Page")
30771      * @type String
30772      */
30773     lastText : "Last Page",
30774     /**
30775      * Customizable piece of the default paging text (defaults to "Refresh")
30776      * @type String
30777      */
30778     refreshText : "Refresh",
30779
30780     // private
30781     renderButtons : function(el){
30782         Roo.PagingToolbar.superclass.render.call(this, el);
30783         this.first = this.addButton({
30784             tooltip: this.firstText,
30785             cls: "x-btn-icon x-grid-page-first",
30786             disabled: true,
30787             handler: this.onClick.createDelegate(this, ["first"])
30788         });
30789         this.prev = this.addButton({
30790             tooltip: this.prevText,
30791             cls: "x-btn-icon x-grid-page-prev",
30792             disabled: true,
30793             handler: this.onClick.createDelegate(this, ["prev"])
30794         });
30795         //this.addSeparator();
30796         this.add(this.beforePageText);
30797         this.field = Roo.get(this.addDom({
30798            tag: "input",
30799            type: "text",
30800            size: "3",
30801            value: "1",
30802            cls: "x-grid-page-number"
30803         }).el);
30804         this.field.on("keydown", this.onPagingKeydown, this);
30805         this.field.on("focus", function(){this.dom.select();});
30806         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
30807         this.field.setHeight(18);
30808         //this.addSeparator();
30809         this.next = this.addButton({
30810             tooltip: this.nextText,
30811             cls: "x-btn-icon x-grid-page-next",
30812             disabled: true,
30813             handler: this.onClick.createDelegate(this, ["next"])
30814         });
30815         this.last = this.addButton({
30816             tooltip: this.lastText,
30817             cls: "x-btn-icon x-grid-page-last",
30818             disabled: true,
30819             handler: this.onClick.createDelegate(this, ["last"])
30820         });
30821         //this.addSeparator();
30822         this.loading = this.addButton({
30823             tooltip: this.refreshText,
30824             cls: "x-btn-icon x-grid-loading",
30825             handler: this.onClick.createDelegate(this, ["refresh"])
30826         });
30827
30828         if(this.displayInfo){
30829             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
30830         }
30831     },
30832
30833     // private
30834     updateInfo : function(){
30835         if(this.displayEl){
30836             var count = this.ds.getCount();
30837             var msg = count == 0 ?
30838                 this.emptyMsg :
30839                 String.format(
30840                     this.displayMsg,
30841                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
30842                 );
30843             this.displayEl.update(msg);
30844         }
30845     },
30846
30847     // private
30848     onLoad : function(ds, r, o){
30849        this.cursor = o.params ? o.params.start : 0;
30850        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
30851
30852        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
30853        this.field.dom.value = ap;
30854        this.first.setDisabled(ap == 1);
30855        this.prev.setDisabled(ap == 1);
30856        this.next.setDisabled(ap == ps);
30857        this.last.setDisabled(ap == ps);
30858        this.loading.enable();
30859        this.updateInfo();
30860     },
30861
30862     // private
30863     getPageData : function(){
30864         var total = this.ds.getTotalCount();
30865         return {
30866             total : total,
30867             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
30868             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
30869         };
30870     },
30871
30872     // private
30873     onLoadError : function(){
30874         this.loading.enable();
30875     },
30876
30877     // private
30878     onPagingKeydown : function(e){
30879         var k = e.getKey();
30880         var d = this.getPageData();
30881         if(k == e.RETURN){
30882             var v = this.field.dom.value, pageNum;
30883             if(!v || isNaN(pageNum = parseInt(v, 10))){
30884                 this.field.dom.value = d.activePage;
30885                 return;
30886             }
30887             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
30888             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
30889             e.stopEvent();
30890         }
30891         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))
30892         {
30893           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
30894           this.field.dom.value = pageNum;
30895           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
30896           e.stopEvent();
30897         }
30898         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
30899         {
30900           var v = this.field.dom.value, pageNum; 
30901           var increment = (e.shiftKey) ? 10 : 1;
30902           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
30903             increment *= -1;
30904           }
30905           if(!v || isNaN(pageNum = parseInt(v, 10))) {
30906             this.field.dom.value = d.activePage;
30907             return;
30908           }
30909           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
30910           {
30911             this.field.dom.value = parseInt(v, 10) + increment;
30912             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
30913             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
30914           }
30915           e.stopEvent();
30916         }
30917     },
30918
30919     // private
30920     beforeLoad : function(){
30921         if(this.loading){
30922             this.loading.disable();
30923         }
30924     },
30925
30926     // private
30927     onClick : function(which){
30928         var ds = this.ds;
30929         switch(which){
30930             case "first":
30931                 ds.load({params:{start: 0, limit: this.pageSize}});
30932             break;
30933             case "prev":
30934                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
30935             break;
30936             case "next":
30937                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
30938             break;
30939             case "last":
30940                 var total = ds.getTotalCount();
30941                 var extra = total % this.pageSize;
30942                 var lastStart = extra ? (total - extra) : total-this.pageSize;
30943                 ds.load({params:{start: lastStart, limit: this.pageSize}});
30944             break;
30945             case "refresh":
30946                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
30947             break;
30948         }
30949     },
30950
30951     /**
30952      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
30953      * @param {Roo.data.Store} store The data store to unbind
30954      */
30955     unbind : function(ds){
30956         ds.un("beforeload", this.beforeLoad, this);
30957         ds.un("load", this.onLoad, this);
30958         ds.un("loadexception", this.onLoadError, this);
30959         ds.un("remove", this.updateInfo, this);
30960         ds.un("add", this.updateInfo, this);
30961         this.ds = undefined;
30962     },
30963
30964     /**
30965      * Binds the paging toolbar to the specified {@link Roo.data.Store}
30966      * @param {Roo.data.Store} store The data store to bind
30967      */
30968     bind : function(ds){
30969         ds.on("beforeload", this.beforeLoad, this);
30970         ds.on("load", this.onLoad, this);
30971         ds.on("loadexception", this.onLoadError, this);
30972         ds.on("remove", this.updateInfo, this);
30973         ds.on("add", this.updateInfo, this);
30974         this.ds = ds;
30975     }
30976 });/*
30977  * Based on:
30978  * Ext JS Library 1.1.1
30979  * Copyright(c) 2006-2007, Ext JS, LLC.
30980  *
30981  * Originally Released Under LGPL - original licence link has changed is not relivant.
30982  *
30983  * Fork - LGPL
30984  * <script type="text/javascript">
30985  */
30986
30987 /**
30988  * @class Roo.Resizable
30989  * @extends Roo.util.Observable
30990  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
30991  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
30992  * 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
30993  * the element will be wrapped for you automatically.</p>
30994  * <p>Here is the list of valid resize handles:</p>
30995  * <pre>
30996 Value   Description
30997 ------  -------------------
30998  'n'     north
30999  's'     south
31000  'e'     east
31001  'w'     west
31002  'nw'    northwest
31003  'sw'    southwest
31004  'se'    southeast
31005  'ne'    northeast
31006  'hd'    horizontal drag
31007  'all'   all
31008 </pre>
31009  * <p>Here's an example showing the creation of a typical Resizable:</p>
31010  * <pre><code>
31011 var resizer = new Roo.Resizable("element-id", {
31012     handles: 'all',
31013     minWidth: 200,
31014     minHeight: 100,
31015     maxWidth: 500,
31016     maxHeight: 400,
31017     pinned: true
31018 });
31019 resizer.on("resize", myHandler);
31020 </code></pre>
31021  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
31022  * resizer.east.setDisplayed(false);</p>
31023  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
31024  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
31025  * resize operation's new size (defaults to [0, 0])
31026  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
31027  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
31028  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
31029  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
31030  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
31031  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
31032  * @cfg {Number} width The width of the element in pixels (defaults to null)
31033  * @cfg {Number} height The height of the element in pixels (defaults to null)
31034  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
31035  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
31036  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
31037  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
31038  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
31039  * in favor of the handles config option (defaults to false)
31040  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
31041  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
31042  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
31043  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
31044  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
31045  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
31046  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
31047  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
31048  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
31049  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
31050  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
31051  * @constructor
31052  * Create a new resizable component
31053  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
31054  * @param {Object} config configuration options
31055   */
31056 Roo.Resizable = function(el, config)
31057 {
31058     this.el = Roo.get(el);
31059
31060     if(config && config.wrap){
31061         config.resizeChild = this.el;
31062         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
31063         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
31064         this.el.setStyle("overflow", "hidden");
31065         this.el.setPositioning(config.resizeChild.getPositioning());
31066         config.resizeChild.clearPositioning();
31067         if(!config.width || !config.height){
31068             var csize = config.resizeChild.getSize();
31069             this.el.setSize(csize.width, csize.height);
31070         }
31071         if(config.pinned && !config.adjustments){
31072             config.adjustments = "auto";
31073         }
31074     }
31075
31076     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
31077     this.proxy.unselectable();
31078     this.proxy.enableDisplayMode('block');
31079
31080     Roo.apply(this, config);
31081
31082     if(this.pinned){
31083         this.disableTrackOver = true;
31084         this.el.addClass("x-resizable-pinned");
31085     }
31086     // if the element isn't positioned, make it relative
31087     var position = this.el.getStyle("position");
31088     if(position != "absolute" && position != "fixed"){
31089         this.el.setStyle("position", "relative");
31090     }
31091     if(!this.handles){ // no handles passed, must be legacy style
31092         this.handles = 's,e,se';
31093         if(this.multiDirectional){
31094             this.handles += ',n,w';
31095         }
31096     }
31097     if(this.handles == "all"){
31098         this.handles = "n s e w ne nw se sw";
31099     }
31100     var hs = this.handles.split(/\s*?[,;]\s*?| /);
31101     var ps = Roo.Resizable.positions;
31102     for(var i = 0, len = hs.length; i < len; i++){
31103         if(hs[i] && ps[hs[i]]){
31104             var pos = ps[hs[i]];
31105             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
31106         }
31107     }
31108     // legacy
31109     this.corner = this.southeast;
31110     
31111     // updateBox = the box can move..
31112     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
31113         this.updateBox = true;
31114     }
31115
31116     this.activeHandle = null;
31117
31118     if(this.resizeChild){
31119         if(typeof this.resizeChild == "boolean"){
31120             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
31121         }else{
31122             this.resizeChild = Roo.get(this.resizeChild, true);
31123         }
31124     }
31125     
31126     if(this.adjustments == "auto"){
31127         var rc = this.resizeChild;
31128         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
31129         if(rc && (hw || hn)){
31130             rc.position("relative");
31131             rc.setLeft(hw ? hw.el.getWidth() : 0);
31132             rc.setTop(hn ? hn.el.getHeight() : 0);
31133         }
31134         this.adjustments = [
31135             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
31136             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
31137         ];
31138     }
31139
31140     if(this.draggable){
31141         this.dd = this.dynamic ?
31142             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
31143         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
31144     }
31145
31146     // public events
31147     this.addEvents({
31148         /**
31149          * @event beforeresize
31150          * Fired before resize is allowed. Set enabled to false to cancel resize.
31151          * @param {Roo.Resizable} this
31152          * @param {Roo.EventObject} e The mousedown event
31153          */
31154         "beforeresize" : true,
31155         /**
31156          * @event resizing
31157          * Fired a resizing.
31158          * @param {Roo.Resizable} this
31159          * @param {Number} x The new x position
31160          * @param {Number} y The new y position
31161          * @param {Number} w The new w width
31162          * @param {Number} h The new h hight
31163          * @param {Roo.EventObject} e The mouseup event
31164          */
31165         "resizing" : true,
31166         /**
31167          * @event resize
31168          * Fired after a resize.
31169          * @param {Roo.Resizable} this
31170          * @param {Number} width The new width
31171          * @param {Number} height The new height
31172          * @param {Roo.EventObject} e The mouseup event
31173          */
31174         "resize" : true
31175     });
31176
31177     if(this.width !== null && this.height !== null){
31178         this.resizeTo(this.width, this.height);
31179     }else{
31180         this.updateChildSize();
31181     }
31182     if(Roo.isIE){
31183         this.el.dom.style.zoom = 1;
31184     }
31185     Roo.Resizable.superclass.constructor.call(this);
31186 };
31187
31188 Roo.extend(Roo.Resizable, Roo.util.Observable, {
31189         resizeChild : false,
31190         adjustments : [0, 0],
31191         minWidth : 5,
31192         minHeight : 5,
31193         maxWidth : 10000,
31194         maxHeight : 10000,
31195         enabled : true,
31196         animate : false,
31197         duration : .35,
31198         dynamic : false,
31199         handles : false,
31200         multiDirectional : false,
31201         disableTrackOver : false,
31202         easing : 'easeOutStrong',
31203         widthIncrement : 0,
31204         heightIncrement : 0,
31205         pinned : false,
31206         width : null,
31207         height : null,
31208         preserveRatio : false,
31209         transparent: false,
31210         minX: 0,
31211         minY: 0,
31212         draggable: false,
31213
31214         /**
31215          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
31216          */
31217         constrainTo: undefined,
31218         /**
31219          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
31220          */
31221         resizeRegion: undefined,
31222
31223
31224     /**
31225      * Perform a manual resize
31226      * @param {Number} width
31227      * @param {Number} height
31228      */
31229     resizeTo : function(width, height){
31230         this.el.setSize(width, height);
31231         this.updateChildSize();
31232         this.fireEvent("resize", this, width, height, null);
31233     },
31234
31235     // private
31236     startSizing : function(e, handle){
31237         this.fireEvent("beforeresize", this, e);
31238         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
31239
31240             if(!this.overlay){
31241                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
31242                 this.overlay.unselectable();
31243                 this.overlay.enableDisplayMode("block");
31244                 this.overlay.on("mousemove", this.onMouseMove, this);
31245                 this.overlay.on("mouseup", this.onMouseUp, this);
31246             }
31247             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
31248
31249             this.resizing = true;
31250             this.startBox = this.el.getBox();
31251             this.startPoint = e.getXY();
31252             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
31253                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
31254
31255             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
31256             this.overlay.show();
31257
31258             if(this.constrainTo) {
31259                 var ct = Roo.get(this.constrainTo);
31260                 this.resizeRegion = ct.getRegion().adjust(
31261                     ct.getFrameWidth('t'),
31262                     ct.getFrameWidth('l'),
31263                     -ct.getFrameWidth('b'),
31264                     -ct.getFrameWidth('r')
31265                 );
31266             }
31267
31268             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
31269             this.proxy.show();
31270             this.proxy.setBox(this.startBox);
31271             if(!this.dynamic){
31272                 this.proxy.setStyle('visibility', 'visible');
31273             }
31274         }
31275     },
31276
31277     // private
31278     onMouseDown : function(handle, e){
31279         if(this.enabled){
31280             e.stopEvent();
31281             this.activeHandle = handle;
31282             this.startSizing(e, handle);
31283         }
31284     },
31285
31286     // private
31287     onMouseUp : function(e){
31288         var size = this.resizeElement();
31289         this.resizing = false;
31290         this.handleOut();
31291         this.overlay.hide();
31292         this.proxy.hide();
31293         this.fireEvent("resize", this, size.width, size.height, e);
31294     },
31295
31296     // private
31297     updateChildSize : function(){
31298         
31299         if(this.resizeChild){
31300             var el = this.el;
31301             var child = this.resizeChild;
31302             var adj = this.adjustments;
31303             if(el.dom.offsetWidth){
31304                 var b = el.getSize(true);
31305                 child.setSize(b.width+adj[0], b.height+adj[1]);
31306             }
31307             // Second call here for IE
31308             // The first call enables instant resizing and
31309             // the second call corrects scroll bars if they
31310             // exist
31311             if(Roo.isIE){
31312                 setTimeout(function(){
31313                     if(el.dom.offsetWidth){
31314                         var b = el.getSize(true);
31315                         child.setSize(b.width+adj[0], b.height+adj[1]);
31316                     }
31317                 }, 10);
31318             }
31319         }
31320     },
31321
31322     // private
31323     snap : function(value, inc, min){
31324         if(!inc || !value) {
31325             return value;
31326         }
31327         var newValue = value;
31328         var m = value % inc;
31329         if(m > 0){
31330             if(m > (inc/2)){
31331                 newValue = value + (inc-m);
31332             }else{
31333                 newValue = value - m;
31334             }
31335         }
31336         return Math.max(min, newValue);
31337     },
31338
31339     // private
31340     resizeElement : function(){
31341         var box = this.proxy.getBox();
31342         if(this.updateBox){
31343             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
31344         }else{
31345             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
31346         }
31347         this.updateChildSize();
31348         if(!this.dynamic){
31349             this.proxy.hide();
31350         }
31351         return box;
31352     },
31353
31354     // private
31355     constrain : function(v, diff, m, mx){
31356         if(v - diff < m){
31357             diff = v - m;
31358         }else if(v - diff > mx){
31359             diff = mx - v;
31360         }
31361         return diff;
31362     },
31363
31364     // private
31365     onMouseMove : function(e){
31366         
31367         if(this.enabled){
31368             try{// try catch so if something goes wrong the user doesn't get hung
31369
31370             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
31371                 return;
31372             }
31373
31374             //var curXY = this.startPoint;
31375             var curSize = this.curSize || this.startBox;
31376             var x = this.startBox.x, y = this.startBox.y;
31377             var ox = x, oy = y;
31378             var w = curSize.width, h = curSize.height;
31379             var ow = w, oh = h;
31380             var mw = this.minWidth, mh = this.minHeight;
31381             var mxw = this.maxWidth, mxh = this.maxHeight;
31382             var wi = this.widthIncrement;
31383             var hi = this.heightIncrement;
31384
31385             var eventXY = e.getXY();
31386             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
31387             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
31388
31389             var pos = this.activeHandle.position;
31390
31391             switch(pos){
31392                 case "east":
31393                     w += diffX;
31394                     w = Math.min(Math.max(mw, w), mxw);
31395                     break;
31396              
31397                 case "south":
31398                     h += diffY;
31399                     h = Math.min(Math.max(mh, h), mxh);
31400                     break;
31401                 case "southeast":
31402                     w += diffX;
31403                     h += diffY;
31404                     w = Math.min(Math.max(mw, w), mxw);
31405                     h = Math.min(Math.max(mh, h), mxh);
31406                     break;
31407                 case "north":
31408                     diffY = this.constrain(h, diffY, mh, mxh);
31409                     y += diffY;
31410                     h -= diffY;
31411                     break;
31412                 case "hdrag":
31413                     
31414                     if (wi) {
31415                         var adiffX = Math.abs(diffX);
31416                         var sub = (adiffX % wi); // how much 
31417                         if (sub > (wi/2)) { // far enough to snap
31418                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
31419                         } else {
31420                             // remove difference.. 
31421                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
31422                         }
31423                     }
31424                     x += diffX;
31425                     x = Math.max(this.minX, x);
31426                     break;
31427                 case "west":
31428                     diffX = this.constrain(w, diffX, mw, mxw);
31429                     x += diffX;
31430                     w -= diffX;
31431                     break;
31432                 case "northeast":
31433                     w += diffX;
31434                     w = Math.min(Math.max(mw, w), mxw);
31435                     diffY = this.constrain(h, diffY, mh, mxh);
31436                     y += diffY;
31437                     h -= diffY;
31438                     break;
31439                 case "northwest":
31440                     diffX = this.constrain(w, diffX, mw, mxw);
31441                     diffY = this.constrain(h, diffY, mh, mxh);
31442                     y += diffY;
31443                     h -= diffY;
31444                     x += diffX;
31445                     w -= diffX;
31446                     break;
31447                case "southwest":
31448                     diffX = this.constrain(w, diffX, mw, mxw);
31449                     h += diffY;
31450                     h = Math.min(Math.max(mh, h), mxh);
31451                     x += diffX;
31452                     w -= diffX;
31453                     break;
31454             }
31455
31456             var sw = this.snap(w, wi, mw);
31457             var sh = this.snap(h, hi, mh);
31458             if(sw != w || sh != h){
31459                 switch(pos){
31460                     case "northeast":
31461                         y -= sh - h;
31462                     break;
31463                     case "north":
31464                         y -= sh - h;
31465                         break;
31466                     case "southwest":
31467                         x -= sw - w;
31468                     break;
31469                     case "west":
31470                         x -= sw - w;
31471                         break;
31472                     case "northwest":
31473                         x -= sw - w;
31474                         y -= sh - h;
31475                     break;
31476                 }
31477                 w = sw;
31478                 h = sh;
31479             }
31480
31481             if(this.preserveRatio){
31482                 switch(pos){
31483                     case "southeast":
31484                     case "east":
31485                         h = oh * (w/ow);
31486                         h = Math.min(Math.max(mh, h), mxh);
31487                         w = ow * (h/oh);
31488                        break;
31489                     case "south":
31490                         w = ow * (h/oh);
31491                         w = Math.min(Math.max(mw, w), mxw);
31492                         h = oh * (w/ow);
31493                         break;
31494                     case "northeast":
31495                         w = ow * (h/oh);
31496                         w = Math.min(Math.max(mw, w), mxw);
31497                         h = oh * (w/ow);
31498                     break;
31499                     case "north":
31500                         var tw = w;
31501                         w = ow * (h/oh);
31502                         w = Math.min(Math.max(mw, w), mxw);
31503                         h = oh * (w/ow);
31504                         x += (tw - w) / 2;
31505                         break;
31506                     case "southwest":
31507                         h = oh * (w/ow);
31508                         h = Math.min(Math.max(mh, h), mxh);
31509                         var tw = w;
31510                         w = ow * (h/oh);
31511                         x += tw - w;
31512                         break;
31513                     case "west":
31514                         var th = h;
31515                         h = oh * (w/ow);
31516                         h = Math.min(Math.max(mh, h), mxh);
31517                         y += (th - h) / 2;
31518                         var tw = w;
31519                         w = ow * (h/oh);
31520                         x += tw - w;
31521                        break;
31522                     case "northwest":
31523                         var tw = w;
31524                         var th = h;
31525                         h = oh * (w/ow);
31526                         h = Math.min(Math.max(mh, h), mxh);
31527                         w = ow * (h/oh);
31528                         y += th - h;
31529                         x += tw - w;
31530                        break;
31531
31532                 }
31533             }
31534             if (pos == 'hdrag') {
31535                 w = ow;
31536             }
31537             this.proxy.setBounds(x, y, w, h);
31538             if(this.dynamic){
31539                 this.resizeElement();
31540             }
31541             }catch(e){}
31542         }
31543         this.fireEvent("resizing", this, x, y, w, h, e);
31544     },
31545
31546     // private
31547     handleOver : function(){
31548         if(this.enabled){
31549             this.el.addClass("x-resizable-over");
31550         }
31551     },
31552
31553     // private
31554     handleOut : function(){
31555         if(!this.resizing){
31556             this.el.removeClass("x-resizable-over");
31557         }
31558     },
31559
31560     /**
31561      * Returns the element this component is bound to.
31562      * @return {Roo.Element}
31563      */
31564     getEl : function(){
31565         return this.el;
31566     },
31567
31568     /**
31569      * Returns the resizeChild element (or null).
31570      * @return {Roo.Element}
31571      */
31572     getResizeChild : function(){
31573         return this.resizeChild;
31574     },
31575     groupHandler : function()
31576     {
31577         
31578     },
31579     /**
31580      * Destroys this resizable. If the element was wrapped and
31581      * removeEl is not true then the element remains.
31582      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
31583      */
31584     destroy : function(removeEl){
31585         this.proxy.remove();
31586         if(this.overlay){
31587             this.overlay.removeAllListeners();
31588             this.overlay.remove();
31589         }
31590         var ps = Roo.Resizable.positions;
31591         for(var k in ps){
31592             if(typeof ps[k] != "function" && this[ps[k]]){
31593                 var h = this[ps[k]];
31594                 h.el.removeAllListeners();
31595                 h.el.remove();
31596             }
31597         }
31598         if(removeEl){
31599             this.el.update("");
31600             this.el.remove();
31601         }
31602     }
31603 });
31604
31605 // private
31606 // hash to map config positions to true positions
31607 Roo.Resizable.positions = {
31608     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
31609     hd: "hdrag"
31610 };
31611
31612 // private
31613 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
31614     if(!this.tpl){
31615         // only initialize the template if resizable is used
31616         var tpl = Roo.DomHelper.createTemplate(
31617             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
31618         );
31619         tpl.compile();
31620         Roo.Resizable.Handle.prototype.tpl = tpl;
31621     }
31622     this.position = pos;
31623     this.rz = rz;
31624     // show north drag fro topdra
31625     var handlepos = pos == 'hdrag' ? 'north' : pos;
31626     
31627     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
31628     if (pos == 'hdrag') {
31629         this.el.setStyle('cursor', 'pointer');
31630     }
31631     this.el.unselectable();
31632     if(transparent){
31633         this.el.setOpacity(0);
31634     }
31635     this.el.on("mousedown", this.onMouseDown, this);
31636     if(!disableTrackOver){
31637         this.el.on("mouseover", this.onMouseOver, this);
31638         this.el.on("mouseout", this.onMouseOut, this);
31639     }
31640 };
31641
31642 // private
31643 Roo.Resizable.Handle.prototype = {
31644     afterResize : function(rz){
31645         Roo.log('after?');
31646         // do nothing
31647     },
31648     // private
31649     onMouseDown : function(e){
31650         this.rz.onMouseDown(this, e);
31651     },
31652     // private
31653     onMouseOver : function(e){
31654         this.rz.handleOver(this, e);
31655     },
31656     // private
31657     onMouseOut : function(e){
31658         this.rz.handleOut(this, e);
31659     }
31660 };/*
31661  * Based on:
31662  * Ext JS Library 1.1.1
31663  * Copyright(c) 2006-2007, Ext JS, LLC.
31664  *
31665  * Originally Released Under LGPL - original licence link has changed is not relivant.
31666  *
31667  * Fork - LGPL
31668  * <script type="text/javascript">
31669  */
31670
31671 /**
31672  * @class Roo.Editor
31673  * @extends Roo.Component
31674  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
31675  * @constructor
31676  * Create a new Editor
31677  * @param {Roo.form.Field} field The Field object (or descendant)
31678  * @param {Object} config The config object
31679  */
31680 Roo.Editor = function(field, config){
31681     Roo.Editor.superclass.constructor.call(this, config);
31682     this.field = field;
31683     this.addEvents({
31684         /**
31685              * @event beforestartedit
31686              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
31687              * false from the handler of this event.
31688              * @param {Editor} this
31689              * @param {Roo.Element} boundEl The underlying element bound to this editor
31690              * @param {Mixed} value The field value being set
31691              */
31692         "beforestartedit" : true,
31693         /**
31694              * @event startedit
31695              * Fires when this editor is displayed
31696              * @param {Roo.Element} boundEl The underlying element bound to this editor
31697              * @param {Mixed} value The starting field value
31698              */
31699         "startedit" : true,
31700         /**
31701              * @event beforecomplete
31702              * Fires after a change has been made to the field, but before the change is reflected in the underlying
31703              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
31704              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
31705              * event will not fire since no edit actually occurred.
31706              * @param {Editor} this
31707              * @param {Mixed} value The current field value
31708              * @param {Mixed} startValue The original field value
31709              */
31710         "beforecomplete" : true,
31711         /**
31712              * @event complete
31713              * Fires after editing is complete and any changed value has been written to the underlying field.
31714              * @param {Editor} this
31715              * @param {Mixed} value The current field value
31716              * @param {Mixed} startValue The original field value
31717              */
31718         "complete" : true,
31719         /**
31720          * @event specialkey
31721          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
31722          * {@link Roo.EventObject#getKey} to determine which key was pressed.
31723          * @param {Roo.form.Field} this
31724          * @param {Roo.EventObject} e The event object
31725          */
31726         "specialkey" : true
31727     });
31728 };
31729
31730 Roo.extend(Roo.Editor, Roo.Component, {
31731     /**
31732      * @cfg {Boolean/String} autosize
31733      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
31734      * or "height" to adopt the height only (defaults to false)
31735      */
31736     /**
31737      * @cfg {Boolean} revertInvalid
31738      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
31739      * validation fails (defaults to true)
31740      */
31741     /**
31742      * @cfg {Boolean} ignoreNoChange
31743      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
31744      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
31745      * will never be ignored.
31746      */
31747     /**
31748      * @cfg {Boolean} hideEl
31749      * False to keep the bound element visible while the editor is displayed (defaults to true)
31750      */
31751     /**
31752      * @cfg {Mixed} value
31753      * The data value of the underlying field (defaults to "")
31754      */
31755     value : "",
31756     /**
31757      * @cfg {String} alignment
31758      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
31759      */
31760     alignment: "c-c?",
31761     /**
31762      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
31763      * for bottom-right shadow (defaults to "frame")
31764      */
31765     shadow : "frame",
31766     /**
31767      * @cfg {Boolean} constrain True to constrain the editor to the viewport
31768      */
31769     constrain : false,
31770     /**
31771      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
31772      */
31773     completeOnEnter : false,
31774     /**
31775      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
31776      */
31777     cancelOnEsc : false,
31778     /**
31779      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
31780      */
31781     updateEl : false,
31782
31783     // private
31784     onRender : function(ct, position){
31785         this.el = new Roo.Layer({
31786             shadow: this.shadow,
31787             cls: "x-editor",
31788             parentEl : ct,
31789             shim : this.shim,
31790             shadowOffset:4,
31791             id: this.id,
31792             constrain: this.constrain
31793         });
31794         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
31795         if(this.field.msgTarget != 'title'){
31796             this.field.msgTarget = 'qtip';
31797         }
31798         this.field.render(this.el);
31799         if(Roo.isGecko){
31800             this.field.el.dom.setAttribute('autocomplete', 'off');
31801         }
31802         this.field.on("specialkey", this.onSpecialKey, this);
31803         if(this.swallowKeys){
31804             this.field.el.swallowEvent(['keydown','keypress']);
31805         }
31806         this.field.show();
31807         this.field.on("blur", this.onBlur, this);
31808         if(this.field.grow){
31809             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
31810         }
31811     },
31812
31813     onSpecialKey : function(field, e)
31814     {
31815         //Roo.log('editor onSpecialKey');
31816         if(this.completeOnEnter && e.getKey() == e.ENTER){
31817             e.stopEvent();
31818             this.completeEdit();
31819             return;
31820         }
31821         // do not fire special key otherwise it might hide close the editor...
31822         if(e.getKey() == e.ENTER){    
31823             return;
31824         }
31825         if(this.cancelOnEsc && e.getKey() == e.ESC){
31826             this.cancelEdit();
31827             return;
31828         } 
31829         this.fireEvent('specialkey', field, e);
31830     
31831     },
31832
31833     /**
31834      * Starts the editing process and shows the editor.
31835      * @param {String/HTMLElement/Element} el The element to edit
31836      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
31837       * to the innerHTML of el.
31838      */
31839     startEdit : function(el, value){
31840         if(this.editing){
31841             this.completeEdit();
31842         }
31843         this.boundEl = Roo.get(el);
31844         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
31845         if(!this.rendered){
31846             this.render(this.parentEl || document.body);
31847         }
31848         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
31849             return;
31850         }
31851         this.startValue = v;
31852         this.field.setValue(v);
31853         if(this.autoSize){
31854             var sz = this.boundEl.getSize();
31855             switch(this.autoSize){
31856                 case "width":
31857                 this.setSize(sz.width,  "");
31858                 break;
31859                 case "height":
31860                 this.setSize("",  sz.height);
31861                 break;
31862                 default:
31863                 this.setSize(sz.width,  sz.height);
31864             }
31865         }
31866         this.el.alignTo(this.boundEl, this.alignment);
31867         this.editing = true;
31868         if(Roo.QuickTips){
31869             Roo.QuickTips.disable();
31870         }
31871         this.show();
31872     },
31873
31874     /**
31875      * Sets the height and width of this editor.
31876      * @param {Number} width The new width
31877      * @param {Number} height The new height
31878      */
31879     setSize : function(w, h){
31880         this.field.setSize(w, h);
31881         if(this.el){
31882             this.el.sync();
31883         }
31884     },
31885
31886     /**
31887      * Realigns the editor to the bound field based on the current alignment config value.
31888      */
31889     realign : function(){
31890         this.el.alignTo(this.boundEl, this.alignment);
31891     },
31892
31893     /**
31894      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
31895      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
31896      */
31897     completeEdit : function(remainVisible){
31898         if(!this.editing){
31899             return;
31900         }
31901         var v = this.getValue();
31902         if(this.revertInvalid !== false && !this.field.isValid()){
31903             v = this.startValue;
31904             this.cancelEdit(true);
31905         }
31906         if(String(v) === String(this.startValue) && this.ignoreNoChange){
31907             this.editing = false;
31908             this.hide();
31909             return;
31910         }
31911         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
31912             this.editing = false;
31913             if(this.updateEl && this.boundEl){
31914                 this.boundEl.update(v);
31915             }
31916             if(remainVisible !== true){
31917                 this.hide();
31918             }
31919             this.fireEvent("complete", this, v, this.startValue);
31920         }
31921     },
31922
31923     // private
31924     onShow : function(){
31925         this.el.show();
31926         if(this.hideEl !== false){
31927             this.boundEl.hide();
31928         }
31929         this.field.show();
31930         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
31931             this.fixIEFocus = true;
31932             this.deferredFocus.defer(50, this);
31933         }else{
31934             this.field.focus();
31935         }
31936         this.fireEvent("startedit", this.boundEl, this.startValue);
31937     },
31938
31939     deferredFocus : function(){
31940         if(this.editing){
31941             this.field.focus();
31942         }
31943     },
31944
31945     /**
31946      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
31947      * reverted to the original starting value.
31948      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
31949      * cancel (defaults to false)
31950      */
31951     cancelEdit : function(remainVisible){
31952         if(this.editing){
31953             this.setValue(this.startValue);
31954             if(remainVisible !== true){
31955                 this.hide();
31956             }
31957         }
31958     },
31959
31960     // private
31961     onBlur : function(){
31962         if(this.allowBlur !== true && this.editing){
31963             this.completeEdit();
31964         }
31965     },
31966
31967     // private
31968     onHide : function(){
31969         if(this.editing){
31970             this.completeEdit();
31971             return;
31972         }
31973         this.field.blur();
31974         if(this.field.collapse){
31975             this.field.collapse();
31976         }
31977         this.el.hide();
31978         if(this.hideEl !== false){
31979             this.boundEl.show();
31980         }
31981         if(Roo.QuickTips){
31982             Roo.QuickTips.enable();
31983         }
31984     },
31985
31986     /**
31987      * Sets the data value of the editor
31988      * @param {Mixed} value Any valid value supported by the underlying field
31989      */
31990     setValue : function(v){
31991         this.field.setValue(v);
31992     },
31993
31994     /**
31995      * Gets the data value of the editor
31996      * @return {Mixed} The data value
31997      */
31998     getValue : function(){
31999         return this.field.getValue();
32000     }
32001 });/*
32002  * Based on:
32003  * Ext JS Library 1.1.1
32004  * Copyright(c) 2006-2007, Ext JS, LLC.
32005  *
32006  * Originally Released Under LGPL - original licence link has changed is not relivant.
32007  *
32008  * Fork - LGPL
32009  * <script type="text/javascript">
32010  */
32011  
32012 /**
32013  * @class Roo.BasicDialog
32014  * @extends Roo.util.Observable
32015  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
32016  * <pre><code>
32017 var dlg = new Roo.BasicDialog("my-dlg", {
32018     height: 200,
32019     width: 300,
32020     minHeight: 100,
32021     minWidth: 150,
32022     modal: true,
32023     proxyDrag: true,
32024     shadow: true
32025 });
32026 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
32027 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
32028 dlg.addButton('Cancel', dlg.hide, dlg);
32029 dlg.show();
32030 </code></pre>
32031   <b>A Dialog should always be a direct child of the body element.</b>
32032  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
32033  * @cfg {String} title Default text to display in the title bar (defaults to null)
32034  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
32035  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
32036  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
32037  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
32038  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
32039  * (defaults to null with no animation)
32040  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
32041  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
32042  * property for valid values (defaults to 'all')
32043  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
32044  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
32045  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
32046  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
32047  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
32048  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
32049  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
32050  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
32051  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
32052  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
32053  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
32054  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
32055  * draggable = true (defaults to false)
32056  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
32057  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
32058  * shadow (defaults to false)
32059  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
32060  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
32061  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
32062  * @cfg {Array} buttons Array of buttons
32063  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
32064  * @constructor
32065  * Create a new BasicDialog.
32066  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
32067  * @param {Object} config Configuration options
32068  */
32069 Roo.BasicDialog = function(el, config){
32070     this.el = Roo.get(el);
32071     var dh = Roo.DomHelper;
32072     if(!this.el && config && config.autoCreate){
32073         if(typeof config.autoCreate == "object"){
32074             if(!config.autoCreate.id){
32075                 config.autoCreate.id = el;
32076             }
32077             this.el = dh.append(document.body,
32078                         config.autoCreate, true);
32079         }else{
32080             this.el = dh.append(document.body,
32081                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
32082         }
32083     }
32084     el = this.el;
32085     el.setDisplayed(true);
32086     el.hide = this.hideAction;
32087     this.id = el.id;
32088     el.addClass("x-dlg");
32089
32090     Roo.apply(this, config);
32091
32092     this.proxy = el.createProxy("x-dlg-proxy");
32093     this.proxy.hide = this.hideAction;
32094     this.proxy.setOpacity(.5);
32095     this.proxy.hide();
32096
32097     if(config.width){
32098         el.setWidth(config.width);
32099     }
32100     if(config.height){
32101         el.setHeight(config.height);
32102     }
32103     this.size = el.getSize();
32104     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
32105         this.xy = [config.x,config.y];
32106     }else{
32107         this.xy = el.getCenterXY(true);
32108     }
32109     /** The header element @type Roo.Element */
32110     this.header = el.child("> .x-dlg-hd");
32111     /** The body element @type Roo.Element */
32112     this.body = el.child("> .x-dlg-bd");
32113     /** The footer element @type Roo.Element */
32114     this.footer = el.child("> .x-dlg-ft");
32115
32116     if(!this.header){
32117         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
32118     }
32119     if(!this.body){
32120         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
32121     }
32122
32123     this.header.unselectable();
32124     if(this.title){
32125         this.header.update(this.title);
32126     }
32127     // this element allows the dialog to be focused for keyboard event
32128     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
32129     this.focusEl.swallowEvent("click", true);
32130
32131     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
32132
32133     // wrap the body and footer for special rendering
32134     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
32135     if(this.footer){
32136         this.bwrap.dom.appendChild(this.footer.dom);
32137     }
32138
32139     this.bg = this.el.createChild({
32140         tag: "div", cls:"x-dlg-bg",
32141         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
32142     });
32143     this.centerBg = this.bg.child("div.x-dlg-bg-center");
32144
32145
32146     if(this.autoScroll !== false && !this.autoTabs){
32147         this.body.setStyle("overflow", "auto");
32148     }
32149
32150     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
32151
32152     if(this.closable !== false){
32153         this.el.addClass("x-dlg-closable");
32154         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
32155         this.close.on("click", this.closeClick, this);
32156         this.close.addClassOnOver("x-dlg-close-over");
32157     }
32158     if(this.collapsible !== false){
32159         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
32160         this.collapseBtn.on("click", this.collapseClick, this);
32161         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
32162         this.header.on("dblclick", this.collapseClick, this);
32163     }
32164     if(this.resizable !== false){
32165         this.el.addClass("x-dlg-resizable");
32166         this.resizer = new Roo.Resizable(el, {
32167             minWidth: this.minWidth || 80,
32168             minHeight:this.minHeight || 80,
32169             handles: this.resizeHandles || "all",
32170             pinned: true
32171         });
32172         this.resizer.on("beforeresize", this.beforeResize, this);
32173         this.resizer.on("resize", this.onResize, this);
32174     }
32175     if(this.draggable !== false){
32176         el.addClass("x-dlg-draggable");
32177         if (!this.proxyDrag) {
32178             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
32179         }
32180         else {
32181             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
32182         }
32183         dd.setHandleElId(this.header.id);
32184         dd.endDrag = this.endMove.createDelegate(this);
32185         dd.startDrag = this.startMove.createDelegate(this);
32186         dd.onDrag = this.onDrag.createDelegate(this);
32187         dd.scroll = false;
32188         this.dd = dd;
32189     }
32190     if(this.modal){
32191         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
32192         this.mask.enableDisplayMode("block");
32193         this.mask.hide();
32194         this.el.addClass("x-dlg-modal");
32195     }
32196     if(this.shadow){
32197         this.shadow = new Roo.Shadow({
32198             mode : typeof this.shadow == "string" ? this.shadow : "sides",
32199             offset : this.shadowOffset
32200         });
32201     }else{
32202         this.shadowOffset = 0;
32203     }
32204     if(Roo.useShims && this.shim !== false){
32205         this.shim = this.el.createShim();
32206         this.shim.hide = this.hideAction;
32207         this.shim.hide();
32208     }else{
32209         this.shim = false;
32210     }
32211     if(this.autoTabs){
32212         this.initTabs();
32213     }
32214     if (this.buttons) { 
32215         var bts= this.buttons;
32216         this.buttons = [];
32217         Roo.each(bts, function(b) {
32218             this.addButton(b);
32219         }, this);
32220     }
32221     
32222     
32223     this.addEvents({
32224         /**
32225          * @event keydown
32226          * Fires when a key is pressed
32227          * @param {Roo.BasicDialog} this
32228          * @param {Roo.EventObject} e
32229          */
32230         "keydown" : true,
32231         /**
32232          * @event move
32233          * Fires when this dialog is moved by the user.
32234          * @param {Roo.BasicDialog} this
32235          * @param {Number} x The new page X
32236          * @param {Number} y The new page Y
32237          */
32238         "move" : true,
32239         /**
32240          * @event resize
32241          * Fires when this dialog is resized by the user.
32242          * @param {Roo.BasicDialog} this
32243          * @param {Number} width The new width
32244          * @param {Number} height The new height
32245          */
32246         "resize" : true,
32247         /**
32248          * @event beforehide
32249          * Fires before this dialog is hidden.
32250          * @param {Roo.BasicDialog} this
32251          */
32252         "beforehide" : true,
32253         /**
32254          * @event hide
32255          * Fires when this dialog is hidden.
32256          * @param {Roo.BasicDialog} this
32257          */
32258         "hide" : true,
32259         /**
32260          * @event beforeshow
32261          * Fires before this dialog is shown.
32262          * @param {Roo.BasicDialog} this
32263          */
32264         "beforeshow" : true,
32265         /**
32266          * @event show
32267          * Fires when this dialog is shown.
32268          * @param {Roo.BasicDialog} this
32269          */
32270         "show" : true
32271     });
32272     el.on("keydown", this.onKeyDown, this);
32273     el.on("mousedown", this.toFront, this);
32274     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
32275     this.el.hide();
32276     Roo.DialogManager.register(this);
32277     Roo.BasicDialog.superclass.constructor.call(this);
32278 };
32279
32280 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
32281     shadowOffset: Roo.isIE ? 6 : 5,
32282     minHeight: 80,
32283     minWidth: 200,
32284     minButtonWidth: 75,
32285     defaultButton: null,
32286     buttonAlign: "right",
32287     tabTag: 'div',
32288     firstShow: true,
32289
32290     /**
32291      * Sets the dialog title text
32292      * @param {String} text The title text to display
32293      * @return {Roo.BasicDialog} this
32294      */
32295     setTitle : function(text){
32296         this.header.update(text);
32297         return this;
32298     },
32299
32300     // private
32301     closeClick : function(){
32302         this.hide();
32303     },
32304
32305     // private
32306     collapseClick : function(){
32307         this[this.collapsed ? "expand" : "collapse"]();
32308     },
32309
32310     /**
32311      * Collapses the dialog to its minimized state (only the title bar is visible).
32312      * Equivalent to the user clicking the collapse dialog button.
32313      */
32314     collapse : function(){
32315         if(!this.collapsed){
32316             this.collapsed = true;
32317             this.el.addClass("x-dlg-collapsed");
32318             this.restoreHeight = this.el.getHeight();
32319             this.resizeTo(this.el.getWidth(), this.header.getHeight());
32320         }
32321     },
32322
32323     /**
32324      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
32325      * clicking the expand dialog button.
32326      */
32327     expand : function(){
32328         if(this.collapsed){
32329             this.collapsed = false;
32330             this.el.removeClass("x-dlg-collapsed");
32331             this.resizeTo(this.el.getWidth(), this.restoreHeight);
32332         }
32333     },
32334
32335     /**
32336      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
32337      * @return {Roo.TabPanel} The tabs component
32338      */
32339     initTabs : function(){
32340         var tabs = this.getTabs();
32341         while(tabs.getTab(0)){
32342             tabs.removeTab(0);
32343         }
32344         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
32345             var dom = el.dom;
32346             tabs.addTab(Roo.id(dom), dom.title);
32347             dom.title = "";
32348         });
32349         tabs.activate(0);
32350         return tabs;
32351     },
32352
32353     // private
32354     beforeResize : function(){
32355         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
32356     },
32357
32358     // private
32359     onResize : function(){
32360         this.refreshSize();
32361         this.syncBodyHeight();
32362         this.adjustAssets();
32363         this.focus();
32364         this.fireEvent("resize", this, this.size.width, this.size.height);
32365     },
32366
32367     // private
32368     onKeyDown : function(e){
32369         if(this.isVisible()){
32370             this.fireEvent("keydown", this, e);
32371         }
32372     },
32373
32374     /**
32375      * Resizes the dialog.
32376      * @param {Number} width
32377      * @param {Number} height
32378      * @return {Roo.BasicDialog} this
32379      */
32380     resizeTo : function(width, height){
32381         this.el.setSize(width, height);
32382         this.size = {width: width, height: height};
32383         this.syncBodyHeight();
32384         if(this.fixedcenter){
32385             this.center();
32386         }
32387         if(this.isVisible()){
32388             this.constrainXY();
32389             this.adjustAssets();
32390         }
32391         this.fireEvent("resize", this, width, height);
32392         return this;
32393     },
32394
32395
32396     /**
32397      * Resizes the dialog to fit the specified content size.
32398      * @param {Number} width
32399      * @param {Number} height
32400      * @return {Roo.BasicDialog} this
32401      */
32402     setContentSize : function(w, h){
32403         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
32404         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
32405         //if(!this.el.isBorderBox()){
32406             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
32407             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
32408         //}
32409         if(this.tabs){
32410             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
32411             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
32412         }
32413         this.resizeTo(w, h);
32414         return this;
32415     },
32416
32417     /**
32418      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
32419      * executed in response to a particular key being pressed while the dialog is active.
32420      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
32421      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
32422      * @param {Function} fn The function to call
32423      * @param {Object} scope (optional) The scope of the function
32424      * @return {Roo.BasicDialog} this
32425      */
32426     addKeyListener : function(key, fn, scope){
32427         var keyCode, shift, ctrl, alt;
32428         if(typeof key == "object" && !(key instanceof Array)){
32429             keyCode = key["key"];
32430             shift = key["shift"];
32431             ctrl = key["ctrl"];
32432             alt = key["alt"];
32433         }else{
32434             keyCode = key;
32435         }
32436         var handler = function(dlg, e){
32437             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
32438                 var k = e.getKey();
32439                 if(keyCode instanceof Array){
32440                     for(var i = 0, len = keyCode.length; i < len; i++){
32441                         if(keyCode[i] == k){
32442                           fn.call(scope || window, dlg, k, e);
32443                           return;
32444                         }
32445                     }
32446                 }else{
32447                     if(k == keyCode){
32448                         fn.call(scope || window, dlg, k, e);
32449                     }
32450                 }
32451             }
32452         };
32453         this.on("keydown", handler);
32454         return this;
32455     },
32456
32457     /**
32458      * Returns the TabPanel component (creates it if it doesn't exist).
32459      * Note: If you wish to simply check for the existence of tabs without creating them,
32460      * check for a null 'tabs' property.
32461      * @return {Roo.TabPanel} The tabs component
32462      */
32463     getTabs : function(){
32464         if(!this.tabs){
32465             this.el.addClass("x-dlg-auto-tabs");
32466             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
32467             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
32468         }
32469         return this.tabs;
32470     },
32471
32472     /**
32473      * Adds a button to the footer section of the dialog.
32474      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
32475      * object or a valid Roo.DomHelper element config
32476      * @param {Function} handler The function called when the button is clicked
32477      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
32478      * @return {Roo.Button} The new button
32479      */
32480     addButton : function(config, handler, scope){
32481         var dh = Roo.DomHelper;
32482         if(!this.footer){
32483             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
32484         }
32485         if(!this.btnContainer){
32486             var tb = this.footer.createChild({
32487
32488                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
32489                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
32490             }, null, true);
32491             this.btnContainer = tb.firstChild.firstChild.firstChild;
32492         }
32493         var bconfig = {
32494             handler: handler,
32495             scope: scope,
32496             minWidth: this.minButtonWidth,
32497             hideParent:true
32498         };
32499         if(typeof config == "string"){
32500             bconfig.text = config;
32501         }else{
32502             if(config.tag){
32503                 bconfig.dhconfig = config;
32504             }else{
32505                 Roo.apply(bconfig, config);
32506             }
32507         }
32508         var fc = false;
32509         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
32510             bconfig.position = Math.max(0, bconfig.position);
32511             fc = this.btnContainer.childNodes[bconfig.position];
32512         }
32513          
32514         var btn = new Roo.Button(
32515             fc ? 
32516                 this.btnContainer.insertBefore(document.createElement("td"),fc)
32517                 : this.btnContainer.appendChild(document.createElement("td")),
32518             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
32519             bconfig
32520         );
32521         this.syncBodyHeight();
32522         if(!this.buttons){
32523             /**
32524              * Array of all the buttons that have been added to this dialog via addButton
32525              * @type Array
32526              */
32527             this.buttons = [];
32528         }
32529         this.buttons.push(btn);
32530         return btn;
32531     },
32532
32533     /**
32534      * Sets the default button to be focused when the dialog is displayed.
32535      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
32536      * @return {Roo.BasicDialog} this
32537      */
32538     setDefaultButton : function(btn){
32539         this.defaultButton = btn;
32540         return this;
32541     },
32542
32543     // private
32544     getHeaderFooterHeight : function(safe){
32545         var height = 0;
32546         if(this.header){
32547            height += this.header.getHeight();
32548         }
32549         if(this.footer){
32550            var fm = this.footer.getMargins();
32551             height += (this.footer.getHeight()+fm.top+fm.bottom);
32552         }
32553         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
32554         height += this.centerBg.getPadding("tb");
32555         return height;
32556     },
32557
32558     // private
32559     syncBodyHeight : function()
32560     {
32561         var bd = this.body, // the text
32562             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
32563             bw = this.bwrap;
32564         var height = this.size.height - this.getHeaderFooterHeight(false);
32565         bd.setHeight(height-bd.getMargins("tb"));
32566         var hh = this.header.getHeight();
32567         var h = this.size.height-hh;
32568         cb.setHeight(h);
32569         
32570         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
32571         bw.setHeight(h-cb.getPadding("tb"));
32572         
32573         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
32574         bd.setWidth(bw.getWidth(true));
32575         if(this.tabs){
32576             this.tabs.syncHeight();
32577             if(Roo.isIE){
32578                 this.tabs.el.repaint();
32579             }
32580         }
32581     },
32582
32583     /**
32584      * Restores the previous state of the dialog if Roo.state is configured.
32585      * @return {Roo.BasicDialog} this
32586      */
32587     restoreState : function(){
32588         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
32589         if(box && box.width){
32590             this.xy = [box.x, box.y];
32591             this.resizeTo(box.width, box.height);
32592         }
32593         return this;
32594     },
32595
32596     // private
32597     beforeShow : function(){
32598         this.expand();
32599         if(this.fixedcenter){
32600             this.xy = this.el.getCenterXY(true);
32601         }
32602         if(this.modal){
32603             Roo.get(document.body).addClass("x-body-masked");
32604             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32605             this.mask.show();
32606         }
32607         this.constrainXY();
32608     },
32609
32610     // private
32611     animShow : function(){
32612         var b = Roo.get(this.animateTarget).getBox();
32613         this.proxy.setSize(b.width, b.height);
32614         this.proxy.setLocation(b.x, b.y);
32615         this.proxy.show();
32616         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
32617                     true, .35, this.showEl.createDelegate(this));
32618     },
32619
32620     /**
32621      * Shows the dialog.
32622      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
32623      * @return {Roo.BasicDialog} this
32624      */
32625     show : function(animateTarget){
32626         if (this.fireEvent("beforeshow", this) === false){
32627             return;
32628         }
32629         if(this.syncHeightBeforeShow){
32630             this.syncBodyHeight();
32631         }else if(this.firstShow){
32632             this.firstShow = false;
32633             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
32634         }
32635         this.animateTarget = animateTarget || this.animateTarget;
32636         if(!this.el.isVisible()){
32637             this.beforeShow();
32638             if(this.animateTarget && Roo.get(this.animateTarget)){
32639                 this.animShow();
32640             }else{
32641                 this.showEl();
32642             }
32643         }
32644         return this;
32645     },
32646
32647     // private
32648     showEl : function(){
32649         this.proxy.hide();
32650         this.el.setXY(this.xy);
32651         this.el.show();
32652         this.adjustAssets(true);
32653         this.toFront();
32654         this.focus();
32655         // IE peekaboo bug - fix found by Dave Fenwick
32656         if(Roo.isIE){
32657             this.el.repaint();
32658         }
32659         this.fireEvent("show", this);
32660     },
32661
32662     /**
32663      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
32664      * dialog itself will receive focus.
32665      */
32666     focus : function(){
32667         if(this.defaultButton){
32668             this.defaultButton.focus();
32669         }else{
32670             this.focusEl.focus();
32671         }
32672     },
32673
32674     // private
32675     constrainXY : function(){
32676         if(this.constraintoviewport !== false){
32677             if(!this.viewSize){
32678                 if(this.container){
32679                     var s = this.container.getSize();
32680                     this.viewSize = [s.width, s.height];
32681                 }else{
32682                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
32683                 }
32684             }
32685             var s = Roo.get(this.container||document).getScroll();
32686
32687             var x = this.xy[0], y = this.xy[1];
32688             var w = this.size.width, h = this.size.height;
32689             var vw = this.viewSize[0], vh = this.viewSize[1];
32690             // only move it if it needs it
32691             var moved = false;
32692             // first validate right/bottom
32693             if(x + w > vw+s.left){
32694                 x = vw - w;
32695                 moved = true;
32696             }
32697             if(y + h > vh+s.top){
32698                 y = vh - h;
32699                 moved = true;
32700             }
32701             // then make sure top/left isn't negative
32702             if(x < s.left){
32703                 x = s.left;
32704                 moved = true;
32705             }
32706             if(y < s.top){
32707                 y = s.top;
32708                 moved = true;
32709             }
32710             if(moved){
32711                 // cache xy
32712                 this.xy = [x, y];
32713                 if(this.isVisible()){
32714                     this.el.setLocation(x, y);
32715                     this.adjustAssets();
32716                 }
32717             }
32718         }
32719     },
32720
32721     // private
32722     onDrag : function(){
32723         if(!this.proxyDrag){
32724             this.xy = this.el.getXY();
32725             this.adjustAssets();
32726         }
32727     },
32728
32729     // private
32730     adjustAssets : function(doShow){
32731         var x = this.xy[0], y = this.xy[1];
32732         var w = this.size.width, h = this.size.height;
32733         if(doShow === true){
32734             if(this.shadow){
32735                 this.shadow.show(this.el);
32736             }
32737             if(this.shim){
32738                 this.shim.show();
32739             }
32740         }
32741         if(this.shadow && this.shadow.isVisible()){
32742             this.shadow.show(this.el);
32743         }
32744         if(this.shim && this.shim.isVisible()){
32745             this.shim.setBounds(x, y, w, h);
32746         }
32747     },
32748
32749     // private
32750     adjustViewport : function(w, h){
32751         if(!w || !h){
32752             w = Roo.lib.Dom.getViewWidth();
32753             h = Roo.lib.Dom.getViewHeight();
32754         }
32755         // cache the size
32756         this.viewSize = [w, h];
32757         if(this.modal && this.mask.isVisible()){
32758             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
32759             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32760         }
32761         if(this.isVisible()){
32762             this.constrainXY();
32763         }
32764     },
32765
32766     /**
32767      * Destroys this dialog and all its supporting elements (including any tabs, shim,
32768      * shadow, proxy, mask, etc.)  Also removes all event listeners.
32769      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
32770      */
32771     destroy : function(removeEl){
32772         if(this.isVisible()){
32773             this.animateTarget = null;
32774             this.hide();
32775         }
32776         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
32777         if(this.tabs){
32778             this.tabs.destroy(removeEl);
32779         }
32780         Roo.destroy(
32781              this.shim,
32782              this.proxy,
32783              this.resizer,
32784              this.close,
32785              this.mask
32786         );
32787         if(this.dd){
32788             this.dd.unreg();
32789         }
32790         if(this.buttons){
32791            for(var i = 0, len = this.buttons.length; i < len; i++){
32792                this.buttons[i].destroy();
32793            }
32794         }
32795         this.el.removeAllListeners();
32796         if(removeEl === true){
32797             this.el.update("");
32798             this.el.remove();
32799         }
32800         Roo.DialogManager.unregister(this);
32801     },
32802
32803     // private
32804     startMove : function(){
32805         if(this.proxyDrag){
32806             this.proxy.show();
32807         }
32808         if(this.constraintoviewport !== false){
32809             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
32810         }
32811     },
32812
32813     // private
32814     endMove : function(){
32815         if(!this.proxyDrag){
32816             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
32817         }else{
32818             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
32819             this.proxy.hide();
32820         }
32821         this.refreshSize();
32822         this.adjustAssets();
32823         this.focus();
32824         this.fireEvent("move", this, this.xy[0], this.xy[1]);
32825     },
32826
32827     /**
32828      * Brings this dialog to the front of any other visible dialogs
32829      * @return {Roo.BasicDialog} this
32830      */
32831     toFront : function(){
32832         Roo.DialogManager.bringToFront(this);
32833         return this;
32834     },
32835
32836     /**
32837      * Sends this dialog to the back (under) of any other visible dialogs
32838      * @return {Roo.BasicDialog} this
32839      */
32840     toBack : function(){
32841         Roo.DialogManager.sendToBack(this);
32842         return this;
32843     },
32844
32845     /**
32846      * Centers this dialog in the viewport
32847      * @return {Roo.BasicDialog} this
32848      */
32849     center : function(){
32850         var xy = this.el.getCenterXY(true);
32851         this.moveTo(xy[0], xy[1]);
32852         return this;
32853     },
32854
32855     /**
32856      * Moves the dialog's top-left corner to the specified point
32857      * @param {Number} x
32858      * @param {Number} y
32859      * @return {Roo.BasicDialog} this
32860      */
32861     moveTo : function(x, y){
32862         this.xy = [x,y];
32863         if(this.isVisible()){
32864             this.el.setXY(this.xy);
32865             this.adjustAssets();
32866         }
32867         return this;
32868     },
32869
32870     /**
32871      * Aligns the dialog to the specified element
32872      * @param {String/HTMLElement/Roo.Element} element The element to align to.
32873      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
32874      * @param {Array} offsets (optional) Offset the positioning by [x, y]
32875      * @return {Roo.BasicDialog} this
32876      */
32877     alignTo : function(element, position, offsets){
32878         this.xy = this.el.getAlignToXY(element, position, offsets);
32879         if(this.isVisible()){
32880             this.el.setXY(this.xy);
32881             this.adjustAssets();
32882         }
32883         return this;
32884     },
32885
32886     /**
32887      * Anchors an element to another element and realigns it when the window is resized.
32888      * @param {String/HTMLElement/Roo.Element} element The element to align to.
32889      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
32890      * @param {Array} offsets (optional) Offset the positioning by [x, y]
32891      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
32892      * is a number, it is used as the buffer delay (defaults to 50ms).
32893      * @return {Roo.BasicDialog} this
32894      */
32895     anchorTo : function(el, alignment, offsets, monitorScroll){
32896         var action = function(){
32897             this.alignTo(el, alignment, offsets);
32898         };
32899         Roo.EventManager.onWindowResize(action, this);
32900         var tm = typeof monitorScroll;
32901         if(tm != 'undefined'){
32902             Roo.EventManager.on(window, 'scroll', action, this,
32903                 {buffer: tm == 'number' ? monitorScroll : 50});
32904         }
32905         action.call(this);
32906         return this;
32907     },
32908
32909     /**
32910      * Returns true if the dialog is visible
32911      * @return {Boolean}
32912      */
32913     isVisible : function(){
32914         return this.el.isVisible();
32915     },
32916
32917     // private
32918     animHide : function(callback){
32919         var b = Roo.get(this.animateTarget).getBox();
32920         this.proxy.show();
32921         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
32922         this.el.hide();
32923         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
32924                     this.hideEl.createDelegate(this, [callback]));
32925     },
32926
32927     /**
32928      * Hides the dialog.
32929      * @param {Function} callback (optional) Function to call when the dialog is hidden
32930      * @return {Roo.BasicDialog} this
32931      */
32932     hide : function(callback){
32933         if (this.fireEvent("beforehide", this) === false){
32934             return;
32935         }
32936         if(this.shadow){
32937             this.shadow.hide();
32938         }
32939         if(this.shim) {
32940           this.shim.hide();
32941         }
32942         // sometimes animateTarget seems to get set.. causing problems...
32943         // this just double checks..
32944         if(this.animateTarget && Roo.get(this.animateTarget)) {
32945            this.animHide(callback);
32946         }else{
32947             this.el.hide();
32948             this.hideEl(callback);
32949         }
32950         return this;
32951     },
32952
32953     // private
32954     hideEl : function(callback){
32955         this.proxy.hide();
32956         if(this.modal){
32957             this.mask.hide();
32958             Roo.get(document.body).removeClass("x-body-masked");
32959         }
32960         this.fireEvent("hide", this);
32961         if(typeof callback == "function"){
32962             callback();
32963         }
32964     },
32965
32966     // private
32967     hideAction : function(){
32968         this.setLeft("-10000px");
32969         this.setTop("-10000px");
32970         this.setStyle("visibility", "hidden");
32971     },
32972
32973     // private
32974     refreshSize : function(){
32975         this.size = this.el.getSize();
32976         this.xy = this.el.getXY();
32977         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
32978     },
32979
32980     // private
32981     // z-index is managed by the DialogManager and may be overwritten at any time
32982     setZIndex : function(index){
32983         if(this.modal){
32984             this.mask.setStyle("z-index", index);
32985         }
32986         if(this.shim){
32987             this.shim.setStyle("z-index", ++index);
32988         }
32989         if(this.shadow){
32990             this.shadow.setZIndex(++index);
32991         }
32992         this.el.setStyle("z-index", ++index);
32993         if(this.proxy){
32994             this.proxy.setStyle("z-index", ++index);
32995         }
32996         if(this.resizer){
32997             this.resizer.proxy.setStyle("z-index", ++index);
32998         }
32999
33000         this.lastZIndex = index;
33001     },
33002
33003     /**
33004      * Returns the element for this dialog
33005      * @return {Roo.Element} The underlying dialog Element
33006      */
33007     getEl : function(){
33008         return this.el;
33009     }
33010 });
33011
33012 /**
33013  * @class Roo.DialogManager
33014  * Provides global access to BasicDialogs that have been created and
33015  * support for z-indexing (layering) multiple open dialogs.
33016  */
33017 Roo.DialogManager = function(){
33018     var list = {};
33019     var accessList = [];
33020     var front = null;
33021
33022     // private
33023     var sortDialogs = function(d1, d2){
33024         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
33025     };
33026
33027     // private
33028     var orderDialogs = function(){
33029         accessList.sort(sortDialogs);
33030         var seed = Roo.DialogManager.zseed;
33031         for(var i = 0, len = accessList.length; i < len; i++){
33032             var dlg = accessList[i];
33033             if(dlg){
33034                 dlg.setZIndex(seed + (i*10));
33035             }
33036         }
33037     };
33038
33039     return {
33040         /**
33041          * The starting z-index for BasicDialogs (defaults to 9000)
33042          * @type Number The z-index value
33043          */
33044         zseed : 9000,
33045
33046         // private
33047         register : function(dlg){
33048             list[dlg.id] = dlg;
33049             accessList.push(dlg);
33050         },
33051
33052         // private
33053         unregister : function(dlg){
33054             delete list[dlg.id];
33055             var i=0;
33056             var len=0;
33057             if(!accessList.indexOf){
33058                 for(  i = 0, len = accessList.length; i < len; i++){
33059                     if(accessList[i] == dlg){
33060                         accessList.splice(i, 1);
33061                         return;
33062                     }
33063                 }
33064             }else{
33065                  i = accessList.indexOf(dlg);
33066                 if(i != -1){
33067                     accessList.splice(i, 1);
33068                 }
33069             }
33070         },
33071
33072         /**
33073          * Gets a registered dialog by id
33074          * @param {String/Object} id The id of the dialog or a dialog
33075          * @return {Roo.BasicDialog} this
33076          */
33077         get : function(id){
33078             return typeof id == "object" ? id : list[id];
33079         },
33080
33081         /**
33082          * Brings the specified dialog to the front
33083          * @param {String/Object} dlg The id of the dialog or a dialog
33084          * @return {Roo.BasicDialog} this
33085          */
33086         bringToFront : function(dlg){
33087             dlg = this.get(dlg);
33088             if(dlg != front){
33089                 front = dlg;
33090                 dlg._lastAccess = new Date().getTime();
33091                 orderDialogs();
33092             }
33093             return dlg;
33094         },
33095
33096         /**
33097          * Sends the specified dialog to the back
33098          * @param {String/Object} dlg The id of the dialog or a dialog
33099          * @return {Roo.BasicDialog} this
33100          */
33101         sendToBack : function(dlg){
33102             dlg = this.get(dlg);
33103             dlg._lastAccess = -(new Date().getTime());
33104             orderDialogs();
33105             return dlg;
33106         },
33107
33108         /**
33109          * Hides all dialogs
33110          */
33111         hideAll : function(){
33112             for(var id in list){
33113                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
33114                     list[id].hide();
33115                 }
33116             }
33117         }
33118     };
33119 }();
33120
33121 /**
33122  * @class Roo.LayoutDialog
33123  * @extends Roo.BasicDialog
33124  * Dialog which provides adjustments for working with a layout in a Dialog.
33125  * Add your necessary layout config options to the dialog's config.<br>
33126  * Example usage (including a nested layout):
33127  * <pre><code>
33128 if(!dialog){
33129     dialog = new Roo.LayoutDialog("download-dlg", {
33130         modal: true,
33131         width:600,
33132         height:450,
33133         shadow:true,
33134         minWidth:500,
33135         minHeight:350,
33136         autoTabs:true,
33137         proxyDrag:true,
33138         // layout config merges with the dialog config
33139         center:{
33140             tabPosition: "top",
33141             alwaysShowTabs: true
33142         }
33143     });
33144     dialog.addKeyListener(27, dialog.hide, dialog);
33145     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
33146     dialog.addButton("Build It!", this.getDownload, this);
33147
33148     // we can even add nested layouts
33149     var innerLayout = new Roo.BorderLayout("dl-inner", {
33150         east: {
33151             initialSize: 200,
33152             autoScroll:true,
33153             split:true
33154         },
33155         center: {
33156             autoScroll:true
33157         }
33158     });
33159     innerLayout.beginUpdate();
33160     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
33161     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
33162     innerLayout.endUpdate(true);
33163
33164     var layout = dialog.getLayout();
33165     layout.beginUpdate();
33166     layout.add("center", new Roo.ContentPanel("standard-panel",
33167                         {title: "Download the Source", fitToFrame:true}));
33168     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
33169                {title: "Build your own roo.js"}));
33170     layout.getRegion("center").showPanel(sp);
33171     layout.endUpdate();
33172 }
33173 </code></pre>
33174     * @constructor
33175     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
33176     * @param {Object} config configuration options
33177   */
33178 Roo.LayoutDialog = function(el, cfg){
33179     
33180     var config=  cfg;
33181     if (typeof(cfg) == 'undefined') {
33182         config = Roo.apply({}, el);
33183         // not sure why we use documentElement here.. - it should always be body.
33184         // IE7 borks horribly if we use documentElement.
33185         // webkit also does not like documentElement - it creates a body element...
33186         el = Roo.get( document.body || document.documentElement ).createChild();
33187         //config.autoCreate = true;
33188     }
33189     
33190     
33191     config.autoTabs = false;
33192     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
33193     this.body.setStyle({overflow:"hidden", position:"relative"});
33194     this.layout = new Roo.BorderLayout(this.body.dom, config);
33195     this.layout.monitorWindowResize = false;
33196     this.el.addClass("x-dlg-auto-layout");
33197     // fix case when center region overwrites center function
33198     this.center = Roo.BasicDialog.prototype.center;
33199     this.on("show", this.layout.layout, this.layout, true);
33200     if (config.items) {
33201         var xitems = config.items;
33202         delete config.items;
33203         Roo.each(xitems, this.addxtype, this);
33204     }
33205     
33206     
33207 };
33208 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
33209     /**
33210      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
33211      * @deprecated
33212      */
33213     endUpdate : function(){
33214         this.layout.endUpdate();
33215     },
33216
33217     /**
33218      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
33219      *  @deprecated
33220      */
33221     beginUpdate : function(){
33222         this.layout.beginUpdate();
33223     },
33224
33225     /**
33226      * Get the BorderLayout for this dialog
33227      * @return {Roo.BorderLayout}
33228      */
33229     getLayout : function(){
33230         return this.layout;
33231     },
33232
33233     showEl : function(){
33234         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
33235         if(Roo.isIE7){
33236             this.layout.layout();
33237         }
33238     },
33239
33240     // private
33241     // Use the syncHeightBeforeShow config option to control this automatically
33242     syncBodyHeight : function(){
33243         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
33244         if(this.layout){this.layout.layout();}
33245     },
33246     
33247       /**
33248      * Add an xtype element (actually adds to the layout.)
33249      * @return {Object} xdata xtype object data.
33250      */
33251     
33252     addxtype : function(c) {
33253         return this.layout.addxtype(c);
33254     }
33255 });/*
33256  * Based on:
33257  * Ext JS Library 1.1.1
33258  * Copyright(c) 2006-2007, Ext JS, LLC.
33259  *
33260  * Originally Released Under LGPL - original licence link has changed is not relivant.
33261  *
33262  * Fork - LGPL
33263  * <script type="text/javascript">
33264  */
33265  
33266 /**
33267  * @class Roo.MessageBox
33268  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
33269  * Example usage:
33270  *<pre><code>
33271 // Basic alert:
33272 Roo.Msg.alert('Status', 'Changes saved successfully.');
33273
33274 // Prompt for user data:
33275 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
33276     if (btn == 'ok'){
33277         // process text value...
33278     }
33279 });
33280
33281 // Show a dialog using config options:
33282 Roo.Msg.show({
33283    title:'Save Changes?',
33284    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
33285    buttons: Roo.Msg.YESNOCANCEL,
33286    fn: processResult,
33287    animEl: 'elId'
33288 });
33289 </code></pre>
33290  * @singleton
33291  */
33292 Roo.MessageBox = function(){
33293     var dlg, opt, mask, waitTimer;
33294     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
33295     var buttons, activeTextEl, bwidth;
33296
33297     // private
33298     var handleButton = function(button){
33299         dlg.hide();
33300         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
33301     };
33302
33303     // private
33304     var handleHide = function(){
33305         if(opt && opt.cls){
33306             dlg.el.removeClass(opt.cls);
33307         }
33308         if(waitTimer){
33309             Roo.TaskMgr.stop(waitTimer);
33310             waitTimer = null;
33311         }
33312     };
33313
33314     // private
33315     var updateButtons = function(b){
33316         var width = 0;
33317         if(!b){
33318             buttons["ok"].hide();
33319             buttons["cancel"].hide();
33320             buttons["yes"].hide();
33321             buttons["no"].hide();
33322             dlg.footer.dom.style.display = 'none';
33323             return width;
33324         }
33325         dlg.footer.dom.style.display = '';
33326         for(var k in buttons){
33327             if(typeof buttons[k] != "function"){
33328                 if(b[k]){
33329                     buttons[k].show();
33330                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
33331                     width += buttons[k].el.getWidth()+15;
33332                 }else{
33333                     buttons[k].hide();
33334                 }
33335             }
33336         }
33337         return width;
33338     };
33339
33340     // private
33341     var handleEsc = function(d, k, e){
33342         if(opt && opt.closable !== false){
33343             dlg.hide();
33344         }
33345         if(e){
33346             e.stopEvent();
33347         }
33348     };
33349
33350     return {
33351         /**
33352          * Returns a reference to the underlying {@link Roo.BasicDialog} element
33353          * @return {Roo.BasicDialog} The BasicDialog element
33354          */
33355         getDialog : function(){
33356            if(!dlg){
33357                 dlg = new Roo.BasicDialog("x-msg-box", {
33358                     autoCreate : true,
33359                     shadow: true,
33360                     draggable: true,
33361                     resizable:false,
33362                     constraintoviewport:false,
33363                     fixedcenter:true,
33364                     collapsible : false,
33365                     shim:true,
33366                     modal: true,
33367                     width:400, height:100,
33368                     buttonAlign:"center",
33369                     closeClick : function(){
33370                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
33371                             handleButton("no");
33372                         }else{
33373                             handleButton("cancel");
33374                         }
33375                     }
33376                 });
33377                 dlg.on("hide", handleHide);
33378                 mask = dlg.mask;
33379                 dlg.addKeyListener(27, handleEsc);
33380                 buttons = {};
33381                 var bt = this.buttonText;
33382                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
33383                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
33384                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
33385                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
33386                 bodyEl = dlg.body.createChild({
33387
33388                     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>'
33389                 });
33390                 msgEl = bodyEl.dom.firstChild;
33391                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
33392                 textboxEl.enableDisplayMode();
33393                 textboxEl.addKeyListener([10,13], function(){
33394                     if(dlg.isVisible() && opt && opt.buttons){
33395                         if(opt.buttons.ok){
33396                             handleButton("ok");
33397                         }else if(opt.buttons.yes){
33398                             handleButton("yes");
33399                         }
33400                     }
33401                 });
33402                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
33403                 textareaEl.enableDisplayMode();
33404                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
33405                 progressEl.enableDisplayMode();
33406                 var pf = progressEl.dom.firstChild;
33407                 if (pf) {
33408                     pp = Roo.get(pf.firstChild);
33409                     pp.setHeight(pf.offsetHeight);
33410                 }
33411                 
33412             }
33413             return dlg;
33414         },
33415
33416         /**
33417          * Updates the message box body text
33418          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
33419          * the XHTML-compliant non-breaking space character '&amp;#160;')
33420          * @return {Roo.MessageBox} This message box
33421          */
33422         updateText : function(text){
33423             if(!dlg.isVisible() && !opt.width){
33424                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
33425             }
33426             msgEl.innerHTML = text || '&#160;';
33427       
33428             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
33429             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
33430             var w = Math.max(
33431                     Math.min(opt.width || cw , this.maxWidth), 
33432                     Math.max(opt.minWidth || this.minWidth, bwidth)
33433             );
33434             if(opt.prompt){
33435                 activeTextEl.setWidth(w);
33436             }
33437             if(dlg.isVisible()){
33438                 dlg.fixedcenter = false;
33439             }
33440             // to big, make it scroll. = But as usual stupid IE does not support
33441             // !important..
33442             
33443             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
33444                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
33445                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
33446             } else {
33447                 bodyEl.dom.style.height = '';
33448                 bodyEl.dom.style.overflowY = '';
33449             }
33450             if (cw > w) {
33451                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
33452             } else {
33453                 bodyEl.dom.style.overflowX = '';
33454             }
33455             
33456             dlg.setContentSize(w, bodyEl.getHeight());
33457             if(dlg.isVisible()){
33458                 dlg.fixedcenter = true;
33459             }
33460             return this;
33461         },
33462
33463         /**
33464          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
33465          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
33466          * @param {Number} value Any number between 0 and 1 (e.g., .5)
33467          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
33468          * @return {Roo.MessageBox} This message box
33469          */
33470         updateProgress : function(value, text){
33471             if(text){
33472                 this.updateText(text);
33473             }
33474             if (pp) { // weird bug on my firefox - for some reason this is not defined
33475                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
33476             }
33477             return this;
33478         },        
33479
33480         /**
33481          * Returns true if the message box is currently displayed
33482          * @return {Boolean} True if the message box is visible, else false
33483          */
33484         isVisible : function(){
33485             return dlg && dlg.isVisible();  
33486         },
33487
33488         /**
33489          * Hides the message box if it is displayed
33490          */
33491         hide : function(){
33492             if(this.isVisible()){
33493                 dlg.hide();
33494             }  
33495         },
33496
33497         /**
33498          * Displays a new message box, or reinitializes an existing message box, based on the config options
33499          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
33500          * The following config object properties are supported:
33501          * <pre>
33502 Property    Type             Description
33503 ----------  ---------------  ------------------------------------------------------------------------------------
33504 animEl            String/Element   An id or Element from which the message box should animate as it opens and
33505                                    closes (defaults to undefined)
33506 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
33507                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
33508 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
33509                                    progress and wait dialogs will ignore this property and always hide the
33510                                    close button as they can only be closed programmatically.
33511 cls               String           A custom CSS class to apply to the message box element
33512 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
33513                                    displayed (defaults to 75)
33514 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
33515                                    function will be btn (the name of the button that was clicked, if applicable,
33516                                    e.g. "ok"), and text (the value of the active text field, if applicable).
33517                                    Progress and wait dialogs will ignore this option since they do not respond to
33518                                    user actions and can only be closed programmatically, so any required function
33519                                    should be called by the same code after it closes the dialog.
33520 icon              String           A CSS class that provides a background image to be used as an icon for
33521                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
33522 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
33523 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
33524 modal             Boolean          False to allow user interaction with the page while the message box is
33525                                    displayed (defaults to true)
33526 msg               String           A string that will replace the existing message box body text (defaults
33527                                    to the XHTML-compliant non-breaking space character '&#160;')
33528 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
33529 progress          Boolean          True to display a progress bar (defaults to false)
33530 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
33531 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
33532 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
33533 title             String           The title text
33534 value             String           The string value to set into the active textbox element if displayed
33535 wait              Boolean          True to display a progress bar (defaults to false)
33536 width             Number           The width of the dialog in pixels
33537 </pre>
33538          *
33539          * Example usage:
33540          * <pre><code>
33541 Roo.Msg.show({
33542    title: 'Address',
33543    msg: 'Please enter your address:',
33544    width: 300,
33545    buttons: Roo.MessageBox.OKCANCEL,
33546    multiline: true,
33547    fn: saveAddress,
33548    animEl: 'addAddressBtn'
33549 });
33550 </code></pre>
33551          * @param {Object} config Configuration options
33552          * @return {Roo.MessageBox} This message box
33553          */
33554         show : function(options)
33555         {
33556             
33557             // this causes nightmares if you show one dialog after another
33558             // especially on callbacks..
33559              
33560             if(this.isVisible()){
33561                 
33562                 this.hide();
33563                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
33564                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
33565                 Roo.log("New Dialog Message:" +  options.msg )
33566                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
33567                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
33568                 
33569             }
33570             var d = this.getDialog();
33571             opt = options;
33572             d.setTitle(opt.title || "&#160;");
33573             d.close.setDisplayed(opt.closable !== false);
33574             activeTextEl = textboxEl;
33575             opt.prompt = opt.prompt || (opt.multiline ? true : false);
33576             if(opt.prompt){
33577                 if(opt.multiline){
33578                     textboxEl.hide();
33579                     textareaEl.show();
33580                     textareaEl.setHeight(typeof opt.multiline == "number" ?
33581                         opt.multiline : this.defaultTextHeight);
33582                     activeTextEl = textareaEl;
33583                 }else{
33584                     textboxEl.show();
33585                     textareaEl.hide();
33586                 }
33587             }else{
33588                 textboxEl.hide();
33589                 textareaEl.hide();
33590             }
33591             progressEl.setDisplayed(opt.progress === true);
33592             this.updateProgress(0);
33593             activeTextEl.dom.value = opt.value || "";
33594             if(opt.prompt){
33595                 dlg.setDefaultButton(activeTextEl);
33596             }else{
33597                 var bs = opt.buttons;
33598                 var db = null;
33599                 if(bs && bs.ok){
33600                     db = buttons["ok"];
33601                 }else if(bs && bs.yes){
33602                     db = buttons["yes"];
33603                 }
33604                 dlg.setDefaultButton(db);
33605             }
33606             bwidth = updateButtons(opt.buttons);
33607             this.updateText(opt.msg);
33608             if(opt.cls){
33609                 d.el.addClass(opt.cls);
33610             }
33611             d.proxyDrag = opt.proxyDrag === true;
33612             d.modal = opt.modal !== false;
33613             d.mask = opt.modal !== false ? mask : false;
33614             if(!d.isVisible()){
33615                 // force it to the end of the z-index stack so it gets a cursor in FF
33616                 document.body.appendChild(dlg.el.dom);
33617                 d.animateTarget = null;
33618                 d.show(options.animEl);
33619             }
33620             return this;
33621         },
33622
33623         /**
33624          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
33625          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
33626          * and closing the message box when the process is complete.
33627          * @param {String} title The title bar text
33628          * @param {String} msg The message box body text
33629          * @return {Roo.MessageBox} This message box
33630          */
33631         progress : function(title, msg){
33632             this.show({
33633                 title : title,
33634                 msg : msg,
33635                 buttons: false,
33636                 progress:true,
33637                 closable:false,
33638                 minWidth: this.minProgressWidth,
33639                 modal : true
33640             });
33641             return this;
33642         },
33643
33644         /**
33645          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
33646          * If a callback function is passed it will be called after the user clicks the button, and the
33647          * id of the button that was clicked will be passed as the only parameter to the callback
33648          * (could also be the top-right close button).
33649          * @param {String} title The title bar text
33650          * @param {String} msg The message box body text
33651          * @param {Function} fn (optional) The callback function invoked after the message box is closed
33652          * @param {Object} scope (optional) The scope of the callback function
33653          * @return {Roo.MessageBox} This message box
33654          */
33655         alert : function(title, msg, fn, scope){
33656             this.show({
33657                 title : title,
33658                 msg : msg,
33659                 buttons: this.OK,
33660                 fn: fn,
33661                 scope : scope,
33662                 modal : true
33663             });
33664             return this;
33665         },
33666
33667         /**
33668          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
33669          * interaction while waiting for a long-running process to complete that does not have defined intervals.
33670          * You are responsible for closing the message box when the process is complete.
33671          * @param {String} msg The message box body text
33672          * @param {String} title (optional) The title bar text
33673          * @return {Roo.MessageBox} This message box
33674          */
33675         wait : function(msg, title){
33676             this.show({
33677                 title : title,
33678                 msg : msg,
33679                 buttons: false,
33680                 closable:false,
33681                 progress:true,
33682                 modal:true,
33683                 width:300,
33684                 wait:true
33685             });
33686             waitTimer = Roo.TaskMgr.start({
33687                 run: function(i){
33688                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
33689                 },
33690                 interval: 1000
33691             });
33692             return this;
33693         },
33694
33695         /**
33696          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
33697          * If a callback function is passed it will be called after the user clicks either button, and the id of the
33698          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
33699          * @param {String} title The title bar text
33700          * @param {String} msg The message box body text
33701          * @param {Function} fn (optional) The callback function invoked after the message box is closed
33702          * @param {Object} scope (optional) The scope of the callback function
33703          * @return {Roo.MessageBox} This message box
33704          */
33705         confirm : function(title, msg, fn, scope){
33706             this.show({
33707                 title : title,
33708                 msg : msg,
33709                 buttons: this.YESNO,
33710                 fn: fn,
33711                 scope : scope,
33712                 modal : true
33713             });
33714             return this;
33715         },
33716
33717         /**
33718          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
33719          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
33720          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
33721          * (could also be the top-right close button) and the text that was entered will be passed as the two
33722          * parameters to the callback.
33723          * @param {String} title The title bar text
33724          * @param {String} msg The message box body text
33725          * @param {Function} fn (optional) The callback function invoked after the message box is closed
33726          * @param {Object} scope (optional) The scope of the callback function
33727          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
33728          * property, or the height in pixels to create the textbox (defaults to false / single-line)
33729          * @return {Roo.MessageBox} This message box
33730          */
33731         prompt : function(title, msg, fn, scope, multiline){
33732             this.show({
33733                 title : title,
33734                 msg : msg,
33735                 buttons: this.OKCANCEL,
33736                 fn: fn,
33737                 minWidth:250,
33738                 scope : scope,
33739                 prompt:true,
33740                 multiline: multiline,
33741                 modal : true
33742             });
33743             return this;
33744         },
33745
33746         /**
33747          * Button config that displays a single OK button
33748          * @type Object
33749          */
33750         OK : {ok:true},
33751         /**
33752          * Button config that displays Yes and No buttons
33753          * @type Object
33754          */
33755         YESNO : {yes:true, no:true},
33756         /**
33757          * Button config that displays OK and Cancel buttons
33758          * @type Object
33759          */
33760         OKCANCEL : {ok:true, cancel:true},
33761         /**
33762          * Button config that displays Yes, No and Cancel buttons
33763          * @type Object
33764          */
33765         YESNOCANCEL : {yes:true, no:true, cancel:true},
33766
33767         /**
33768          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
33769          * @type Number
33770          */
33771         defaultTextHeight : 75,
33772         /**
33773          * The maximum width in pixels of the message box (defaults to 600)
33774          * @type Number
33775          */
33776         maxWidth : 600,
33777         /**
33778          * The minimum width in pixels of the message box (defaults to 100)
33779          * @type Number
33780          */
33781         minWidth : 100,
33782         /**
33783          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
33784          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
33785          * @type Number
33786          */
33787         minProgressWidth : 250,
33788         /**
33789          * An object containing the default button text strings that can be overriden for localized language support.
33790          * Supported properties are: ok, cancel, yes and no.
33791          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
33792          * @type Object
33793          */
33794         buttonText : {
33795             ok : "OK",
33796             cancel : "Cancel",
33797             yes : "Yes",
33798             no : "No"
33799         }
33800     };
33801 }();
33802
33803 /**
33804  * Shorthand for {@link Roo.MessageBox}
33805  */
33806 Roo.Msg = Roo.MessageBox;/*
33807  * Based on:
33808  * Ext JS Library 1.1.1
33809  * Copyright(c) 2006-2007, Ext JS, LLC.
33810  *
33811  * Originally Released Under LGPL - original licence link has changed is not relivant.
33812  *
33813  * Fork - LGPL
33814  * <script type="text/javascript">
33815  */
33816 /**
33817  * @class Roo.QuickTips
33818  * Provides attractive and customizable tooltips for any element.
33819  * @singleton
33820  */
33821 Roo.QuickTips = function(){
33822     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
33823     var ce, bd, xy, dd;
33824     var visible = false, disabled = true, inited = false;
33825     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
33826     
33827     var onOver = function(e){
33828         if(disabled){
33829             return;
33830         }
33831         var t = e.getTarget();
33832         if(!t || t.nodeType !== 1 || t == document || t == document.body){
33833             return;
33834         }
33835         if(ce && t == ce.el){
33836             clearTimeout(hideProc);
33837             return;
33838         }
33839         if(t && tagEls[t.id]){
33840             tagEls[t.id].el = t;
33841             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
33842             return;
33843         }
33844         var ttp, et = Roo.fly(t);
33845         var ns = cfg.namespace;
33846         if(tm.interceptTitles && t.title){
33847             ttp = t.title;
33848             t.qtip = ttp;
33849             t.removeAttribute("title");
33850             e.preventDefault();
33851         }else{
33852             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
33853         }
33854         if(ttp){
33855             showProc = show.defer(tm.showDelay, tm, [{
33856                 el: t, 
33857                 text: ttp.replace(/\\n/g,'<br/>'),
33858                 width: et.getAttributeNS(ns, cfg.width),
33859                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
33860                 title: et.getAttributeNS(ns, cfg.title),
33861                     cls: et.getAttributeNS(ns, cfg.cls)
33862             }]);
33863         }
33864     };
33865     
33866     var onOut = function(e){
33867         clearTimeout(showProc);
33868         var t = e.getTarget();
33869         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
33870             hideProc = setTimeout(hide, tm.hideDelay);
33871         }
33872     };
33873     
33874     var onMove = function(e){
33875         if(disabled){
33876             return;
33877         }
33878         xy = e.getXY();
33879         xy[1] += 18;
33880         if(tm.trackMouse && ce){
33881             el.setXY(xy);
33882         }
33883     };
33884     
33885     var onDown = function(e){
33886         clearTimeout(showProc);
33887         clearTimeout(hideProc);
33888         if(!e.within(el)){
33889             if(tm.hideOnClick){
33890                 hide();
33891                 tm.disable();
33892                 tm.enable.defer(100, tm);
33893             }
33894         }
33895     };
33896     
33897     var getPad = function(){
33898         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
33899     };
33900
33901     var show = function(o){
33902         if(disabled){
33903             return;
33904         }
33905         clearTimeout(dismissProc);
33906         ce = o;
33907         if(removeCls){ // in case manually hidden
33908             el.removeClass(removeCls);
33909             removeCls = null;
33910         }
33911         if(ce.cls){
33912             el.addClass(ce.cls);
33913             removeCls = ce.cls;
33914         }
33915         if(ce.title){
33916             tipTitle.update(ce.title);
33917             tipTitle.show();
33918         }else{
33919             tipTitle.update('');
33920             tipTitle.hide();
33921         }
33922         el.dom.style.width  = tm.maxWidth+'px';
33923         //tipBody.dom.style.width = '';
33924         tipBodyText.update(o.text);
33925         var p = getPad(), w = ce.width;
33926         if(!w){
33927             var td = tipBodyText.dom;
33928             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
33929             if(aw > tm.maxWidth){
33930                 w = tm.maxWidth;
33931             }else if(aw < tm.minWidth){
33932                 w = tm.minWidth;
33933             }else{
33934                 w = aw;
33935             }
33936         }
33937         //tipBody.setWidth(w);
33938         el.setWidth(parseInt(w, 10) + p);
33939         if(ce.autoHide === false){
33940             close.setDisplayed(true);
33941             if(dd){
33942                 dd.unlock();
33943             }
33944         }else{
33945             close.setDisplayed(false);
33946             if(dd){
33947                 dd.lock();
33948             }
33949         }
33950         if(xy){
33951             el.avoidY = xy[1]-18;
33952             el.setXY(xy);
33953         }
33954         if(tm.animate){
33955             el.setOpacity(.1);
33956             el.setStyle("visibility", "visible");
33957             el.fadeIn({callback: afterShow});
33958         }else{
33959             afterShow();
33960         }
33961     };
33962     
33963     var afterShow = function(){
33964         if(ce){
33965             el.show();
33966             esc.enable();
33967             if(tm.autoDismiss && ce.autoHide !== false){
33968                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
33969             }
33970         }
33971     };
33972     
33973     var hide = function(noanim){
33974         clearTimeout(dismissProc);
33975         clearTimeout(hideProc);
33976         ce = null;
33977         if(el.isVisible()){
33978             esc.disable();
33979             if(noanim !== true && tm.animate){
33980                 el.fadeOut({callback: afterHide});
33981             }else{
33982                 afterHide();
33983             } 
33984         }
33985     };
33986     
33987     var afterHide = function(){
33988         el.hide();
33989         if(removeCls){
33990             el.removeClass(removeCls);
33991             removeCls = null;
33992         }
33993     };
33994     
33995     return {
33996         /**
33997         * @cfg {Number} minWidth
33998         * The minimum width of the quick tip (defaults to 40)
33999         */
34000        minWidth : 40,
34001         /**
34002         * @cfg {Number} maxWidth
34003         * The maximum width of the quick tip (defaults to 300)
34004         */
34005        maxWidth : 300,
34006         /**
34007         * @cfg {Boolean} interceptTitles
34008         * True to automatically use the element's DOM title value if available (defaults to false)
34009         */
34010        interceptTitles : false,
34011         /**
34012         * @cfg {Boolean} trackMouse
34013         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
34014         */
34015        trackMouse : false,
34016         /**
34017         * @cfg {Boolean} hideOnClick
34018         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
34019         */
34020        hideOnClick : true,
34021         /**
34022         * @cfg {Number} showDelay
34023         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
34024         */
34025        showDelay : 500,
34026         /**
34027         * @cfg {Number} hideDelay
34028         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
34029         */
34030        hideDelay : 200,
34031         /**
34032         * @cfg {Boolean} autoHide
34033         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
34034         * Used in conjunction with hideDelay.
34035         */
34036        autoHide : true,
34037         /**
34038         * @cfg {Boolean}
34039         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
34040         * (defaults to true).  Used in conjunction with autoDismissDelay.
34041         */
34042        autoDismiss : true,
34043         /**
34044         * @cfg {Number}
34045         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
34046         */
34047        autoDismissDelay : 5000,
34048        /**
34049         * @cfg {Boolean} animate
34050         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
34051         */
34052        animate : false,
34053
34054        /**
34055         * @cfg {String} title
34056         * Title text to display (defaults to '').  This can be any valid HTML markup.
34057         */
34058         title: '',
34059        /**
34060         * @cfg {String} text
34061         * Body text to display (defaults to '').  This can be any valid HTML markup.
34062         */
34063         text : '',
34064        /**
34065         * @cfg {String} cls
34066         * A CSS class to apply to the base quick tip element (defaults to '').
34067         */
34068         cls : '',
34069        /**
34070         * @cfg {Number} width
34071         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
34072         * minWidth or maxWidth.
34073         */
34074         width : null,
34075
34076     /**
34077      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
34078      * or display QuickTips in a page.
34079      */
34080        init : function(){
34081           tm = Roo.QuickTips;
34082           cfg = tm.tagConfig;
34083           if(!inited){
34084               if(!Roo.isReady){ // allow calling of init() before onReady
34085                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
34086                   return;
34087               }
34088               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
34089               el.fxDefaults = {stopFx: true};
34090               // maximum custom styling
34091               //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>');
34092               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>');              
34093               tipTitle = el.child('h3');
34094               tipTitle.enableDisplayMode("block");
34095               tipBody = el.child('div.x-tip-bd');
34096               tipBodyText = el.child('div.x-tip-bd-inner');
34097               //bdLeft = el.child('div.x-tip-bd-left');
34098               //bdRight = el.child('div.x-tip-bd-right');
34099               close = el.child('div.x-tip-close');
34100               close.enableDisplayMode("block");
34101               close.on("click", hide);
34102               var d = Roo.get(document);
34103               d.on("mousedown", onDown);
34104               d.on("mouseover", onOver);
34105               d.on("mouseout", onOut);
34106               d.on("mousemove", onMove);
34107               esc = d.addKeyListener(27, hide);
34108               esc.disable();
34109               if(Roo.dd.DD){
34110                   dd = el.initDD("default", null, {
34111                       onDrag : function(){
34112                           el.sync();  
34113                       }
34114                   });
34115                   dd.setHandleElId(tipTitle.id);
34116                   dd.lock();
34117               }
34118               inited = true;
34119           }
34120           this.enable(); 
34121        },
34122
34123     /**
34124      * Configures a new quick tip instance and assigns it to a target element.  The following config options
34125      * are supported:
34126      * <pre>
34127 Property    Type                   Description
34128 ----------  ---------------------  ------------------------------------------------------------------------
34129 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
34130      * </ul>
34131      * @param {Object} config The config object
34132      */
34133        register : function(config){
34134            var cs = config instanceof Array ? config : arguments;
34135            for(var i = 0, len = cs.length; i < len; i++) {
34136                var c = cs[i];
34137                var target = c.target;
34138                if(target){
34139                    if(target instanceof Array){
34140                        for(var j = 0, jlen = target.length; j < jlen; j++){
34141                            tagEls[target[j]] = c;
34142                        }
34143                    }else{
34144                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
34145                    }
34146                }
34147            }
34148        },
34149
34150     /**
34151      * Removes this quick tip from its element and destroys it.
34152      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
34153      */
34154        unregister : function(el){
34155            delete tagEls[Roo.id(el)];
34156        },
34157
34158     /**
34159      * Enable this quick tip.
34160      */
34161        enable : function(){
34162            if(inited && disabled){
34163                locks.pop();
34164                if(locks.length < 1){
34165                    disabled = false;
34166                }
34167            }
34168        },
34169
34170     /**
34171      * Disable this quick tip.
34172      */
34173        disable : function(){
34174           disabled = true;
34175           clearTimeout(showProc);
34176           clearTimeout(hideProc);
34177           clearTimeout(dismissProc);
34178           if(ce){
34179               hide(true);
34180           }
34181           locks.push(1);
34182        },
34183
34184     /**
34185      * Returns true if the quick tip is enabled, else false.
34186      */
34187        isEnabled : function(){
34188             return !disabled;
34189        },
34190
34191         // private
34192        tagConfig : {
34193            namespace : "roo", // was ext?? this may break..
34194            alt_namespace : "ext",
34195            attribute : "qtip",
34196            width : "width",
34197            target : "target",
34198            title : "qtitle",
34199            hide : "hide",
34200            cls : "qclass"
34201        }
34202    };
34203 }();
34204
34205 // backwards compat
34206 Roo.QuickTips.tips = Roo.QuickTips.register;/*
34207  * Based on:
34208  * Ext JS Library 1.1.1
34209  * Copyright(c) 2006-2007, Ext JS, LLC.
34210  *
34211  * Originally Released Under LGPL - original licence link has changed is not relivant.
34212  *
34213  * Fork - LGPL
34214  * <script type="text/javascript">
34215  */
34216  
34217
34218 /**
34219  * @class Roo.tree.TreePanel
34220  * @extends Roo.data.Tree
34221
34222  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
34223  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
34224  * @cfg {Boolean} enableDD true to enable drag and drop
34225  * @cfg {Boolean} enableDrag true to enable just drag
34226  * @cfg {Boolean} enableDrop true to enable just drop
34227  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
34228  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
34229  * @cfg {String} ddGroup The DD group this TreePanel belongs to
34230  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
34231  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
34232  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
34233  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
34234  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
34235  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
34236  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
34237  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
34238  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
34239  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
34240  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
34241  * @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>
34242  * @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>
34243  * 
34244  * @constructor
34245  * @param {String/HTMLElement/Element} el The container element
34246  * @param {Object} config
34247  */
34248 Roo.tree.TreePanel = function(el, config){
34249     var root = false;
34250     var loader = false;
34251     if (config.root) {
34252         root = config.root;
34253         delete config.root;
34254     }
34255     if (config.loader) {
34256         loader = config.loader;
34257         delete config.loader;
34258     }
34259     
34260     Roo.apply(this, config);
34261     Roo.tree.TreePanel.superclass.constructor.call(this);
34262     this.el = Roo.get(el);
34263     this.el.addClass('x-tree');
34264     //console.log(root);
34265     if (root) {
34266         this.setRootNode( Roo.factory(root, Roo.tree));
34267     }
34268     if (loader) {
34269         this.loader = Roo.factory(loader, Roo.tree);
34270     }
34271    /**
34272     * Read-only. The id of the container element becomes this TreePanel's id.
34273     */
34274     this.id = this.el.id;
34275     this.addEvents({
34276         /**
34277         * @event beforeload
34278         * Fires before a node is loaded, return false to cancel
34279         * @param {Node} node The node being loaded
34280         */
34281         "beforeload" : true,
34282         /**
34283         * @event load
34284         * Fires when a node is loaded
34285         * @param {Node} node The node that was loaded
34286         */
34287         "load" : true,
34288         /**
34289         * @event textchange
34290         * Fires when the text for a node is changed
34291         * @param {Node} node The node
34292         * @param {String} text The new text
34293         * @param {String} oldText The old text
34294         */
34295         "textchange" : true,
34296         /**
34297         * @event beforeexpand
34298         * Fires before a node is expanded, return false to cancel.
34299         * @param {Node} node The node
34300         * @param {Boolean} deep
34301         * @param {Boolean} anim
34302         */
34303         "beforeexpand" : true,
34304         /**
34305         * @event beforecollapse
34306         * Fires before a node is collapsed, return false to cancel.
34307         * @param {Node} node The node
34308         * @param {Boolean} deep
34309         * @param {Boolean} anim
34310         */
34311         "beforecollapse" : true,
34312         /**
34313         * @event expand
34314         * Fires when a node is expanded
34315         * @param {Node} node The node
34316         */
34317         "expand" : true,
34318         /**
34319         * @event disabledchange
34320         * Fires when the disabled status of a node changes
34321         * @param {Node} node The node
34322         * @param {Boolean} disabled
34323         */
34324         "disabledchange" : true,
34325         /**
34326         * @event collapse
34327         * Fires when a node is collapsed
34328         * @param {Node} node The node
34329         */
34330         "collapse" : true,
34331         /**
34332         * @event beforeclick
34333         * Fires before click processing on a node. Return false to cancel the default action.
34334         * @param {Node} node The node
34335         * @param {Roo.EventObject} e The event object
34336         */
34337         "beforeclick":true,
34338         /**
34339         * @event checkchange
34340         * Fires when a node with a checkbox's checked property changes
34341         * @param {Node} this This node
34342         * @param {Boolean} checked
34343         */
34344         "checkchange":true,
34345         /**
34346         * @event click
34347         * Fires when a node is clicked
34348         * @param {Node} node The node
34349         * @param {Roo.EventObject} e The event object
34350         */
34351         "click":true,
34352         /**
34353         * @event dblclick
34354         * Fires when a node is double clicked
34355         * @param {Node} node The node
34356         * @param {Roo.EventObject} e The event object
34357         */
34358         "dblclick":true,
34359         /**
34360         * @event contextmenu
34361         * Fires when a node is right clicked
34362         * @param {Node} node The node
34363         * @param {Roo.EventObject} e The event object
34364         */
34365         "contextmenu":true,
34366         /**
34367         * @event beforechildrenrendered
34368         * Fires right before the child nodes for a node are rendered
34369         * @param {Node} node The node
34370         */
34371         "beforechildrenrendered":true,
34372         /**
34373         * @event startdrag
34374         * Fires when a node starts being dragged
34375         * @param {Roo.tree.TreePanel} this
34376         * @param {Roo.tree.TreeNode} node
34377         * @param {event} e The raw browser event
34378         */ 
34379        "startdrag" : true,
34380        /**
34381         * @event enddrag
34382         * Fires when a drag operation is complete
34383         * @param {Roo.tree.TreePanel} this
34384         * @param {Roo.tree.TreeNode} node
34385         * @param {event} e The raw browser event
34386         */
34387        "enddrag" : true,
34388        /**
34389         * @event dragdrop
34390         * Fires when a dragged node is dropped on a valid DD target
34391         * @param {Roo.tree.TreePanel} this
34392         * @param {Roo.tree.TreeNode} node
34393         * @param {DD} dd The dd it was dropped on
34394         * @param {event} e The raw browser event
34395         */
34396        "dragdrop" : true,
34397        /**
34398         * @event beforenodedrop
34399         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
34400         * passed to handlers has the following properties:<br />
34401         * <ul style="padding:5px;padding-left:16px;">
34402         * <li>tree - The TreePanel</li>
34403         * <li>target - The node being targeted for the drop</li>
34404         * <li>data - The drag data from the drag source</li>
34405         * <li>point - The point of the drop - append, above or below</li>
34406         * <li>source - The drag source</li>
34407         * <li>rawEvent - Raw mouse event</li>
34408         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
34409         * to be inserted by setting them on this object.</li>
34410         * <li>cancel - Set this to true to cancel the drop.</li>
34411         * </ul>
34412         * @param {Object} dropEvent
34413         */
34414        "beforenodedrop" : true,
34415        /**
34416         * @event nodedrop
34417         * Fires after a DD object is dropped on a node in this tree. The dropEvent
34418         * passed to handlers has the following properties:<br />
34419         * <ul style="padding:5px;padding-left:16px;">
34420         * <li>tree - The TreePanel</li>
34421         * <li>target - The node being targeted for the drop</li>
34422         * <li>data - The drag data from the drag source</li>
34423         * <li>point - The point of the drop - append, above or below</li>
34424         * <li>source - The drag source</li>
34425         * <li>rawEvent - Raw mouse event</li>
34426         * <li>dropNode - Dropped node(s).</li>
34427         * </ul>
34428         * @param {Object} dropEvent
34429         */
34430        "nodedrop" : true,
34431         /**
34432         * @event nodedragover
34433         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
34434         * passed to handlers has the following properties:<br />
34435         * <ul style="padding:5px;padding-left:16px;">
34436         * <li>tree - The TreePanel</li>
34437         * <li>target - The node being targeted for the drop</li>
34438         * <li>data - The drag data from the drag source</li>
34439         * <li>point - The point of the drop - append, above or below</li>
34440         * <li>source - The drag source</li>
34441         * <li>rawEvent - Raw mouse event</li>
34442         * <li>dropNode - Drop node(s) provided by the source.</li>
34443         * <li>cancel - Set this to true to signal drop not allowed.</li>
34444         * </ul>
34445         * @param {Object} dragOverEvent
34446         */
34447        "nodedragover" : true,
34448        /**
34449         * @event appendnode
34450         * Fires when append node to the tree
34451         * @param {Roo.tree.TreePanel} this
34452         * @param {Roo.tree.TreeNode} node
34453         * @param {Number} index The index of the newly appended node
34454         */
34455        "appendnode" : true
34456         
34457     });
34458     if(this.singleExpand){
34459        this.on("beforeexpand", this.restrictExpand, this);
34460     }
34461     if (this.editor) {
34462         this.editor.tree = this;
34463         this.editor = Roo.factory(this.editor, Roo.tree);
34464     }
34465     
34466     if (this.selModel) {
34467         this.selModel = Roo.factory(this.selModel, Roo.tree);
34468     }
34469    
34470 };
34471 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
34472     rootVisible : true,
34473     animate: Roo.enableFx,
34474     lines : true,
34475     enableDD : false,
34476     hlDrop : Roo.enableFx,
34477   
34478     renderer: false,
34479     
34480     rendererTip: false,
34481     // private
34482     restrictExpand : function(node){
34483         var p = node.parentNode;
34484         if(p){
34485             if(p.expandedChild && p.expandedChild.parentNode == p){
34486                 p.expandedChild.collapse();
34487             }
34488             p.expandedChild = node;
34489         }
34490     },
34491
34492     // private override
34493     setRootNode : function(node){
34494         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
34495         if(!this.rootVisible){
34496             node.ui = new Roo.tree.RootTreeNodeUI(node);
34497         }
34498         return node;
34499     },
34500
34501     /**
34502      * Returns the container element for this TreePanel
34503      */
34504     getEl : function(){
34505         return this.el;
34506     },
34507
34508     /**
34509      * Returns the default TreeLoader for this TreePanel
34510      */
34511     getLoader : function(){
34512         return this.loader;
34513     },
34514
34515     /**
34516      * Expand all nodes
34517      */
34518     expandAll : function(){
34519         this.root.expand(true);
34520     },
34521
34522     /**
34523      * Collapse all nodes
34524      */
34525     collapseAll : function(){
34526         this.root.collapse(true);
34527     },
34528
34529     /**
34530      * Returns the selection model used by this TreePanel
34531      */
34532     getSelectionModel : function(){
34533         if(!this.selModel){
34534             this.selModel = new Roo.tree.DefaultSelectionModel();
34535         }
34536         return this.selModel;
34537     },
34538
34539     /**
34540      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
34541      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
34542      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
34543      * @return {Array}
34544      */
34545     getChecked : function(a, startNode){
34546         startNode = startNode || this.root;
34547         var r = [];
34548         var f = function(){
34549             if(this.attributes.checked){
34550                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
34551             }
34552         }
34553         startNode.cascade(f);
34554         return r;
34555     },
34556
34557     /**
34558      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
34559      * @param {String} path
34560      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
34561      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
34562      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
34563      */
34564     expandPath : function(path, attr, callback){
34565         attr = attr || "id";
34566         var keys = path.split(this.pathSeparator);
34567         var curNode = this.root;
34568         if(curNode.attributes[attr] != keys[1]){ // invalid root
34569             if(callback){
34570                 callback(false, null);
34571             }
34572             return;
34573         }
34574         var index = 1;
34575         var f = function(){
34576             if(++index == keys.length){
34577                 if(callback){
34578                     callback(true, curNode);
34579                 }
34580                 return;
34581             }
34582             var c = curNode.findChild(attr, keys[index]);
34583             if(!c){
34584                 if(callback){
34585                     callback(false, curNode);
34586                 }
34587                 return;
34588             }
34589             curNode = c;
34590             c.expand(false, false, f);
34591         };
34592         curNode.expand(false, false, f);
34593     },
34594
34595     /**
34596      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
34597      * @param {String} path
34598      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
34599      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
34600      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
34601      */
34602     selectPath : function(path, attr, callback){
34603         attr = attr || "id";
34604         var keys = path.split(this.pathSeparator);
34605         var v = keys.pop();
34606         if(keys.length > 0){
34607             var f = function(success, node){
34608                 if(success && node){
34609                     var n = node.findChild(attr, v);
34610                     if(n){
34611                         n.select();
34612                         if(callback){
34613                             callback(true, n);
34614                         }
34615                     }else if(callback){
34616                         callback(false, n);
34617                     }
34618                 }else{
34619                     if(callback){
34620                         callback(false, n);
34621                     }
34622                 }
34623             };
34624             this.expandPath(keys.join(this.pathSeparator), attr, f);
34625         }else{
34626             this.root.select();
34627             if(callback){
34628                 callback(true, this.root);
34629             }
34630         }
34631     },
34632
34633     getTreeEl : function(){
34634         return this.el;
34635     },
34636
34637     /**
34638      * Trigger rendering of this TreePanel
34639      */
34640     render : function(){
34641         if (this.innerCt) {
34642             return this; // stop it rendering more than once!!
34643         }
34644         
34645         this.innerCt = this.el.createChild({tag:"ul",
34646                cls:"x-tree-root-ct " +
34647                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
34648
34649         if(this.containerScroll){
34650             Roo.dd.ScrollManager.register(this.el);
34651         }
34652         if((this.enableDD || this.enableDrop) && !this.dropZone){
34653            /**
34654             * The dropZone used by this tree if drop is enabled
34655             * @type Roo.tree.TreeDropZone
34656             */
34657              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
34658                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
34659            });
34660         }
34661         if((this.enableDD || this.enableDrag) && !this.dragZone){
34662            /**
34663             * The dragZone used by this tree if drag is enabled
34664             * @type Roo.tree.TreeDragZone
34665             */
34666             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
34667                ddGroup: this.ddGroup || "TreeDD",
34668                scroll: this.ddScroll
34669            });
34670         }
34671         this.getSelectionModel().init(this);
34672         if (!this.root) {
34673             Roo.log("ROOT not set in tree");
34674             return this;
34675         }
34676         this.root.render();
34677         if(!this.rootVisible){
34678             this.root.renderChildren();
34679         }
34680         return this;
34681     }
34682 });/*
34683  * Based on:
34684  * Ext JS Library 1.1.1
34685  * Copyright(c) 2006-2007, Ext JS, LLC.
34686  *
34687  * Originally Released Under LGPL - original licence link has changed is not relivant.
34688  *
34689  * Fork - LGPL
34690  * <script type="text/javascript">
34691  */
34692  
34693
34694 /**
34695  * @class Roo.tree.DefaultSelectionModel
34696  * @extends Roo.util.Observable
34697  * The default single selection for a TreePanel.
34698  * @param {Object} cfg Configuration
34699  */
34700 Roo.tree.DefaultSelectionModel = function(cfg){
34701    this.selNode = null;
34702    
34703    
34704    
34705    this.addEvents({
34706        /**
34707         * @event selectionchange
34708         * Fires when the selected node changes
34709         * @param {DefaultSelectionModel} this
34710         * @param {TreeNode} node the new selection
34711         */
34712        "selectionchange" : true,
34713
34714        /**
34715         * @event beforeselect
34716         * Fires before the selected node changes, return false to cancel the change
34717         * @param {DefaultSelectionModel} this
34718         * @param {TreeNode} node the new selection
34719         * @param {TreeNode} node the old selection
34720         */
34721        "beforeselect" : true
34722    });
34723    
34724     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
34725 };
34726
34727 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
34728     init : function(tree){
34729         this.tree = tree;
34730         tree.getTreeEl().on("keydown", this.onKeyDown, this);
34731         tree.on("click", this.onNodeClick, this);
34732     },
34733     
34734     onNodeClick : function(node, e){
34735         if (e.ctrlKey && this.selNode == node)  {
34736             this.unselect(node);
34737             return;
34738         }
34739         this.select(node);
34740     },
34741     
34742     /**
34743      * Select a node.
34744      * @param {TreeNode} node The node to select
34745      * @return {TreeNode} The selected node
34746      */
34747     select : function(node){
34748         var last = this.selNode;
34749         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
34750             if(last){
34751                 last.ui.onSelectedChange(false);
34752             }
34753             this.selNode = node;
34754             node.ui.onSelectedChange(true);
34755             this.fireEvent("selectionchange", this, node, last);
34756         }
34757         return node;
34758     },
34759     
34760     /**
34761      * Deselect a node.
34762      * @param {TreeNode} node The node to unselect
34763      */
34764     unselect : function(node){
34765         if(this.selNode == node){
34766             this.clearSelections();
34767         }    
34768     },
34769     
34770     /**
34771      * Clear all selections
34772      */
34773     clearSelections : function(){
34774         var n = this.selNode;
34775         if(n){
34776             n.ui.onSelectedChange(false);
34777             this.selNode = null;
34778             this.fireEvent("selectionchange", this, null);
34779         }
34780         return n;
34781     },
34782     
34783     /**
34784      * Get the selected node
34785      * @return {TreeNode} The selected node
34786      */
34787     getSelectedNode : function(){
34788         return this.selNode;    
34789     },
34790     
34791     /**
34792      * Returns true if the node is selected
34793      * @param {TreeNode} node The node to check
34794      * @return {Boolean}
34795      */
34796     isSelected : function(node){
34797         return this.selNode == node;  
34798     },
34799
34800     /**
34801      * Selects the node above the selected node in the tree, intelligently walking the nodes
34802      * @return TreeNode The new selection
34803      */
34804     selectPrevious : function(){
34805         var s = this.selNode || this.lastSelNode;
34806         if(!s){
34807             return null;
34808         }
34809         var ps = s.previousSibling;
34810         if(ps){
34811             if(!ps.isExpanded() || ps.childNodes.length < 1){
34812                 return this.select(ps);
34813             } else{
34814                 var lc = ps.lastChild;
34815                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
34816                     lc = lc.lastChild;
34817                 }
34818                 return this.select(lc);
34819             }
34820         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
34821             return this.select(s.parentNode);
34822         }
34823         return null;
34824     },
34825
34826     /**
34827      * Selects the node above the selected node in the tree, intelligently walking the nodes
34828      * @return TreeNode The new selection
34829      */
34830     selectNext : function(){
34831         var s = this.selNode || this.lastSelNode;
34832         if(!s){
34833             return null;
34834         }
34835         if(s.firstChild && s.isExpanded()){
34836              return this.select(s.firstChild);
34837          }else if(s.nextSibling){
34838              return this.select(s.nextSibling);
34839          }else if(s.parentNode){
34840             var newS = null;
34841             s.parentNode.bubble(function(){
34842                 if(this.nextSibling){
34843                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
34844                     return false;
34845                 }
34846             });
34847             return newS;
34848          }
34849         return null;
34850     },
34851
34852     onKeyDown : function(e){
34853         var s = this.selNode || this.lastSelNode;
34854         // undesirable, but required
34855         var sm = this;
34856         if(!s){
34857             return;
34858         }
34859         var k = e.getKey();
34860         switch(k){
34861              case e.DOWN:
34862                  e.stopEvent();
34863                  this.selectNext();
34864              break;
34865              case e.UP:
34866                  e.stopEvent();
34867                  this.selectPrevious();
34868              break;
34869              case e.RIGHT:
34870                  e.preventDefault();
34871                  if(s.hasChildNodes()){
34872                      if(!s.isExpanded()){
34873                          s.expand();
34874                      }else if(s.firstChild){
34875                          this.select(s.firstChild, e);
34876                      }
34877                  }
34878              break;
34879              case e.LEFT:
34880                  e.preventDefault();
34881                  if(s.hasChildNodes() && s.isExpanded()){
34882                      s.collapse();
34883                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
34884                      this.select(s.parentNode, e);
34885                  }
34886              break;
34887         };
34888     }
34889 });
34890
34891 /**
34892  * @class Roo.tree.MultiSelectionModel
34893  * @extends Roo.util.Observable
34894  * Multi selection for a TreePanel.
34895  * @param {Object} cfg Configuration
34896  */
34897 Roo.tree.MultiSelectionModel = function(){
34898    this.selNodes = [];
34899    this.selMap = {};
34900    this.addEvents({
34901        /**
34902         * @event selectionchange
34903         * Fires when the selected nodes change
34904         * @param {MultiSelectionModel} this
34905         * @param {Array} nodes Array of the selected nodes
34906         */
34907        "selectionchange" : true
34908    });
34909    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
34910    
34911 };
34912
34913 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
34914     init : function(tree){
34915         this.tree = tree;
34916         tree.getTreeEl().on("keydown", this.onKeyDown, this);
34917         tree.on("click", this.onNodeClick, this);
34918     },
34919     
34920     onNodeClick : function(node, e){
34921         this.select(node, e, e.ctrlKey);
34922     },
34923     
34924     /**
34925      * Select a node.
34926      * @param {TreeNode} node The node to select
34927      * @param {EventObject} e (optional) An event associated with the selection
34928      * @param {Boolean} keepExisting True to retain existing selections
34929      * @return {TreeNode} The selected node
34930      */
34931     select : function(node, e, keepExisting){
34932         if(keepExisting !== true){
34933             this.clearSelections(true);
34934         }
34935         if(this.isSelected(node)){
34936             this.lastSelNode = node;
34937             return node;
34938         }
34939         this.selNodes.push(node);
34940         this.selMap[node.id] = node;
34941         this.lastSelNode = node;
34942         node.ui.onSelectedChange(true);
34943         this.fireEvent("selectionchange", this, this.selNodes);
34944         return node;
34945     },
34946     
34947     /**
34948      * Deselect a node.
34949      * @param {TreeNode} node The node to unselect
34950      */
34951     unselect : function(node){
34952         if(this.selMap[node.id]){
34953             node.ui.onSelectedChange(false);
34954             var sn = this.selNodes;
34955             var index = -1;
34956             if(sn.indexOf){
34957                 index = sn.indexOf(node);
34958             }else{
34959                 for(var i = 0, len = sn.length; i < len; i++){
34960                     if(sn[i] == node){
34961                         index = i;
34962                         break;
34963                     }
34964                 }
34965             }
34966             if(index != -1){
34967                 this.selNodes.splice(index, 1);
34968             }
34969             delete this.selMap[node.id];
34970             this.fireEvent("selectionchange", this, this.selNodes);
34971         }
34972     },
34973     
34974     /**
34975      * Clear all selections
34976      */
34977     clearSelections : function(suppressEvent){
34978         var sn = this.selNodes;
34979         if(sn.length > 0){
34980             for(var i = 0, len = sn.length; i < len; i++){
34981                 sn[i].ui.onSelectedChange(false);
34982             }
34983             this.selNodes = [];
34984             this.selMap = {};
34985             if(suppressEvent !== true){
34986                 this.fireEvent("selectionchange", this, this.selNodes);
34987             }
34988         }
34989     },
34990     
34991     /**
34992      * Returns true if the node is selected
34993      * @param {TreeNode} node The node to check
34994      * @return {Boolean}
34995      */
34996     isSelected : function(node){
34997         return this.selMap[node.id] ? true : false;  
34998     },
34999     
35000     /**
35001      * Returns an array of the selected nodes
35002      * @return {Array}
35003      */
35004     getSelectedNodes : function(){
35005         return this.selNodes;    
35006     },
35007
35008     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
35009
35010     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
35011
35012     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
35013 });/*
35014  * Based on:
35015  * Ext JS Library 1.1.1
35016  * Copyright(c) 2006-2007, Ext JS, LLC.
35017  *
35018  * Originally Released Under LGPL - original licence link has changed is not relivant.
35019  *
35020  * Fork - LGPL
35021  * <script type="text/javascript">
35022  */
35023  
35024 /**
35025  * @class Roo.tree.TreeNode
35026  * @extends Roo.data.Node
35027  * @cfg {String} text The text for this node
35028  * @cfg {Boolean} expanded true to start the node expanded
35029  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
35030  * @cfg {Boolean} allowDrop false if this node cannot be drop on
35031  * @cfg {Boolean} disabled true to start the node disabled
35032  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
35033  *    is to use the cls or iconCls attributes and add the icon via a CSS background image.
35034  * @cfg {String} cls A css class to be added to the node
35035  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
35036  * @cfg {String} href URL of the link used for the node (defaults to #)
35037  * @cfg {String} hrefTarget target frame for the link
35038  * @cfg {String} qtip An Ext QuickTip for the node
35039  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
35040  * @cfg {Boolean} singleClickExpand True for single click expand on this node
35041  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
35042  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
35043  * (defaults to undefined with no checkbox rendered)
35044  * @constructor
35045  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
35046  */
35047 Roo.tree.TreeNode = function(attributes){
35048     attributes = attributes || {};
35049     if(typeof attributes == "string"){
35050         attributes = {text: attributes};
35051     }
35052     this.childrenRendered = false;
35053     this.rendered = false;
35054     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
35055     this.expanded = attributes.expanded === true;
35056     this.isTarget = attributes.isTarget !== false;
35057     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
35058     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
35059
35060     /**
35061      * Read-only. The text for this node. To change it use setText().
35062      * @type String
35063      */
35064     this.text = attributes.text;
35065     /**
35066      * True if this node is disabled.
35067      * @type Boolean
35068      */
35069     this.disabled = attributes.disabled === true;
35070
35071     this.addEvents({
35072         /**
35073         * @event textchange
35074         * Fires when the text for this node is changed
35075         * @param {Node} this This node
35076         * @param {String} text The new text
35077         * @param {String} oldText The old text
35078         */
35079         "textchange" : true,
35080         /**
35081         * @event beforeexpand
35082         * Fires before this node is expanded, return false to cancel.
35083         * @param {Node} this This node
35084         * @param {Boolean} deep
35085         * @param {Boolean} anim
35086         */
35087         "beforeexpand" : true,
35088         /**
35089         * @event beforecollapse
35090         * Fires before this node is collapsed, return false to cancel.
35091         * @param {Node} this This node
35092         * @param {Boolean} deep
35093         * @param {Boolean} anim
35094         */
35095         "beforecollapse" : true,
35096         /**
35097         * @event expand
35098         * Fires when this node is expanded
35099         * @param {Node} this This node
35100         */
35101         "expand" : true,
35102         /**
35103         * @event disabledchange
35104         * Fires when the disabled status of this node changes
35105         * @param {Node} this This node
35106         * @param {Boolean} disabled
35107         */
35108         "disabledchange" : true,
35109         /**
35110         * @event collapse
35111         * Fires when this node is collapsed
35112         * @param {Node} this This node
35113         */
35114         "collapse" : true,
35115         /**
35116         * @event beforeclick
35117         * Fires before click processing. Return false to cancel the default action.
35118         * @param {Node} this This node
35119         * @param {Roo.EventObject} e The event object
35120         */
35121         "beforeclick":true,
35122         /**
35123         * @event checkchange
35124         * Fires when a node with a checkbox's checked property changes
35125         * @param {Node} this This node
35126         * @param {Boolean} checked
35127         */
35128         "checkchange":true,
35129         /**
35130         * @event click
35131         * Fires when this node is clicked
35132         * @param {Node} this This node
35133         * @param {Roo.EventObject} e The event object
35134         */
35135         "click":true,
35136         /**
35137         * @event dblclick
35138         * Fires when this node is double clicked
35139         * @param {Node} this This node
35140         * @param {Roo.EventObject} e The event object
35141         */
35142         "dblclick":true,
35143         /**
35144         * @event contextmenu
35145         * Fires when this node is right clicked
35146         * @param {Node} this This node
35147         * @param {Roo.EventObject} e The event object
35148         */
35149         "contextmenu":true,
35150         /**
35151         * @event beforechildrenrendered
35152         * Fires right before the child nodes for this node are rendered
35153         * @param {Node} this This node
35154         */
35155         "beforechildrenrendered":true
35156     });
35157
35158     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
35159
35160     /**
35161      * Read-only. The UI for this node
35162      * @type TreeNodeUI
35163      */
35164     this.ui = new uiClass(this);
35165     
35166     // finally support items[]
35167     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
35168         return;
35169     }
35170     
35171     
35172     Roo.each(this.attributes.items, function(c) {
35173         this.appendChild(Roo.factory(c,Roo.Tree));
35174     }, this);
35175     delete this.attributes.items;
35176     
35177     
35178     
35179 };
35180 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
35181     preventHScroll: true,
35182     /**
35183      * Returns true if this node is expanded
35184      * @return {Boolean}
35185      */
35186     isExpanded : function(){
35187         return this.expanded;
35188     },
35189
35190     /**
35191      * Returns the UI object for this node
35192      * @return {TreeNodeUI}
35193      */
35194     getUI : function(){
35195         return this.ui;
35196     },
35197
35198     // private override
35199     setFirstChild : function(node){
35200         var of = this.firstChild;
35201         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
35202         if(this.childrenRendered && of && node != of){
35203             of.renderIndent(true, true);
35204         }
35205         if(this.rendered){
35206             this.renderIndent(true, true);
35207         }
35208     },
35209
35210     // private override
35211     setLastChild : function(node){
35212         var ol = this.lastChild;
35213         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
35214         if(this.childrenRendered && ol && node != ol){
35215             ol.renderIndent(true, true);
35216         }
35217         if(this.rendered){
35218             this.renderIndent(true, true);
35219         }
35220     },
35221
35222     // these methods are overridden to provide lazy rendering support
35223     // private override
35224     appendChild : function()
35225     {
35226         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
35227         if(node && this.childrenRendered){
35228             node.render();
35229         }
35230         this.ui.updateExpandIcon();
35231         return node;
35232     },
35233
35234     // private override
35235     removeChild : function(node){
35236         this.ownerTree.getSelectionModel().unselect(node);
35237         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
35238         // if it's been rendered remove dom node
35239         if(this.childrenRendered){
35240             node.ui.remove();
35241         }
35242         if(this.childNodes.length < 1){
35243             this.collapse(false, false);
35244         }else{
35245             this.ui.updateExpandIcon();
35246         }
35247         if(!this.firstChild) {
35248             this.childrenRendered = false;
35249         }
35250         return node;
35251     },
35252
35253     // private override
35254     insertBefore : function(node, refNode){
35255         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
35256         if(newNode && refNode && this.childrenRendered){
35257             node.render();
35258         }
35259         this.ui.updateExpandIcon();
35260         return newNode;
35261     },
35262
35263     /**
35264      * Sets the text for this node
35265      * @param {String} text
35266      */
35267     setText : function(text){
35268         var oldText = this.text;
35269         this.text = text;
35270         this.attributes.text = text;
35271         if(this.rendered){ // event without subscribing
35272             this.ui.onTextChange(this, text, oldText);
35273         }
35274         this.fireEvent("textchange", this, text, oldText);
35275     },
35276
35277     /**
35278      * Triggers selection of this node
35279      */
35280     select : function(){
35281         this.getOwnerTree().getSelectionModel().select(this);
35282     },
35283
35284     /**
35285      * Triggers deselection of this node
35286      */
35287     unselect : function(){
35288         this.getOwnerTree().getSelectionModel().unselect(this);
35289     },
35290
35291     /**
35292      * Returns true if this node is selected
35293      * @return {Boolean}
35294      */
35295     isSelected : function(){
35296         return this.getOwnerTree().getSelectionModel().isSelected(this);
35297     },
35298
35299     /**
35300      * Expand this node.
35301      * @param {Boolean} deep (optional) True to expand all children as well
35302      * @param {Boolean} anim (optional) false to cancel the default animation
35303      * @param {Function} callback (optional) A callback to be called when
35304      * expanding this node completes (does not wait for deep expand to complete).
35305      * Called with 1 parameter, this node.
35306      */
35307     expand : function(deep, anim, callback){
35308         if(!this.expanded){
35309             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
35310                 return;
35311             }
35312             if(!this.childrenRendered){
35313                 this.renderChildren();
35314             }
35315             this.expanded = true;
35316             
35317             if(!this.isHiddenRoot() && (this.getOwnerTree() && this.getOwnerTree().animate && anim !== false) || anim){
35318                 this.ui.animExpand(function(){
35319                     this.fireEvent("expand", this);
35320                     if(typeof callback == "function"){
35321                         callback(this);
35322                     }
35323                     if(deep === true){
35324                         this.expandChildNodes(true);
35325                     }
35326                 }.createDelegate(this));
35327                 return;
35328             }else{
35329                 this.ui.expand();
35330                 this.fireEvent("expand", this);
35331                 if(typeof callback == "function"){
35332                     callback(this);
35333                 }
35334             }
35335         }else{
35336            if(typeof callback == "function"){
35337                callback(this);
35338            }
35339         }
35340         if(deep === true){
35341             this.expandChildNodes(true);
35342         }
35343     },
35344
35345     isHiddenRoot : function(){
35346         return this.isRoot && !this.getOwnerTree().rootVisible;
35347     },
35348
35349     /**
35350      * Collapse this node.
35351      * @param {Boolean} deep (optional) True to collapse all children as well
35352      * @param {Boolean} anim (optional) false to cancel the default animation
35353      */
35354     collapse : function(deep, anim){
35355         if(this.expanded && !this.isHiddenRoot()){
35356             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
35357                 return;
35358             }
35359             this.expanded = false;
35360             if((this.getOwnerTree().animate && anim !== false) || anim){
35361                 this.ui.animCollapse(function(){
35362                     this.fireEvent("collapse", this);
35363                     if(deep === true){
35364                         this.collapseChildNodes(true);
35365                     }
35366                 }.createDelegate(this));
35367                 return;
35368             }else{
35369                 this.ui.collapse();
35370                 this.fireEvent("collapse", this);
35371             }
35372         }
35373         if(deep === true){
35374             var cs = this.childNodes;
35375             for(var i = 0, len = cs.length; i < len; i++) {
35376                 cs[i].collapse(true, false);
35377             }
35378         }
35379     },
35380
35381     // private
35382     delayedExpand : function(delay){
35383         if(!this.expandProcId){
35384             this.expandProcId = this.expand.defer(delay, this);
35385         }
35386     },
35387
35388     // private
35389     cancelExpand : function(){
35390         if(this.expandProcId){
35391             clearTimeout(this.expandProcId);
35392         }
35393         this.expandProcId = false;
35394     },
35395
35396     /**
35397      * Toggles expanded/collapsed state of the node
35398      */
35399     toggle : function(){
35400         if(this.expanded){
35401             this.collapse();
35402         }else{
35403             this.expand();
35404         }
35405     },
35406
35407     /**
35408      * Ensures all parent nodes are expanded
35409      */
35410     ensureVisible : function(callback){
35411         var tree = this.getOwnerTree();
35412         tree.expandPath(this.parentNode.getPath(), false, function(){
35413             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
35414             Roo.callback(callback);
35415         }.createDelegate(this));
35416     },
35417
35418     /**
35419      * Expand all child nodes
35420      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
35421      */
35422     expandChildNodes : function(deep){
35423         var cs = this.childNodes;
35424         for(var i = 0, len = cs.length; i < len; i++) {
35425                 cs[i].expand(deep);
35426         }
35427     },
35428
35429     /**
35430      * Collapse all child nodes
35431      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
35432      */
35433     collapseChildNodes : function(deep){
35434         var cs = this.childNodes;
35435         for(var i = 0, len = cs.length; i < len; i++) {
35436                 cs[i].collapse(deep);
35437         }
35438     },
35439
35440     /**
35441      * Disables this node
35442      */
35443     disable : function(){
35444         this.disabled = true;
35445         this.unselect();
35446         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
35447             this.ui.onDisableChange(this, true);
35448         }
35449         this.fireEvent("disabledchange", this, true);
35450     },
35451
35452     /**
35453      * Enables this node
35454      */
35455     enable : function(){
35456         this.disabled = false;
35457         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
35458             this.ui.onDisableChange(this, false);
35459         }
35460         this.fireEvent("disabledchange", this, false);
35461     },
35462
35463     // private
35464     renderChildren : function(suppressEvent){
35465         if(suppressEvent !== false){
35466             this.fireEvent("beforechildrenrendered", this);
35467         }
35468         var cs = this.childNodes;
35469         for(var i = 0, len = cs.length; i < len; i++){
35470             cs[i].render(true);
35471         }
35472         this.childrenRendered = true;
35473     },
35474
35475     // private
35476     sort : function(fn, scope){
35477         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
35478         if(this.childrenRendered){
35479             var cs = this.childNodes;
35480             for(var i = 0, len = cs.length; i < len; i++){
35481                 cs[i].render(true);
35482             }
35483         }
35484     },
35485
35486     // private
35487     render : function(bulkRender){
35488         this.ui.render(bulkRender);
35489         if(!this.rendered){
35490             this.rendered = true;
35491             if(this.expanded){
35492                 this.expanded = false;
35493                 this.expand(false, false);
35494             }
35495         }
35496     },
35497
35498     // private
35499     renderIndent : function(deep, refresh){
35500         if(refresh){
35501             this.ui.childIndent = null;
35502         }
35503         this.ui.renderIndent();
35504         if(deep === true && this.childrenRendered){
35505             var cs = this.childNodes;
35506             for(var i = 0, len = cs.length; i < len; i++){
35507                 cs[i].renderIndent(true, refresh);
35508             }
35509         }
35510     }
35511 });/*
35512  * Based on:
35513  * Ext JS Library 1.1.1
35514  * Copyright(c) 2006-2007, Ext JS, LLC.
35515  *
35516  * Originally Released Under LGPL - original licence link has changed is not relivant.
35517  *
35518  * Fork - LGPL
35519  * <script type="text/javascript">
35520  */
35521  
35522 /**
35523  * @class Roo.tree.AsyncTreeNode
35524  * @extends Roo.tree.TreeNode
35525  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
35526  * @constructor
35527  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
35528  */
35529  Roo.tree.AsyncTreeNode = function(config){
35530     this.loaded = false;
35531     this.loading = false;
35532     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
35533     /**
35534     * @event beforeload
35535     * Fires before this node is loaded, return false to cancel
35536     * @param {Node} this This node
35537     */
35538     this.addEvents({'beforeload':true, 'load': true});
35539     /**
35540     * @event load
35541     * Fires when this node is loaded
35542     * @param {Node} this This node
35543     */
35544     /**
35545      * The loader used by this node (defaults to using the tree's defined loader)
35546      * @type TreeLoader
35547      * @property loader
35548      */
35549 };
35550 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
35551     expand : function(deep, anim, callback){
35552         if(this.loading){ // if an async load is already running, waiting til it's done
35553             var timer;
35554             var f = function(){
35555                 if(!this.loading){ // done loading
35556                     clearInterval(timer);
35557                     this.expand(deep, anim, callback);
35558                 }
35559             }.createDelegate(this);
35560             timer = setInterval(f, 200);
35561             return;
35562         }
35563         if(!this.loaded){
35564             if(this.fireEvent("beforeload", this) === false){
35565                 return;
35566             }
35567             this.loading = true;
35568             this.ui.beforeLoad(this);
35569             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
35570             if(loader){
35571                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
35572                 return;
35573             }
35574         }
35575         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
35576     },
35577     
35578     /**
35579      * Returns true if this node is currently loading
35580      * @return {Boolean}
35581      */
35582     isLoading : function(){
35583         return this.loading;  
35584     },
35585     
35586     loadComplete : function(deep, anim, callback){
35587         this.loading = false;
35588         this.loaded = true;
35589         this.ui.afterLoad(this);
35590         this.fireEvent("load", this);
35591         this.expand(deep, anim, callback);
35592     },
35593     
35594     /**
35595      * Returns true if this node has been loaded
35596      * @return {Boolean}
35597      */
35598     isLoaded : function(){
35599         return this.loaded;
35600     },
35601     
35602     hasChildNodes : function(){
35603         if(!this.isLeaf() && !this.loaded){
35604             return true;
35605         }else{
35606             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
35607         }
35608     },
35609
35610     /**
35611      * Trigger a reload for this node
35612      * @param {Function} callback
35613      */
35614     reload : function(callback){
35615         this.collapse(false, false);
35616         while(this.firstChild){
35617             this.removeChild(this.firstChild);
35618         }
35619         this.childrenRendered = false;
35620         this.loaded = false;
35621         if(this.isHiddenRoot()){
35622             this.expanded = false;
35623         }
35624         this.expand(false, false, callback);
35625     }
35626 });/*
35627  * Based on:
35628  * Ext JS Library 1.1.1
35629  * Copyright(c) 2006-2007, Ext JS, LLC.
35630  *
35631  * Originally Released Under LGPL - original licence link has changed is not relivant.
35632  *
35633  * Fork - LGPL
35634  * <script type="text/javascript">
35635  */
35636  
35637 /**
35638  * @class Roo.tree.TreeNodeUI
35639  * @constructor
35640  * @param {Object} node The node to render
35641  * The TreeNode UI implementation is separate from the
35642  * tree implementation. Unless you are customizing the tree UI,
35643  * you should never have to use this directly.
35644  */
35645 Roo.tree.TreeNodeUI = function(node){
35646     this.node = node;
35647     this.rendered = false;
35648     this.animating = false;
35649     this.emptyIcon = Roo.BLANK_IMAGE_URL;
35650 };
35651
35652 Roo.tree.TreeNodeUI.prototype = {
35653     removeChild : function(node){
35654         if(this.rendered){
35655             this.ctNode.removeChild(node.ui.getEl());
35656         }
35657     },
35658
35659     beforeLoad : function(){
35660          this.addClass("x-tree-node-loading");
35661     },
35662
35663     afterLoad : function(){
35664          this.removeClass("x-tree-node-loading");
35665     },
35666
35667     onTextChange : function(node, text, oldText){
35668         if(this.rendered){
35669             this.textNode.innerHTML = text;
35670         }
35671     },
35672
35673     onDisableChange : function(node, state){
35674         this.disabled = state;
35675         if(state){
35676             this.addClass("x-tree-node-disabled");
35677         }else{
35678             this.removeClass("x-tree-node-disabled");
35679         }
35680     },
35681
35682     onSelectedChange : function(state){
35683         if(state){
35684             this.focus();
35685             this.addClass("x-tree-selected");
35686         }else{
35687             //this.blur();
35688             this.removeClass("x-tree-selected");
35689         }
35690     },
35691
35692     onMove : function(tree, node, oldParent, newParent, index, refNode){
35693         this.childIndent = null;
35694         if(this.rendered){
35695             var targetNode = newParent.ui.getContainer();
35696             if(!targetNode){//target not rendered
35697                 this.holder = document.createElement("div");
35698                 this.holder.appendChild(this.wrap);
35699                 return;
35700             }
35701             var insertBefore = refNode ? refNode.ui.getEl() : null;
35702             if(insertBefore){
35703                 targetNode.insertBefore(this.wrap, insertBefore);
35704             }else{
35705                 targetNode.appendChild(this.wrap);
35706             }
35707             this.node.renderIndent(true);
35708         }
35709     },
35710
35711     addClass : function(cls){
35712         if(this.elNode){
35713             Roo.fly(this.elNode).addClass(cls);
35714         }
35715     },
35716
35717     removeClass : function(cls){
35718         if(this.elNode){
35719             Roo.fly(this.elNode).removeClass(cls);
35720         }
35721     },
35722
35723     remove : function(){
35724         if(this.rendered){
35725             this.holder = document.createElement("div");
35726             this.holder.appendChild(this.wrap);
35727         }
35728     },
35729
35730     fireEvent : function(){
35731         return this.node.fireEvent.apply(this.node, arguments);
35732     },
35733
35734     initEvents : function(){
35735         this.node.on("move", this.onMove, this);
35736         var E = Roo.EventManager;
35737         var a = this.anchor;
35738
35739         var el = Roo.fly(a, '_treeui');
35740
35741         if(Roo.isOpera){ // opera render bug ignores the CSS
35742             el.setStyle("text-decoration", "none");
35743         }
35744
35745         el.on("click", this.onClick, this);
35746         el.on("dblclick", this.onDblClick, this);
35747
35748         if(this.checkbox){
35749             Roo.EventManager.on(this.checkbox,
35750                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
35751         }
35752
35753         el.on("contextmenu", this.onContextMenu, this);
35754
35755         var icon = Roo.fly(this.iconNode);
35756         icon.on("click", this.onClick, this);
35757         icon.on("dblclick", this.onDblClick, this);
35758         icon.on("contextmenu", this.onContextMenu, this);
35759         E.on(this.ecNode, "click", this.ecClick, this, true);
35760
35761         if(this.node.disabled){
35762             this.addClass("x-tree-node-disabled");
35763         }
35764         if(this.node.hidden){
35765             this.addClass("x-tree-node-disabled");
35766         }
35767         var ot = this.node.getOwnerTree();
35768         var dd = ot ? (ot.enableDD || ot.enableDrag || ot.enableDrop) : false;
35769         if(dd && (!this.node.isRoot || ot.rootVisible)){
35770             Roo.dd.Registry.register(this.elNode, {
35771                 node: this.node,
35772                 handles: this.getDDHandles(),
35773                 isHandle: false
35774             });
35775         }
35776     },
35777
35778     getDDHandles : function(){
35779         return [this.iconNode, this.textNode];
35780     },
35781
35782     hide : function(){
35783         if(this.rendered){
35784             this.wrap.style.display = "none";
35785         }
35786     },
35787
35788     show : function(){
35789         if(this.rendered){
35790             this.wrap.style.display = "";
35791         }
35792     },
35793
35794     onContextMenu : function(e){
35795         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
35796             e.preventDefault();
35797             this.focus();
35798             this.fireEvent("contextmenu", this.node, e);
35799         }
35800     },
35801
35802     onClick : function(e){
35803         if(this.dropping){
35804             e.stopEvent();
35805             return;
35806         }
35807         if(this.fireEvent("beforeclick", this.node, e) !== false){
35808             if(!this.disabled && this.node.attributes.href){
35809                 this.fireEvent("click", this.node, e);
35810                 return;
35811             }
35812             e.preventDefault();
35813             if(this.disabled){
35814                 return;
35815             }
35816
35817             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
35818                 this.node.toggle();
35819             }
35820
35821             this.fireEvent("click", this.node, e);
35822         }else{
35823             e.stopEvent();
35824         }
35825     },
35826
35827     onDblClick : function(e){
35828         e.preventDefault();
35829         if(this.disabled){
35830             return;
35831         }
35832         if(this.checkbox){
35833             this.toggleCheck();
35834         }
35835         if(!this.animating && this.node.hasChildNodes()){
35836             this.node.toggle();
35837         }
35838         this.fireEvent("dblclick", this.node, e);
35839     },
35840
35841     onCheckChange : function(){
35842         var checked = this.checkbox.checked;
35843         this.node.attributes.checked = checked;
35844         this.fireEvent('checkchange', this.node, checked);
35845     },
35846
35847     ecClick : function(e){
35848         if(!this.animating && this.node.hasChildNodes()){
35849             this.node.toggle();
35850         }
35851     },
35852
35853     startDrop : function(){
35854         this.dropping = true;
35855     },
35856
35857     // delayed drop so the click event doesn't get fired on a drop
35858     endDrop : function(){
35859        setTimeout(function(){
35860            this.dropping = false;
35861        }.createDelegate(this), 50);
35862     },
35863
35864     expand : function(){
35865         this.updateExpandIcon();
35866         this.ctNode.style.display = "";
35867     },
35868
35869     focus : function(){
35870         if(!this.node.preventHScroll){
35871             try{this.anchor.focus();
35872             }catch(e){}
35873         }else if(!Roo.isIE){
35874             try{
35875                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
35876                 var l = noscroll.scrollLeft;
35877                 this.anchor.focus();
35878                 noscroll.scrollLeft = l;
35879             }catch(e){}
35880         }
35881     },
35882
35883     toggleCheck : function(value){
35884         var cb = this.checkbox;
35885         if(cb){
35886             cb.checked = (value === undefined ? !cb.checked : value);
35887         }
35888     },
35889
35890     blur : function(){
35891         try{
35892             this.anchor.blur();
35893         }catch(e){}
35894     },
35895
35896     animExpand : function(callback){
35897         var ct = Roo.get(this.ctNode);
35898         ct.stopFx();
35899         if(!this.node.hasChildNodes()){
35900             this.updateExpandIcon();
35901             this.ctNode.style.display = "";
35902             Roo.callback(callback);
35903             return;
35904         }
35905         this.animating = true;
35906         this.updateExpandIcon();
35907
35908         ct.slideIn('t', {
35909            callback : function(){
35910                this.animating = false;
35911                Roo.callback(callback);
35912             },
35913             scope: this,
35914             duration: this.node.ownerTree.duration || .25
35915         });
35916     },
35917
35918     highlight : function(){
35919         var tree = this.node.getOwnerTree();
35920         Roo.fly(this.wrap).highlight(
35921             tree.hlColor || "C3DAF9",
35922             {endColor: tree.hlBaseColor}
35923         );
35924     },
35925
35926     collapse : function(){
35927         this.updateExpandIcon();
35928         this.ctNode.style.display = "none";
35929     },
35930
35931     animCollapse : function(callback){
35932         var ct = Roo.get(this.ctNode);
35933         ct.enableDisplayMode('block');
35934         ct.stopFx();
35935
35936         this.animating = true;
35937         this.updateExpandIcon();
35938
35939         ct.slideOut('t', {
35940             callback : function(){
35941                this.animating = false;
35942                Roo.callback(callback);
35943             },
35944             scope: this,
35945             duration: this.node.ownerTree.duration || .25
35946         });
35947     },
35948
35949     getContainer : function(){
35950         return this.ctNode;
35951     },
35952
35953     getEl : function(){
35954         return this.wrap;
35955     },
35956
35957     appendDDGhost : function(ghostNode){
35958         ghostNode.appendChild(this.elNode.cloneNode(true));
35959     },
35960
35961     getDDRepairXY : function(){
35962         return Roo.lib.Dom.getXY(this.iconNode);
35963     },
35964
35965     onRender : function(){
35966         this.render();
35967     },
35968
35969     render : function(bulkRender){
35970         var n = this.node, a = n.attributes;
35971         var targetNode = n.parentNode ?
35972               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
35973
35974         if(!this.rendered){
35975             this.rendered = true;
35976
35977             this.renderElements(n, a, targetNode, bulkRender);
35978
35979             if(a.qtip){
35980                if(this.textNode.setAttributeNS){
35981                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
35982                    if(a.qtipTitle){
35983                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
35984                    }
35985                }else{
35986                    this.textNode.setAttribute("ext:qtip", a.qtip);
35987                    if(a.qtipTitle){
35988                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
35989                    }
35990                }
35991             }else if(a.qtipCfg){
35992                 a.qtipCfg.target = Roo.id(this.textNode);
35993                 Roo.QuickTips.register(a.qtipCfg);
35994             }
35995             this.initEvents();
35996             if(!this.node.expanded){
35997                 this.updateExpandIcon();
35998             }
35999         }else{
36000             if(bulkRender === true) {
36001                 targetNode.appendChild(this.wrap);
36002             }
36003         }
36004     },
36005
36006     renderElements : function(n, a, targetNode, bulkRender)
36007     {
36008         // add some indent caching, this helps performance when rendering a large tree
36009         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
36010         var t = n.getOwnerTree();
36011         var txt = t && t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
36012         if (typeof(n.attributes.html) != 'undefined') {
36013             txt = n.attributes.html;
36014         }
36015         var tip = t && t.rendererTip ? t.rendererTip(n.attributes) : txt;
36016         var cb = typeof a.checked == 'boolean';
36017         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
36018         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
36019             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
36020             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
36021             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
36022             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
36023             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
36024              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
36025                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
36026             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
36027             "</li>"];
36028
36029         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
36030             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
36031                                 n.nextSibling.ui.getEl(), buf.join(""));
36032         }else{
36033             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
36034         }
36035
36036         this.elNode = this.wrap.childNodes[0];
36037         this.ctNode = this.wrap.childNodes[1];
36038         var cs = this.elNode.childNodes;
36039         this.indentNode = cs[0];
36040         this.ecNode = cs[1];
36041         this.iconNode = cs[2];
36042         var index = 3;
36043         if(cb){
36044             this.checkbox = cs[3];
36045             index++;
36046         }
36047         this.anchor = cs[index];
36048         this.textNode = cs[index].firstChild;
36049     },
36050
36051     getAnchor : function(){
36052         return this.anchor;
36053     },
36054
36055     getTextEl : function(){
36056         return this.textNode;
36057     },
36058
36059     getIconEl : function(){
36060         return this.iconNode;
36061     },
36062
36063     isChecked : function(){
36064         return this.checkbox ? this.checkbox.checked : false;
36065     },
36066
36067     updateExpandIcon : function(){
36068         if(this.rendered){
36069             var n = this.node, c1, c2;
36070             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
36071             var hasChild = n.hasChildNodes();
36072             if(hasChild){
36073                 if(n.expanded){
36074                     cls += "-minus";
36075                     c1 = "x-tree-node-collapsed";
36076                     c2 = "x-tree-node-expanded";
36077                 }else{
36078                     cls += "-plus";
36079                     c1 = "x-tree-node-expanded";
36080                     c2 = "x-tree-node-collapsed";
36081                 }
36082                 if(this.wasLeaf){
36083                     this.removeClass("x-tree-node-leaf");
36084                     this.wasLeaf = false;
36085                 }
36086                 if(this.c1 != c1 || this.c2 != c2){
36087                     Roo.fly(this.elNode).replaceClass(c1, c2);
36088                     this.c1 = c1; this.c2 = c2;
36089                 }
36090             }else{
36091                 // this changes non-leafs into leafs if they have no children.
36092                 // it's not very rational behaviour..
36093                 
36094                 if(!this.wasLeaf && this.node.leaf){
36095                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
36096                     delete this.c1;
36097                     delete this.c2;
36098                     this.wasLeaf = true;
36099                 }
36100             }
36101             var ecc = "x-tree-ec-icon "+cls;
36102             if(this.ecc != ecc){
36103                 this.ecNode.className = ecc;
36104                 this.ecc = ecc;
36105             }
36106         }
36107     },
36108
36109     getChildIndent : function(){
36110         if(!this.childIndent){
36111             var buf = [];
36112             var p = this.node;
36113             while(p){
36114                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
36115                     if(!p.isLast()) {
36116                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
36117                     } else {
36118                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
36119                     }
36120                 }
36121                 p = p.parentNode;
36122             }
36123             this.childIndent = buf.join("");
36124         }
36125         return this.childIndent;
36126     },
36127
36128     renderIndent : function(){
36129         if(this.rendered){
36130             var indent = "";
36131             var p = this.node.parentNode;
36132             if(p){
36133                 indent = p.ui.getChildIndent();
36134             }
36135             if(this.indentMarkup != indent){ // don't rerender if not required
36136                 this.indentNode.innerHTML = indent;
36137                 this.indentMarkup = indent;
36138             }
36139             this.updateExpandIcon();
36140         }
36141     }
36142 };
36143
36144 Roo.tree.RootTreeNodeUI = function(){
36145     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
36146 };
36147 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
36148     render : function(){
36149         if(!this.rendered){
36150             var targetNode = this.node.ownerTree.innerCt.dom;
36151             this.node.expanded = true;
36152             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
36153             this.wrap = this.ctNode = targetNode.firstChild;
36154         }
36155     },
36156     collapse : function(){
36157     },
36158     expand : function(){
36159     }
36160 });/*
36161  * Based on:
36162  * Ext JS Library 1.1.1
36163  * Copyright(c) 2006-2007, Ext JS, LLC.
36164  *
36165  * Originally Released Under LGPL - original licence link has changed is not relivant.
36166  *
36167  * Fork - LGPL
36168  * <script type="text/javascript">
36169  */
36170 /**
36171  * @class Roo.tree.TreeLoader
36172  * @extends Roo.util.Observable
36173  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
36174  * nodes from a specified URL. The response must be a javascript Array definition
36175  * who's elements are node definition objects. eg:
36176  * <pre><code>
36177 {  success : true,
36178    data :      [
36179    
36180     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
36181     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
36182     ]
36183 }
36184
36185
36186 </code></pre>
36187  * <br><br>
36188  * The old style respose with just an array is still supported, but not recommended.
36189  * <br><br>
36190  *
36191  * A server request is sent, and child nodes are loaded only when a node is expanded.
36192  * The loading node's id is passed to the server under the parameter name "node" to
36193  * enable the server to produce the correct child nodes.
36194  * <br><br>
36195  * To pass extra parameters, an event handler may be attached to the "beforeload"
36196  * event, and the parameters specified in the TreeLoader's baseParams property:
36197  * <pre><code>
36198     myTreeLoader.on("beforeload", function(treeLoader, node) {
36199         this.baseParams.category = node.attributes.category;
36200     }, this);
36201     
36202 </code></pre>
36203  *
36204  * This would pass an HTTP parameter called "category" to the server containing
36205  * the value of the Node's "category" attribute.
36206  * @constructor
36207  * Creates a new Treeloader.
36208  * @param {Object} config A config object containing config properties.
36209  */
36210 Roo.tree.TreeLoader = function(config){
36211     this.baseParams = {};
36212     this.requestMethod = "POST";
36213     Roo.apply(this, config);
36214
36215     this.addEvents({
36216     
36217         /**
36218          * @event beforeload
36219          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
36220          * @param {Object} This TreeLoader object.
36221          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
36222          * @param {Object} callback The callback function specified in the {@link #load} call.
36223          */
36224         beforeload : true,
36225         /**
36226          * @event load
36227          * Fires when the node has been successfuly loaded.
36228          * @param {Object} This TreeLoader object.
36229          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
36230          * @param {Object} response The response object containing the data from the server.
36231          */
36232         load : true,
36233         /**
36234          * @event loadexception
36235          * Fires if the network request failed.
36236          * @param {Object} This TreeLoader object.
36237          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
36238          * @param {Object} response The response object containing the data from the server.
36239          */
36240         loadexception : true,
36241         /**
36242          * @event create
36243          * Fires before a node is created, enabling you to return custom Node types 
36244          * @param {Object} This TreeLoader object.
36245          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
36246          */
36247         create : true
36248     });
36249
36250     Roo.tree.TreeLoader.superclass.constructor.call(this);
36251 };
36252
36253 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
36254     /**
36255     * @cfg {String} dataUrl The URL from which to request a Json string which
36256     * specifies an array of node definition object representing the child nodes
36257     * to be loaded.
36258     */
36259     /**
36260     * @cfg {String} requestMethod either GET or POST
36261     * defaults to POST (due to BC)
36262     * to be loaded.
36263     */
36264     /**
36265     * @cfg {Object} baseParams (optional) An object containing properties which
36266     * specify HTTP parameters to be passed to each request for child nodes.
36267     */
36268     /**
36269     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
36270     * created by this loader. If the attributes sent by the server have an attribute in this object,
36271     * they take priority.
36272     */
36273     /**
36274     * @cfg {Object} uiProviders (optional) An object containing properties which
36275     * 
36276     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
36277     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
36278     * <i>uiProvider</i> attribute of a returned child node is a string rather
36279     * than a reference to a TreeNodeUI implementation, this that string value
36280     * is used as a property name in the uiProviders object. You can define the provider named
36281     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
36282     */
36283     uiProviders : {},
36284
36285     /**
36286     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
36287     * child nodes before loading.
36288     */
36289     clearOnLoad : true,
36290
36291     /**
36292     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
36293     * property on loading, rather than expecting an array. (eg. more compatible to a standard
36294     * Grid query { data : [ .....] }
36295     */
36296     
36297     root : false,
36298      /**
36299     * @cfg {String} queryParam (optional) 
36300     * Name of the query as it will be passed on the querystring (defaults to 'node')
36301     * eg. the request will be ?node=[id]
36302     */
36303     
36304     
36305     queryParam: false,
36306     
36307     /**
36308      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
36309      * This is called automatically when a node is expanded, but may be used to reload
36310      * a node (or append new children if the {@link #clearOnLoad} option is false.)
36311      * @param {Roo.tree.TreeNode} node
36312      * @param {Function} callback
36313      */
36314     load : function(node, callback){
36315         if(this.clearOnLoad){
36316             while(node.firstChild){
36317                 node.removeChild(node.firstChild);
36318             }
36319         }
36320         if(node.attributes.children){ // preloaded json children
36321             var cs = node.attributes.children;
36322             for(var i = 0, len = cs.length; i < len; i++){
36323                 node.appendChild(this.createNode(cs[i]));
36324             }
36325             if(typeof callback == "function"){
36326                 callback();
36327             }
36328         }else if(this.dataUrl){
36329             this.requestData(node, callback);
36330         }
36331     },
36332
36333     getParams: function(node){
36334         var buf = [], bp = this.baseParams;
36335         for(var key in bp){
36336             if(typeof bp[key] != "function"){
36337                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
36338             }
36339         }
36340         var n = this.queryParam === false ? 'node' : this.queryParam;
36341         buf.push(n + "=", encodeURIComponent(node.id));
36342         return buf.join("");
36343     },
36344
36345     requestData : function(node, callback){
36346         if(this.fireEvent("beforeload", this, node, callback) !== false){
36347             this.transId = Roo.Ajax.request({
36348                 method:this.requestMethod,
36349                 url: this.dataUrl||this.url,
36350                 success: this.handleResponse,
36351                 failure: this.handleFailure,
36352                 scope: this,
36353                 argument: {callback: callback, node: node},
36354                 params: this.getParams(node)
36355             });
36356         }else{
36357             // if the load is cancelled, make sure we notify
36358             // the node that we are done
36359             if(typeof callback == "function"){
36360                 callback();
36361             }
36362         }
36363     },
36364
36365     isLoading : function(){
36366         return this.transId ? true : false;
36367     },
36368
36369     abort : function(){
36370         if(this.isLoading()){
36371             Roo.Ajax.abort(this.transId);
36372         }
36373     },
36374
36375     // private
36376     createNode : function(attr)
36377     {
36378         // apply baseAttrs, nice idea Corey!
36379         if(this.baseAttrs){
36380             Roo.applyIf(attr, this.baseAttrs);
36381         }
36382         if(this.applyLoader !== false){
36383             attr.loader = this;
36384         }
36385         // uiProvider = depreciated..
36386         
36387         if(typeof(attr.uiProvider) == 'string'){
36388            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
36389                 /**  eval:var:attr */ eval(attr.uiProvider);
36390         }
36391         if(typeof(this.uiProviders['default']) != 'undefined') {
36392             attr.uiProvider = this.uiProviders['default'];
36393         }
36394         
36395         this.fireEvent('create', this, attr);
36396         
36397         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
36398         return(attr.leaf ?
36399                         new Roo.tree.TreeNode(attr) :
36400                         new Roo.tree.AsyncTreeNode(attr));
36401     },
36402
36403     processResponse : function(response, node, callback)
36404     {
36405         var json = response.responseText;
36406         try {
36407             
36408             var o = Roo.decode(json);
36409             
36410             if (this.root === false && typeof(o.success) != undefined) {
36411                 this.root = 'data'; // the default behaviour for list like data..
36412                 }
36413                 
36414             if (this.root !== false &&  !o.success) {
36415                 // it's a failure condition.
36416                 var a = response.argument;
36417                 this.fireEvent("loadexception", this, a.node, response);
36418                 Roo.log("Load failed - should have a handler really");
36419                 return;
36420             }
36421             
36422             
36423             
36424             if (this.root !== false) {
36425                  o = o[this.root];
36426             }
36427             
36428             for(var i = 0, len = o.length; i < len; i++){
36429                 var n = this.createNode(o[i]);
36430                 if(n){
36431                     node.appendChild(n);
36432                 }
36433             }
36434             if(typeof callback == "function"){
36435                 callback(this, node);
36436             }
36437         }catch(e){
36438             this.handleFailure(response);
36439         }
36440     },
36441
36442     handleResponse : function(response){
36443         this.transId = false;
36444         var a = response.argument;
36445         this.processResponse(response, a.node, a.callback);
36446         this.fireEvent("load", this, a.node, response);
36447     },
36448
36449     handleFailure : function(response)
36450     {
36451         // should handle failure better..
36452         this.transId = false;
36453         var a = response.argument;
36454         this.fireEvent("loadexception", this, a.node, response);
36455         if(typeof a.callback == "function"){
36456             a.callback(this, a.node);
36457         }
36458     }
36459 });/*
36460  * Based on:
36461  * Ext JS Library 1.1.1
36462  * Copyright(c) 2006-2007, Ext JS, LLC.
36463  *
36464  * Originally Released Under LGPL - original licence link has changed is not relivant.
36465  *
36466  * Fork - LGPL
36467  * <script type="text/javascript">
36468  */
36469
36470 /**
36471 * @class Roo.tree.TreeFilter
36472 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
36473 * @param {TreePanel} tree
36474 * @param {Object} config (optional)
36475  */
36476 Roo.tree.TreeFilter = function(tree, config){
36477     this.tree = tree;
36478     this.filtered = {};
36479     Roo.apply(this, config);
36480 };
36481
36482 Roo.tree.TreeFilter.prototype = {
36483     clearBlank:false,
36484     reverse:false,
36485     autoClear:false,
36486     remove:false,
36487
36488      /**
36489      * Filter the data by a specific attribute.
36490      * @param {String/RegExp} value Either string that the attribute value
36491      * should start with or a RegExp to test against the attribute
36492      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
36493      * @param {TreeNode} startNode (optional) The node to start the filter at.
36494      */
36495     filter : function(value, attr, startNode){
36496         attr = attr || "text";
36497         var f;
36498         if(typeof value == "string"){
36499             var vlen = value.length;
36500             // auto clear empty filter
36501             if(vlen == 0 && this.clearBlank){
36502                 this.clear();
36503                 return;
36504             }
36505             value = value.toLowerCase();
36506             f = function(n){
36507                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
36508             };
36509         }else if(value.exec){ // regex?
36510             f = function(n){
36511                 return value.test(n.attributes[attr]);
36512             };
36513         }else{
36514             throw 'Illegal filter type, must be string or regex';
36515         }
36516         this.filterBy(f, null, startNode);
36517         },
36518
36519     /**
36520      * Filter by a function. The passed function will be called with each
36521      * node in the tree (or from the startNode). If the function returns true, the node is kept
36522      * otherwise it is filtered. If a node is filtered, its children are also filtered.
36523      * @param {Function} fn The filter function
36524      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
36525      */
36526     filterBy : function(fn, scope, startNode){
36527         startNode = startNode || this.tree.root;
36528         if(this.autoClear){
36529             this.clear();
36530         }
36531         var af = this.filtered, rv = this.reverse;
36532         var f = function(n){
36533             if(n == startNode){
36534                 return true;
36535             }
36536             if(af[n.id]){
36537                 return false;
36538             }
36539             var m = fn.call(scope || n, n);
36540             if(!m || rv){
36541                 af[n.id] = n;
36542                 n.ui.hide();
36543                 return false;
36544             }
36545             return true;
36546         };
36547         startNode.cascade(f);
36548         if(this.remove){
36549            for(var id in af){
36550                if(typeof id != "function"){
36551                    var n = af[id];
36552                    if(n && n.parentNode){
36553                        n.parentNode.removeChild(n);
36554                    }
36555                }
36556            }
36557         }
36558     },
36559
36560     /**
36561      * Clears the current filter. Note: with the "remove" option
36562      * set a filter cannot be cleared.
36563      */
36564     clear : function(){
36565         var t = this.tree;
36566         var af = this.filtered;
36567         for(var id in af){
36568             if(typeof id != "function"){
36569                 var n = af[id];
36570                 if(n){
36571                     n.ui.show();
36572                 }
36573             }
36574         }
36575         this.filtered = {};
36576     }
36577 };
36578 /*
36579  * Based on:
36580  * Ext JS Library 1.1.1
36581  * Copyright(c) 2006-2007, Ext JS, LLC.
36582  *
36583  * Originally Released Under LGPL - original licence link has changed is not relivant.
36584  *
36585  * Fork - LGPL
36586  * <script type="text/javascript">
36587  */
36588  
36589
36590 /**
36591  * @class Roo.tree.TreeSorter
36592  * Provides sorting of nodes in a TreePanel
36593  * 
36594  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
36595  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
36596  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
36597  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
36598  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
36599  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
36600  * @constructor
36601  * @param {TreePanel} tree
36602  * @param {Object} config
36603  */
36604 Roo.tree.TreeSorter = function(tree, config){
36605     Roo.apply(this, config);
36606     tree.on("beforechildrenrendered", this.doSort, this);
36607     tree.on("append", this.updateSort, this);
36608     tree.on("insert", this.updateSort, this);
36609     
36610     var dsc = this.dir && this.dir.toLowerCase() == "desc";
36611     var p = this.property || "text";
36612     var sortType = this.sortType;
36613     var fs = this.folderSort;
36614     var cs = this.caseSensitive === true;
36615     var leafAttr = this.leafAttr || 'leaf';
36616
36617     this.sortFn = function(n1, n2){
36618         if(fs){
36619             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
36620                 return 1;
36621             }
36622             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
36623                 return -1;
36624             }
36625         }
36626         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
36627         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
36628         if(v1 < v2){
36629                         return dsc ? +1 : -1;
36630                 }else if(v1 > v2){
36631                         return dsc ? -1 : +1;
36632         }else{
36633                 return 0;
36634         }
36635     };
36636 };
36637
36638 Roo.tree.TreeSorter.prototype = {
36639     doSort : function(node){
36640         node.sort(this.sortFn);
36641     },
36642     
36643     compareNodes : function(n1, n2){
36644         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
36645     },
36646     
36647     updateSort : function(tree, node){
36648         if(node.childrenRendered){
36649             this.doSort.defer(1, this, [node]);
36650         }
36651     }
36652 };/*
36653  * Based on:
36654  * Ext JS Library 1.1.1
36655  * Copyright(c) 2006-2007, Ext JS, LLC.
36656  *
36657  * Originally Released Under LGPL - original licence link has changed is not relivant.
36658  *
36659  * Fork - LGPL
36660  * <script type="text/javascript">
36661  */
36662
36663 if(Roo.dd.DropZone){
36664     
36665 Roo.tree.TreeDropZone = function(tree, config){
36666     this.allowParentInsert = false;
36667     this.allowContainerDrop = false;
36668     this.appendOnly = false;
36669     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
36670     this.tree = tree;
36671     this.lastInsertClass = "x-tree-no-status";
36672     this.dragOverData = {};
36673 };
36674
36675 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
36676     ddGroup : "TreeDD",
36677     scroll:  true,
36678     
36679     expandDelay : 1000,
36680     
36681     expandNode : function(node){
36682         if(node.hasChildNodes() && !node.isExpanded()){
36683             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
36684         }
36685     },
36686     
36687     queueExpand : function(node){
36688         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
36689     },
36690     
36691     cancelExpand : function(){
36692         if(this.expandProcId){
36693             clearTimeout(this.expandProcId);
36694             this.expandProcId = false;
36695         }
36696     },
36697     
36698     isValidDropPoint : function(n, pt, dd, e, data){
36699         if(!n || !data){ return false; }
36700         var targetNode = n.node;
36701         var dropNode = data.node;
36702         // default drop rules
36703         if(!(targetNode && targetNode.isTarget && pt)){
36704             return false;
36705         }
36706         if(pt == "append" && targetNode.allowChildren === false){
36707             return false;
36708         }
36709         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
36710             return false;
36711         }
36712         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
36713             return false;
36714         }
36715         // reuse the object
36716         var overEvent = this.dragOverData;
36717         overEvent.tree = this.tree;
36718         overEvent.target = targetNode;
36719         overEvent.data = data;
36720         overEvent.point = pt;
36721         overEvent.source = dd;
36722         overEvent.rawEvent = e;
36723         overEvent.dropNode = dropNode;
36724         overEvent.cancel = false;  
36725         var result = this.tree.fireEvent("nodedragover", overEvent);
36726         return overEvent.cancel === false && result !== false;
36727     },
36728     
36729     getDropPoint : function(e, n, dd)
36730     {
36731         var tn = n.node;
36732         if(tn.isRoot){
36733             return tn.allowChildren !== false ? "append" : false; // always append for root
36734         }
36735         var dragEl = n.ddel;
36736         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
36737         var y = Roo.lib.Event.getPageY(e);
36738         //var noAppend = tn.allowChildren === false || tn.isLeaf();
36739         
36740         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
36741         var noAppend = tn.allowChildren === false;
36742         if(this.appendOnly || tn.parentNode.allowChildren === false){
36743             return noAppend ? false : "append";
36744         }
36745         var noBelow = false;
36746         if(!this.allowParentInsert){
36747             noBelow = tn.hasChildNodes() && tn.isExpanded();
36748         }
36749         var q = (b - t) / (noAppend ? 2 : 3);
36750         if(y >= t && y < (t + q)){
36751             return "above";
36752         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
36753             return "below";
36754         }else{
36755             return "append";
36756         }
36757     },
36758     
36759     onNodeEnter : function(n, dd, e, data)
36760     {
36761         this.cancelExpand();
36762     },
36763     
36764     onNodeOver : function(n, dd, e, data)
36765     {
36766        
36767         var pt = this.getDropPoint(e, n, dd);
36768         var node = n.node;
36769         
36770         // auto node expand check
36771         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
36772             this.queueExpand(node);
36773         }else if(pt != "append"){
36774             this.cancelExpand();
36775         }
36776         
36777         // set the insert point style on the target node
36778         var returnCls = this.dropNotAllowed;
36779         if(this.isValidDropPoint(n, pt, dd, e, data)){
36780            if(pt){
36781                var el = n.ddel;
36782                var cls;
36783                if(pt == "above"){
36784                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
36785                    cls = "x-tree-drag-insert-above";
36786                }else if(pt == "below"){
36787                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
36788                    cls = "x-tree-drag-insert-below";
36789                }else{
36790                    returnCls = "x-tree-drop-ok-append";
36791                    cls = "x-tree-drag-append";
36792                }
36793                if(this.lastInsertClass != cls){
36794                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
36795                    this.lastInsertClass = cls;
36796                }
36797            }
36798        }
36799        return returnCls;
36800     },
36801     
36802     onNodeOut : function(n, dd, e, data){
36803         
36804         this.cancelExpand();
36805         this.removeDropIndicators(n);
36806     },
36807     
36808     onNodeDrop : function(n, dd, e, data){
36809         var point = this.getDropPoint(e, n, dd);
36810         var targetNode = n.node;
36811         targetNode.ui.startDrop();
36812         if(!this.isValidDropPoint(n, point, dd, e, data)){
36813             targetNode.ui.endDrop();
36814             return false;
36815         }
36816         // first try to find the drop node
36817         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
36818         var dropEvent = {
36819             tree : this.tree,
36820             target: targetNode,
36821             data: data,
36822             point: point,
36823             source: dd,
36824             rawEvent: e,
36825             dropNode: dropNode,
36826             cancel: !dropNode   
36827         };
36828         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
36829         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
36830             targetNode.ui.endDrop();
36831             return false;
36832         }
36833         // allow target changing
36834         targetNode = dropEvent.target;
36835         if(point == "append" && !targetNode.isExpanded()){
36836             targetNode.expand(false, null, function(){
36837                 this.completeDrop(dropEvent);
36838             }.createDelegate(this));
36839         }else{
36840             this.completeDrop(dropEvent);
36841         }
36842         return true;
36843     },
36844     
36845     completeDrop : function(de){
36846         var ns = de.dropNode, p = de.point, t = de.target;
36847         if(!(ns instanceof Array)){
36848             ns = [ns];
36849         }
36850         var n;
36851         for(var i = 0, len = ns.length; i < len; i++){
36852             n = ns[i];
36853             if(p == "above"){
36854                 t.parentNode.insertBefore(n, t);
36855             }else if(p == "below"){
36856                 t.parentNode.insertBefore(n, t.nextSibling);
36857             }else{
36858                 t.appendChild(n);
36859             }
36860         }
36861         n.ui.focus();
36862         if(this.tree.hlDrop){
36863             n.ui.highlight();
36864         }
36865         t.ui.endDrop();
36866         this.tree.fireEvent("nodedrop", de);
36867     },
36868     
36869     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
36870         if(this.tree.hlDrop){
36871             dropNode.ui.focus();
36872             dropNode.ui.highlight();
36873         }
36874         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
36875     },
36876     
36877     getTree : function(){
36878         return this.tree;
36879     },
36880     
36881     removeDropIndicators : function(n){
36882         if(n && n.ddel){
36883             var el = n.ddel;
36884             Roo.fly(el).removeClass([
36885                     "x-tree-drag-insert-above",
36886                     "x-tree-drag-insert-below",
36887                     "x-tree-drag-append"]);
36888             this.lastInsertClass = "_noclass";
36889         }
36890     },
36891     
36892     beforeDragDrop : function(target, e, id){
36893         this.cancelExpand();
36894         return true;
36895     },
36896     
36897     afterRepair : function(data){
36898         if(data && Roo.enableFx){
36899             data.node.ui.highlight();
36900         }
36901         this.hideProxy();
36902     } 
36903     
36904 });
36905
36906 }
36907 /*
36908  * Based on:
36909  * Ext JS Library 1.1.1
36910  * Copyright(c) 2006-2007, Ext JS, LLC.
36911  *
36912  * Originally Released Under LGPL - original licence link has changed is not relivant.
36913  *
36914  * Fork - LGPL
36915  * <script type="text/javascript">
36916  */
36917  
36918
36919 if(Roo.dd.DragZone){
36920 Roo.tree.TreeDragZone = function(tree, config){
36921     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
36922     this.tree = tree;
36923 };
36924
36925 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
36926     ddGroup : "TreeDD",
36927    
36928     onBeforeDrag : function(data, e){
36929         var n = data.node;
36930         return n && n.draggable && !n.disabled;
36931     },
36932      
36933     
36934     onInitDrag : function(e){
36935         var data = this.dragData;
36936         this.tree.getSelectionModel().select(data.node);
36937         this.proxy.update("");
36938         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
36939         this.tree.fireEvent("startdrag", this.tree, data.node, e);
36940     },
36941     
36942     getRepairXY : function(e, data){
36943         return data.node.ui.getDDRepairXY();
36944     },
36945     
36946     onEndDrag : function(data, e){
36947         this.tree.fireEvent("enddrag", this.tree, data.node, e);
36948         
36949         
36950     },
36951     
36952     onValidDrop : function(dd, e, id){
36953         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
36954         this.hideProxy();
36955     },
36956     
36957     beforeInvalidDrop : function(e, id){
36958         // this scrolls the original position back into view
36959         var sm = this.tree.getSelectionModel();
36960         sm.clearSelections();
36961         sm.select(this.dragData.node);
36962     }
36963 });
36964 }/*
36965  * Based on:
36966  * Ext JS Library 1.1.1
36967  * Copyright(c) 2006-2007, Ext JS, LLC.
36968  *
36969  * Originally Released Under LGPL - original licence link has changed is not relivant.
36970  *
36971  * Fork - LGPL
36972  * <script type="text/javascript">
36973  */
36974 /**
36975  * @class Roo.tree.TreeEditor
36976  * @extends Roo.Editor
36977  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
36978  * as the editor field.
36979  * @constructor
36980  * @param {Object} config (used to be the tree panel.)
36981  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
36982  * 
36983  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
36984  * @cfg {Roo.form.TextField|Object} field The field configuration
36985  *
36986  * 
36987  */
36988 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
36989     var tree = config;
36990     var field;
36991     if (oldconfig) { // old style..
36992         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
36993     } else {
36994         // new style..
36995         tree = config.tree;
36996         config.field = config.field  || {};
36997         config.field.xtype = 'TextField';
36998         field = Roo.factory(config.field, Roo.form);
36999     }
37000     config = config || {};
37001     
37002     
37003     this.addEvents({
37004         /**
37005          * @event beforenodeedit
37006          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
37007          * false from the handler of this event.
37008          * @param {Editor} this
37009          * @param {Roo.tree.Node} node 
37010          */
37011         "beforenodeedit" : true
37012     });
37013     
37014     //Roo.log(config);
37015     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
37016
37017     this.tree = tree;
37018
37019     tree.on('beforeclick', this.beforeNodeClick, this);
37020     tree.getTreeEl().on('mousedown', this.hide, this);
37021     this.on('complete', this.updateNode, this);
37022     this.on('beforestartedit', this.fitToTree, this);
37023     this.on('startedit', this.bindScroll, this, {delay:10});
37024     this.on('specialkey', this.onSpecialKey, this);
37025 };
37026
37027 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
37028     /**
37029      * @cfg {String} alignment
37030      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
37031      */
37032     alignment: "l-l",
37033     // inherit
37034     autoSize: false,
37035     /**
37036      * @cfg {Boolean} hideEl
37037      * True to hide the bound element while the editor is displayed (defaults to false)
37038      */
37039     hideEl : false,
37040     /**
37041      * @cfg {String} cls
37042      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
37043      */
37044     cls: "x-small-editor x-tree-editor",
37045     /**
37046      * @cfg {Boolean} shim
37047      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
37048      */
37049     shim:false,
37050     // inherit
37051     shadow:"frame",
37052     /**
37053      * @cfg {Number} maxWidth
37054      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
37055      * the containing tree element's size, it will be automatically limited for you to the container width, taking
37056      * scroll and client offsets into account prior to each edit.
37057      */
37058     maxWidth: 250,
37059
37060     editDelay : 350,
37061
37062     // private
37063     fitToTree : function(ed, el){
37064         var td = this.tree.getTreeEl().dom, nd = el.dom;
37065         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
37066             td.scrollLeft = nd.offsetLeft;
37067         }
37068         var w = Math.min(
37069                 this.maxWidth,
37070                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
37071         this.setSize(w, '');
37072         
37073         return this.fireEvent('beforenodeedit', this, this.editNode);
37074         
37075     },
37076
37077     // private
37078     triggerEdit : function(node){
37079         this.completeEdit();
37080         this.editNode = node;
37081         this.startEdit(node.ui.textNode, node.text);
37082     },
37083
37084     // private
37085     bindScroll : function(){
37086         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
37087     },
37088
37089     // private
37090     beforeNodeClick : function(node, e){
37091         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
37092         this.lastClick = new Date();
37093         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
37094             e.stopEvent();
37095             this.triggerEdit(node);
37096             return false;
37097         }
37098         return true;
37099     },
37100
37101     // private
37102     updateNode : function(ed, value){
37103         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
37104         this.editNode.setText(value);
37105     },
37106
37107     // private
37108     onHide : function(){
37109         Roo.tree.TreeEditor.superclass.onHide.call(this);
37110         if(this.editNode){
37111             this.editNode.ui.focus();
37112         }
37113     },
37114
37115     // private
37116     onSpecialKey : function(field, e){
37117         var k = e.getKey();
37118         if(k == e.ESC){
37119             e.stopEvent();
37120             this.cancelEdit();
37121         }else if(k == e.ENTER && !e.hasModifier()){
37122             e.stopEvent();
37123             this.completeEdit();
37124         }
37125     }
37126 });//<Script type="text/javascript">
37127 /*
37128  * Based on:
37129  * Ext JS Library 1.1.1
37130  * Copyright(c) 2006-2007, Ext JS, LLC.
37131  *
37132  * Originally Released Under LGPL - original licence link has changed is not relivant.
37133  *
37134  * Fork - LGPL
37135  * <script type="text/javascript">
37136  */
37137  
37138 /**
37139  * Not documented??? - probably should be...
37140  */
37141
37142 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
37143     //focus: Roo.emptyFn, // prevent odd scrolling behavior
37144     
37145     renderElements : function(n, a, targetNode, bulkRender){
37146         //consel.log("renderElements?");
37147         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
37148
37149         var t = n.getOwnerTree();
37150         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
37151         
37152         var cols = t.columns;
37153         var bw = t.borderWidth;
37154         var c = cols[0];
37155         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
37156          var cb = typeof a.checked == "boolean";
37157         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
37158         var colcls = 'x-t-' + tid + '-c0';
37159         var buf = [
37160             '<li class="x-tree-node">',
37161             
37162                 
37163                 '<div class="x-tree-node-el ', a.cls,'">',
37164                     // extran...
37165                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
37166                 
37167                 
37168                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
37169                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
37170                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
37171                            (a.icon ? ' x-tree-node-inline-icon' : ''),
37172                            (a.iconCls ? ' '+a.iconCls : ''),
37173                            '" unselectable="on" />',
37174                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
37175                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
37176                              
37177                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
37178                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
37179                             '<span unselectable="on" qtip="' + tx + '">',
37180                              tx,
37181                              '</span></a>' ,
37182                     '</div>',
37183                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
37184                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
37185                  ];
37186         for(var i = 1, len = cols.length; i < len; i++){
37187             c = cols[i];
37188             colcls = 'x-t-' + tid + '-c' +i;
37189             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
37190             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
37191                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
37192                       "</div>");
37193          }
37194          
37195          buf.push(
37196             '</a>',
37197             '<div class="x-clear"></div></div>',
37198             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
37199             "</li>");
37200         
37201         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
37202             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
37203                                 n.nextSibling.ui.getEl(), buf.join(""));
37204         }else{
37205             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
37206         }
37207         var el = this.wrap.firstChild;
37208         this.elRow = el;
37209         this.elNode = el.firstChild;
37210         this.ranchor = el.childNodes[1];
37211         this.ctNode = this.wrap.childNodes[1];
37212         var cs = el.firstChild.childNodes;
37213         this.indentNode = cs[0];
37214         this.ecNode = cs[1];
37215         this.iconNode = cs[2];
37216         var index = 3;
37217         if(cb){
37218             this.checkbox = cs[3];
37219             index++;
37220         }
37221         this.anchor = cs[index];
37222         
37223         this.textNode = cs[index].firstChild;
37224         
37225         //el.on("click", this.onClick, this);
37226         //el.on("dblclick", this.onDblClick, this);
37227         
37228         
37229        // console.log(this);
37230     },
37231     initEvents : function(){
37232         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
37233         
37234             
37235         var a = this.ranchor;
37236
37237         var el = Roo.get(a);
37238
37239         if(Roo.isOpera){ // opera render bug ignores the CSS
37240             el.setStyle("text-decoration", "none");
37241         }
37242
37243         el.on("click", this.onClick, this);
37244         el.on("dblclick", this.onDblClick, this);
37245         el.on("contextmenu", this.onContextMenu, this);
37246         
37247     },
37248     
37249     /*onSelectedChange : function(state){
37250         if(state){
37251             this.focus();
37252             this.addClass("x-tree-selected");
37253         }else{
37254             //this.blur();
37255             this.removeClass("x-tree-selected");
37256         }
37257     },*/
37258     addClass : function(cls){
37259         if(this.elRow){
37260             Roo.fly(this.elRow).addClass(cls);
37261         }
37262         
37263     },
37264     
37265     
37266     removeClass : function(cls){
37267         if(this.elRow){
37268             Roo.fly(this.elRow).removeClass(cls);
37269         }
37270     }
37271
37272     
37273     
37274 });//<Script type="text/javascript">
37275
37276 /*
37277  * Based on:
37278  * Ext JS Library 1.1.1
37279  * Copyright(c) 2006-2007, Ext JS, LLC.
37280  *
37281  * Originally Released Under LGPL - original licence link has changed is not relivant.
37282  *
37283  * Fork - LGPL
37284  * <script type="text/javascript">
37285  */
37286  
37287
37288 /**
37289  * @class Roo.tree.ColumnTree
37290  * @extends Roo.data.TreePanel
37291  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
37292  * @cfg {int} borderWidth  compined right/left border allowance
37293  * @constructor
37294  * @param {String/HTMLElement/Element} el The container element
37295  * @param {Object} config
37296  */
37297 Roo.tree.ColumnTree =  function(el, config)
37298 {
37299    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
37300    this.addEvents({
37301         /**
37302         * @event resize
37303         * Fire this event on a container when it resizes
37304         * @param {int} w Width
37305         * @param {int} h Height
37306         */
37307        "resize" : true
37308     });
37309     this.on('resize', this.onResize, this);
37310 };
37311
37312 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
37313     //lines:false,
37314     
37315     
37316     borderWidth: Roo.isBorderBox ? 0 : 2, 
37317     headEls : false,
37318     
37319     render : function(){
37320         // add the header.....
37321        
37322         Roo.tree.ColumnTree.superclass.render.apply(this);
37323         
37324         this.el.addClass('x-column-tree');
37325         
37326         this.headers = this.el.createChild(
37327             {cls:'x-tree-headers'},this.innerCt.dom);
37328    
37329         var cols = this.columns, c;
37330         var totalWidth = 0;
37331         this.headEls = [];
37332         var  len = cols.length;
37333         for(var i = 0; i < len; i++){
37334              c = cols[i];
37335              totalWidth += c.width;
37336             this.headEls.push(this.headers.createChild({
37337                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
37338                  cn: {
37339                      cls:'x-tree-hd-text',
37340                      html: c.header
37341                  },
37342                  style:'width:'+(c.width-this.borderWidth)+'px;'
37343              }));
37344         }
37345         this.headers.createChild({cls:'x-clear'});
37346         // prevent floats from wrapping when clipped
37347         this.headers.setWidth(totalWidth);
37348         //this.innerCt.setWidth(totalWidth);
37349         this.innerCt.setStyle({ overflow: 'auto' });
37350         this.onResize(this.width, this.height);
37351              
37352         
37353     },
37354     onResize : function(w,h)
37355     {
37356         this.height = h;
37357         this.width = w;
37358         // resize cols..
37359         this.innerCt.setWidth(this.width);
37360         this.innerCt.setHeight(this.height-20);
37361         
37362         // headers...
37363         var cols = this.columns, c;
37364         var totalWidth = 0;
37365         var expEl = false;
37366         var len = cols.length;
37367         for(var i = 0; i < len; i++){
37368             c = cols[i];
37369             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
37370                 // it's the expander..
37371                 expEl  = this.headEls[i];
37372                 continue;
37373             }
37374             totalWidth += c.width;
37375             
37376         }
37377         if (expEl) {
37378             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
37379         }
37380         this.headers.setWidth(w-20);
37381
37382         
37383         
37384         
37385     }
37386 });
37387 /*
37388  * Based on:
37389  * Ext JS Library 1.1.1
37390  * Copyright(c) 2006-2007, Ext JS, LLC.
37391  *
37392  * Originally Released Under LGPL - original licence link has changed is not relivant.
37393  *
37394  * Fork - LGPL
37395  * <script type="text/javascript">
37396  */
37397  
37398 /**
37399  * @class Roo.menu.Menu
37400  * @extends Roo.util.Observable
37401  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
37402  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
37403  * @constructor
37404  * Creates a new Menu
37405  * @param {Object} config Configuration options
37406  */
37407 Roo.menu.Menu = function(config){
37408     
37409     Roo.menu.Menu.superclass.constructor.call(this, config);
37410     
37411     this.id = this.id || Roo.id();
37412     this.addEvents({
37413         /**
37414          * @event beforeshow
37415          * Fires before this menu is displayed
37416          * @param {Roo.menu.Menu} this
37417          */
37418         beforeshow : true,
37419         /**
37420          * @event beforehide
37421          * Fires before this menu is hidden
37422          * @param {Roo.menu.Menu} this
37423          */
37424         beforehide : true,
37425         /**
37426          * @event show
37427          * Fires after this menu is displayed
37428          * @param {Roo.menu.Menu} this
37429          */
37430         show : true,
37431         /**
37432          * @event hide
37433          * Fires after this menu is hidden
37434          * @param {Roo.menu.Menu} this
37435          */
37436         hide : true,
37437         /**
37438          * @event click
37439          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
37440          * @param {Roo.menu.Menu} this
37441          * @param {Roo.menu.Item} menuItem The menu item that was clicked
37442          * @param {Roo.EventObject} e
37443          */
37444         click : true,
37445         /**
37446          * @event mouseover
37447          * Fires when the mouse is hovering over this menu
37448          * @param {Roo.menu.Menu} this
37449          * @param {Roo.EventObject} e
37450          * @param {Roo.menu.Item} menuItem The menu item that was clicked
37451          */
37452         mouseover : true,
37453         /**
37454          * @event mouseout
37455          * Fires when the mouse exits this menu
37456          * @param {Roo.menu.Menu} this
37457          * @param {Roo.EventObject} e
37458          * @param {Roo.menu.Item} menuItem The menu item that was clicked
37459          */
37460         mouseout : true,
37461         /**
37462          * @event itemclick
37463          * Fires when a menu item contained in this menu is clicked
37464          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
37465          * @param {Roo.EventObject} e
37466          */
37467         itemclick: true
37468     });
37469     if (this.registerMenu) {
37470         Roo.menu.MenuMgr.register(this);
37471     }
37472     
37473     var mis = this.items;
37474     this.items = new Roo.util.MixedCollection();
37475     if(mis){
37476         this.add.apply(this, mis);
37477     }
37478 };
37479
37480 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
37481     /**
37482      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
37483      */
37484     minWidth : 120,
37485     /**
37486      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
37487      * for bottom-right shadow (defaults to "sides")
37488      */
37489     shadow : "sides",
37490     /**
37491      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
37492      * this menu (defaults to "tl-tr?")
37493      */
37494     subMenuAlign : "tl-tr?",
37495     /**
37496      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
37497      * relative to its element of origin (defaults to "tl-bl?")
37498      */
37499     defaultAlign : "tl-bl?",
37500     /**
37501      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
37502      */
37503     allowOtherMenus : false,
37504     /**
37505      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
37506      */
37507     registerMenu : true,
37508
37509     hidden:true,
37510
37511     // private
37512     render : function(){
37513         if(this.el){
37514             return;
37515         }
37516         var el = this.el = new Roo.Layer({
37517             cls: "x-menu",
37518             shadow:this.shadow,
37519             constrain: false,
37520             parentEl: this.parentEl || document.body,
37521             zindex:15000
37522         });
37523
37524         this.keyNav = new Roo.menu.MenuNav(this);
37525
37526         if(this.plain){
37527             el.addClass("x-menu-plain");
37528         }
37529         if(this.cls){
37530             el.addClass(this.cls);
37531         }
37532         // generic focus element
37533         this.focusEl = el.createChild({
37534             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
37535         });
37536         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
37537         //disabling touch- as it's causing issues ..
37538         //ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
37539         ul.on('click'   , this.onClick, this);
37540         
37541         
37542         ul.on("mouseover", this.onMouseOver, this);
37543         ul.on("mouseout", this.onMouseOut, this);
37544         this.items.each(function(item){
37545             if (item.hidden) {
37546                 return;
37547             }
37548             
37549             var li = document.createElement("li");
37550             li.className = "x-menu-list-item";
37551             ul.dom.appendChild(li);
37552             item.render(li, this);
37553         }, this);
37554         this.ul = ul;
37555         this.autoWidth();
37556     },
37557
37558     // private
37559     autoWidth : function(){
37560         var el = this.el, ul = this.ul;
37561         if(!el){
37562             return;
37563         }
37564         var w = this.width;
37565         if(w){
37566             el.setWidth(w);
37567         }else if(Roo.isIE){
37568             el.setWidth(this.minWidth);
37569             var t = el.dom.offsetWidth; // force recalc
37570             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
37571         }
37572     },
37573
37574     // private
37575     delayAutoWidth : function(){
37576         if(this.rendered){
37577             if(!this.awTask){
37578                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
37579             }
37580             this.awTask.delay(20);
37581         }
37582     },
37583
37584     // private
37585     findTargetItem : function(e){
37586         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
37587         if(t && t.menuItemId){
37588             return this.items.get(t.menuItemId);
37589         }
37590     },
37591
37592     // private
37593     onClick : function(e){
37594         Roo.log("menu.onClick");
37595         var t = this.findTargetItem(e);
37596         if(!t){
37597             return;
37598         }
37599         Roo.log(e);
37600         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
37601             if(t == this.activeItem && t.shouldDeactivate(e)){
37602                 this.activeItem.deactivate();
37603                 delete this.activeItem;
37604                 return;
37605             }
37606             if(t.canActivate){
37607                 this.setActiveItem(t, true);
37608             }
37609             return;
37610             
37611             
37612         }
37613         
37614         t.onClick(e);
37615         this.fireEvent("click", this, t, e);
37616     },
37617
37618     // private
37619     setActiveItem : function(item, autoExpand){
37620         if(item != this.activeItem){
37621             if(this.activeItem){
37622                 this.activeItem.deactivate();
37623             }
37624             this.activeItem = item;
37625             item.activate(autoExpand);
37626         }else if(autoExpand){
37627             item.expandMenu();
37628         }
37629     },
37630
37631     // private
37632     tryActivate : function(start, step){
37633         var items = this.items;
37634         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
37635             var item = items.get(i);
37636             if(!item.disabled && item.canActivate){
37637                 this.setActiveItem(item, false);
37638                 return item;
37639             }
37640         }
37641         return false;
37642     },
37643
37644     // private
37645     onMouseOver : function(e){
37646         var t;
37647         if(t = this.findTargetItem(e)){
37648             if(t.canActivate && !t.disabled){
37649                 this.setActiveItem(t, true);
37650             }
37651         }
37652         this.fireEvent("mouseover", this, e, t);
37653     },
37654
37655     // private
37656     onMouseOut : function(e){
37657         var t;
37658         if(t = this.findTargetItem(e)){
37659             if(t == this.activeItem && t.shouldDeactivate(e)){
37660                 this.activeItem.deactivate();
37661                 delete this.activeItem;
37662             }
37663         }
37664         this.fireEvent("mouseout", this, e, t);
37665     },
37666
37667     /**
37668      * Read-only.  Returns true if the menu is currently displayed, else false.
37669      * @type Boolean
37670      */
37671     isVisible : function(){
37672         return this.el && !this.hidden;
37673     },
37674
37675     /**
37676      * Displays this menu relative to another element
37677      * @param {String/HTMLElement/Roo.Element} element The element to align to
37678      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
37679      * the element (defaults to this.defaultAlign)
37680      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
37681      */
37682     show : function(el, pos, parentMenu){
37683         this.parentMenu = parentMenu;
37684         if(!this.el){
37685             this.render();
37686         }
37687         this.fireEvent("beforeshow", this);
37688         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
37689     },
37690
37691     /**
37692      * Displays this menu at a specific xy position
37693      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
37694      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
37695      */
37696     showAt : function(xy, parentMenu, /* private: */_e){
37697         this.parentMenu = parentMenu;
37698         if(!this.el){
37699             this.render();
37700         }
37701         if(_e !== false){
37702             this.fireEvent("beforeshow", this);
37703             xy = this.el.adjustForConstraints(xy);
37704         }
37705         this.el.setXY(xy);
37706         this.el.show();
37707         this.hidden = false;
37708         this.focus();
37709         this.fireEvent("show", this);
37710     },
37711
37712     focus : function(){
37713         if(!this.hidden){
37714             this.doFocus.defer(50, this);
37715         }
37716     },
37717
37718     doFocus : function(){
37719         if(!this.hidden){
37720             this.focusEl.focus();
37721         }
37722     },
37723
37724     /**
37725      * Hides this menu and optionally all parent menus
37726      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
37727      */
37728     hide : function(deep){
37729         if(this.el && this.isVisible()){
37730             this.fireEvent("beforehide", this);
37731             if(this.activeItem){
37732                 this.activeItem.deactivate();
37733                 this.activeItem = null;
37734             }
37735             this.el.hide();
37736             this.hidden = true;
37737             this.fireEvent("hide", this);
37738         }
37739         if(deep === true && this.parentMenu){
37740             this.parentMenu.hide(true);
37741         }
37742     },
37743
37744     /**
37745      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
37746      * Any of the following are valid:
37747      * <ul>
37748      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
37749      * <li>An HTMLElement object which will be converted to a menu item</li>
37750      * <li>A menu item config object that will be created as a new menu item</li>
37751      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
37752      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
37753      * </ul>
37754      * Usage:
37755      * <pre><code>
37756 // Create the menu
37757 var menu = new Roo.menu.Menu();
37758
37759 // Create a menu item to add by reference
37760 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
37761
37762 // Add a bunch of items at once using different methods.
37763 // Only the last item added will be returned.
37764 var item = menu.add(
37765     menuItem,                // add existing item by ref
37766     'Dynamic Item',          // new TextItem
37767     '-',                     // new separator
37768     { text: 'Config Item' }  // new item by config
37769 );
37770 </code></pre>
37771      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
37772      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
37773      */
37774     add : function(){
37775         var a = arguments, l = a.length, item;
37776         for(var i = 0; i < l; i++){
37777             var el = a[i];
37778             if ((typeof(el) == "object") && el.xtype && el.xns) {
37779                 el = Roo.factory(el, Roo.menu);
37780             }
37781             
37782             if(el.render){ // some kind of Item
37783                 item = this.addItem(el);
37784             }else if(typeof el == "string"){ // string
37785                 if(el == "separator" || el == "-"){
37786                     item = this.addSeparator();
37787                 }else{
37788                     item = this.addText(el);
37789                 }
37790             }else if(el.tagName || el.el){ // element
37791                 item = this.addElement(el);
37792             }else if(typeof el == "object"){ // must be menu item config?
37793                 item = this.addMenuItem(el);
37794             }
37795         }
37796         return item;
37797     },
37798
37799     /**
37800      * Returns this menu's underlying {@link Roo.Element} object
37801      * @return {Roo.Element} The element
37802      */
37803     getEl : function(){
37804         if(!this.el){
37805             this.render();
37806         }
37807         return this.el;
37808     },
37809
37810     /**
37811      * Adds a separator bar to the menu
37812      * @return {Roo.menu.Item} The menu item that was added
37813      */
37814     addSeparator : function(){
37815         return this.addItem(new Roo.menu.Separator());
37816     },
37817
37818     /**
37819      * Adds an {@link Roo.Element} object to the menu
37820      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
37821      * @return {Roo.menu.Item} The menu item that was added
37822      */
37823     addElement : function(el){
37824         return this.addItem(new Roo.menu.BaseItem(el));
37825     },
37826
37827     /**
37828      * Adds an existing object based on {@link Roo.menu.Item} to the menu
37829      * @param {Roo.menu.Item} item The menu item to add
37830      * @return {Roo.menu.Item} The menu item that was added
37831      */
37832     addItem : function(item){
37833         this.items.add(item);
37834         if(this.ul){
37835             var li = document.createElement("li");
37836             li.className = "x-menu-list-item";
37837             this.ul.dom.appendChild(li);
37838             item.render(li, this);
37839             this.delayAutoWidth();
37840         }
37841         return item;
37842     },
37843
37844     /**
37845      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
37846      * @param {Object} config A MenuItem config object
37847      * @return {Roo.menu.Item} The menu item that was added
37848      */
37849     addMenuItem : function(config){
37850         if(!(config instanceof Roo.menu.Item)){
37851             if(typeof config.checked == "boolean"){ // must be check menu item config?
37852                 config = new Roo.menu.CheckItem(config);
37853             }else{
37854                 config = new Roo.menu.Item(config);
37855             }
37856         }
37857         return this.addItem(config);
37858     },
37859
37860     /**
37861      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
37862      * @param {String} text The text to display in the menu item
37863      * @return {Roo.menu.Item} The menu item that was added
37864      */
37865     addText : function(text){
37866         return this.addItem(new Roo.menu.TextItem({ text : text }));
37867     },
37868
37869     /**
37870      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
37871      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
37872      * @param {Roo.menu.Item} item The menu item to add
37873      * @return {Roo.menu.Item} The menu item that was added
37874      */
37875     insert : function(index, item){
37876         this.items.insert(index, item);
37877         if(this.ul){
37878             var li = document.createElement("li");
37879             li.className = "x-menu-list-item";
37880             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
37881             item.render(li, this);
37882             this.delayAutoWidth();
37883         }
37884         return item;
37885     },
37886
37887     /**
37888      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
37889      * @param {Roo.menu.Item} item The menu item to remove
37890      */
37891     remove : function(item){
37892         this.items.removeKey(item.id);
37893         item.destroy();
37894     },
37895
37896     /**
37897      * Removes and destroys all items in the menu
37898      */
37899     removeAll : function(){
37900         var f;
37901         while(f = this.items.first()){
37902             this.remove(f);
37903         }
37904     }
37905 });
37906
37907 // MenuNav is a private utility class used internally by the Menu
37908 Roo.menu.MenuNav = function(menu){
37909     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
37910     this.scope = this.menu = menu;
37911 };
37912
37913 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
37914     doRelay : function(e, h){
37915         var k = e.getKey();
37916         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
37917             this.menu.tryActivate(0, 1);
37918             return false;
37919         }
37920         return h.call(this.scope || this, e, this.menu);
37921     },
37922
37923     up : function(e, m){
37924         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
37925             m.tryActivate(m.items.length-1, -1);
37926         }
37927     },
37928
37929     down : function(e, m){
37930         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
37931             m.tryActivate(0, 1);
37932         }
37933     },
37934
37935     right : function(e, m){
37936         if(m.activeItem){
37937             m.activeItem.expandMenu(true);
37938         }
37939     },
37940
37941     left : function(e, m){
37942         m.hide();
37943         if(m.parentMenu && m.parentMenu.activeItem){
37944             m.parentMenu.activeItem.activate();
37945         }
37946     },
37947
37948     enter : function(e, m){
37949         if(m.activeItem){
37950             e.stopPropagation();
37951             m.activeItem.onClick(e);
37952             m.fireEvent("click", this, m.activeItem);
37953             return true;
37954         }
37955     }
37956 });/*
37957  * Based on:
37958  * Ext JS Library 1.1.1
37959  * Copyright(c) 2006-2007, Ext JS, LLC.
37960  *
37961  * Originally Released Under LGPL - original licence link has changed is not relivant.
37962  *
37963  * Fork - LGPL
37964  * <script type="text/javascript">
37965  */
37966  
37967 /**
37968  * @class Roo.menu.MenuMgr
37969  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
37970  * @singleton
37971  */
37972 Roo.menu.MenuMgr = function(){
37973    var menus, active, groups = {}, attached = false, lastShow = new Date();
37974
37975    // private - called when first menu is created
37976    function init(){
37977        menus = {};
37978        active = new Roo.util.MixedCollection();
37979        Roo.get(document).addKeyListener(27, function(){
37980            if(active.length > 0){
37981                hideAll();
37982            }
37983        });
37984    }
37985
37986    // private
37987    function hideAll(){
37988        if(active && active.length > 0){
37989            var c = active.clone();
37990            c.each(function(m){
37991                m.hide();
37992            });
37993        }
37994    }
37995
37996    // private
37997    function onHide(m){
37998        active.remove(m);
37999        if(active.length < 1){
38000            Roo.get(document).un("mousedown", onMouseDown);
38001            attached = false;
38002        }
38003    }
38004
38005    // private
38006    function onShow(m){
38007        var last = active.last();
38008        lastShow = new Date();
38009        active.add(m);
38010        if(!attached){
38011            Roo.get(document).on("mousedown", onMouseDown);
38012            attached = true;
38013        }
38014        if(m.parentMenu){
38015           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
38016           m.parentMenu.activeChild = m;
38017        }else if(last && last.isVisible()){
38018           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
38019        }
38020    }
38021
38022    // private
38023    function onBeforeHide(m){
38024        if(m.activeChild){
38025            m.activeChild.hide();
38026        }
38027        if(m.autoHideTimer){
38028            clearTimeout(m.autoHideTimer);
38029            delete m.autoHideTimer;
38030        }
38031    }
38032
38033    // private
38034    function onBeforeShow(m){
38035        var pm = m.parentMenu;
38036        if(!pm && !m.allowOtherMenus){
38037            hideAll();
38038        }else if(pm && pm.activeChild && active != m){
38039            pm.activeChild.hide();
38040        }
38041    }
38042
38043    // private
38044    function onMouseDown(e){
38045        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
38046            hideAll();
38047        }
38048    }
38049
38050    // private
38051    function onBeforeCheck(mi, state){
38052        if(state){
38053            var g = groups[mi.group];
38054            for(var i = 0, l = g.length; i < l; i++){
38055                if(g[i] != mi){
38056                    g[i].setChecked(false);
38057                }
38058            }
38059        }
38060    }
38061
38062    return {
38063
38064        /**
38065         * Hides all menus that are currently visible
38066         */
38067        hideAll : function(){
38068             hideAll();  
38069        },
38070
38071        // private
38072        register : function(menu){
38073            if(!menus){
38074                init();
38075            }
38076            menus[menu.id] = menu;
38077            menu.on("beforehide", onBeforeHide);
38078            menu.on("hide", onHide);
38079            menu.on("beforeshow", onBeforeShow);
38080            menu.on("show", onShow);
38081            var g = menu.group;
38082            if(g && menu.events["checkchange"]){
38083                if(!groups[g]){
38084                    groups[g] = [];
38085                }
38086                groups[g].push(menu);
38087                menu.on("checkchange", onCheck);
38088            }
38089        },
38090
38091         /**
38092          * Returns a {@link Roo.menu.Menu} object
38093          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
38094          * be used to generate and return a new Menu instance.
38095          */
38096        get : function(menu){
38097            if(typeof menu == "string"){ // menu id
38098                return menus[menu];
38099            }else if(menu.events){  // menu instance
38100                return menu;
38101            }else if(typeof menu.length == 'number'){ // array of menu items?
38102                return new Roo.menu.Menu({items:menu});
38103            }else{ // otherwise, must be a config
38104                return new Roo.menu.Menu(menu);
38105            }
38106        },
38107
38108        // private
38109        unregister : function(menu){
38110            delete menus[menu.id];
38111            menu.un("beforehide", onBeforeHide);
38112            menu.un("hide", onHide);
38113            menu.un("beforeshow", onBeforeShow);
38114            menu.un("show", onShow);
38115            var g = menu.group;
38116            if(g && menu.events["checkchange"]){
38117                groups[g].remove(menu);
38118                menu.un("checkchange", onCheck);
38119            }
38120        },
38121
38122        // private
38123        registerCheckable : function(menuItem){
38124            var g = menuItem.group;
38125            if(g){
38126                if(!groups[g]){
38127                    groups[g] = [];
38128                }
38129                groups[g].push(menuItem);
38130                menuItem.on("beforecheckchange", onBeforeCheck);
38131            }
38132        },
38133
38134        // private
38135        unregisterCheckable : function(menuItem){
38136            var g = menuItem.group;
38137            if(g){
38138                groups[g].remove(menuItem);
38139                menuItem.un("beforecheckchange", onBeforeCheck);
38140            }
38141        }
38142    };
38143 }();/*
38144  * Based on:
38145  * Ext JS Library 1.1.1
38146  * Copyright(c) 2006-2007, Ext JS, LLC.
38147  *
38148  * Originally Released Under LGPL - original licence link has changed is not relivant.
38149  *
38150  * Fork - LGPL
38151  * <script type="text/javascript">
38152  */
38153  
38154
38155 /**
38156  * @class Roo.menu.BaseItem
38157  * @extends Roo.Component
38158  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
38159  * management and base configuration options shared by all menu components.
38160  * @constructor
38161  * Creates a new BaseItem
38162  * @param {Object} config Configuration options
38163  */
38164 Roo.menu.BaseItem = function(config){
38165     Roo.menu.BaseItem.superclass.constructor.call(this, config);
38166
38167     this.addEvents({
38168         /**
38169          * @event click
38170          * Fires when this item is clicked
38171          * @param {Roo.menu.BaseItem} this
38172          * @param {Roo.EventObject} e
38173          */
38174         click: true,
38175         /**
38176          * @event activate
38177          * Fires when this item is activated
38178          * @param {Roo.menu.BaseItem} this
38179          */
38180         activate : true,
38181         /**
38182          * @event deactivate
38183          * Fires when this item is deactivated
38184          * @param {Roo.menu.BaseItem} this
38185          */
38186         deactivate : true
38187     });
38188
38189     if(this.handler){
38190         this.on("click", this.handler, this.scope, true);
38191     }
38192 };
38193
38194 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
38195     /**
38196      * @cfg {Function} handler
38197      * A function that will handle the click event of this menu item (defaults to undefined)
38198      */
38199     /**
38200      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
38201      */
38202     canActivate : false,
38203     
38204      /**
38205      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
38206      */
38207     hidden: false,
38208     
38209     /**
38210      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
38211      */
38212     activeClass : "x-menu-item-active",
38213     /**
38214      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
38215      */
38216     hideOnClick : true,
38217     /**
38218      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
38219      */
38220     hideDelay : 100,
38221
38222     // private
38223     ctype: "Roo.menu.BaseItem",
38224
38225     // private
38226     actionMode : "container",
38227
38228     // private
38229     render : function(container, parentMenu){
38230         this.parentMenu = parentMenu;
38231         Roo.menu.BaseItem.superclass.render.call(this, container);
38232         this.container.menuItemId = this.id;
38233     },
38234
38235     // private
38236     onRender : function(container, position){
38237         this.el = Roo.get(this.el);
38238         container.dom.appendChild(this.el.dom);
38239     },
38240
38241     // private
38242     onClick : function(e){
38243         if(!this.disabled && this.fireEvent("click", this, e) !== false
38244                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
38245             this.handleClick(e);
38246         }else{
38247             e.stopEvent();
38248         }
38249     },
38250
38251     // private
38252     activate : function(){
38253         if(this.disabled){
38254             return false;
38255         }
38256         var li = this.container;
38257         li.addClass(this.activeClass);
38258         this.region = li.getRegion().adjust(2, 2, -2, -2);
38259         this.fireEvent("activate", this);
38260         return true;
38261     },
38262
38263     // private
38264     deactivate : function(){
38265         this.container.removeClass(this.activeClass);
38266         this.fireEvent("deactivate", this);
38267     },
38268
38269     // private
38270     shouldDeactivate : function(e){
38271         return !this.region || !this.region.contains(e.getPoint());
38272     },
38273
38274     // private
38275     handleClick : function(e){
38276         if(this.hideOnClick){
38277             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
38278         }
38279     },
38280
38281     // private
38282     expandMenu : function(autoActivate){
38283         // do nothing
38284     },
38285
38286     // private
38287     hideMenu : function(){
38288         // do nothing
38289     }
38290 });/*
38291  * Based on:
38292  * Ext JS Library 1.1.1
38293  * Copyright(c) 2006-2007, Ext JS, LLC.
38294  *
38295  * Originally Released Under LGPL - original licence link has changed is not relivant.
38296  *
38297  * Fork - LGPL
38298  * <script type="text/javascript">
38299  */
38300  
38301 /**
38302  * @class Roo.menu.Adapter
38303  * @extends Roo.menu.BaseItem
38304  * 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.
38305  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
38306  * @constructor
38307  * Creates a new Adapter
38308  * @param {Object} config Configuration options
38309  */
38310 Roo.menu.Adapter = function(component, config){
38311     Roo.menu.Adapter.superclass.constructor.call(this, config);
38312     this.component = component;
38313 };
38314 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
38315     // private
38316     canActivate : true,
38317
38318     // private
38319     onRender : function(container, position){
38320         this.component.render(container);
38321         this.el = this.component.getEl();
38322     },
38323
38324     // private
38325     activate : function(){
38326         if(this.disabled){
38327             return false;
38328         }
38329         this.component.focus();
38330         this.fireEvent("activate", this);
38331         return true;
38332     },
38333
38334     // private
38335     deactivate : function(){
38336         this.fireEvent("deactivate", this);
38337     },
38338
38339     // private
38340     disable : function(){
38341         this.component.disable();
38342         Roo.menu.Adapter.superclass.disable.call(this);
38343     },
38344
38345     // private
38346     enable : function(){
38347         this.component.enable();
38348         Roo.menu.Adapter.superclass.enable.call(this);
38349     }
38350 });/*
38351  * Based on:
38352  * Ext JS Library 1.1.1
38353  * Copyright(c) 2006-2007, Ext JS, LLC.
38354  *
38355  * Originally Released Under LGPL - original licence link has changed is not relivant.
38356  *
38357  * Fork - LGPL
38358  * <script type="text/javascript">
38359  */
38360
38361 /**
38362  * @class Roo.menu.TextItem
38363  * @extends Roo.menu.BaseItem
38364  * Adds a static text string to a menu, usually used as either a heading or group separator.
38365  * Note: old style constructor with text is still supported.
38366  * 
38367  * @constructor
38368  * Creates a new TextItem
38369  * @param {Object} cfg Configuration
38370  */
38371 Roo.menu.TextItem = function(cfg){
38372     if (typeof(cfg) == 'string') {
38373         this.text = cfg;
38374     } else {
38375         Roo.apply(this,cfg);
38376     }
38377     
38378     Roo.menu.TextItem.superclass.constructor.call(this);
38379 };
38380
38381 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
38382     /**
38383      * @cfg {Boolean} text Text to show on item.
38384      */
38385     text : '',
38386     
38387     /**
38388      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
38389      */
38390     hideOnClick : false,
38391     /**
38392      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
38393      */
38394     itemCls : "x-menu-text",
38395
38396     // private
38397     onRender : function(){
38398         var s = document.createElement("span");
38399         s.className = this.itemCls;
38400         s.innerHTML = this.text;
38401         this.el = s;
38402         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
38403     }
38404 });/*
38405  * Based on:
38406  * Ext JS Library 1.1.1
38407  * Copyright(c) 2006-2007, Ext JS, LLC.
38408  *
38409  * Originally Released Under LGPL - original licence link has changed is not relivant.
38410  *
38411  * Fork - LGPL
38412  * <script type="text/javascript">
38413  */
38414
38415 /**
38416  * @class Roo.menu.Separator
38417  * @extends Roo.menu.BaseItem
38418  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
38419  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
38420  * @constructor
38421  * @param {Object} config Configuration options
38422  */
38423 Roo.menu.Separator = function(config){
38424     Roo.menu.Separator.superclass.constructor.call(this, config);
38425 };
38426
38427 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
38428     /**
38429      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
38430      */
38431     itemCls : "x-menu-sep",
38432     /**
38433      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
38434      */
38435     hideOnClick : false,
38436
38437     // private
38438     onRender : function(li){
38439         var s = document.createElement("span");
38440         s.className = this.itemCls;
38441         s.innerHTML = "&#160;";
38442         this.el = s;
38443         li.addClass("x-menu-sep-li");
38444         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
38445     }
38446 });/*
38447  * Based on:
38448  * Ext JS Library 1.1.1
38449  * Copyright(c) 2006-2007, Ext JS, LLC.
38450  *
38451  * Originally Released Under LGPL - original licence link has changed is not relivant.
38452  *
38453  * Fork - LGPL
38454  * <script type="text/javascript">
38455  */
38456 /**
38457  * @class Roo.menu.Item
38458  * @extends Roo.menu.BaseItem
38459  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
38460  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
38461  * activation and click handling.
38462  * @constructor
38463  * Creates a new Item
38464  * @param {Object} config Configuration options
38465  */
38466 Roo.menu.Item = function(config){
38467     Roo.menu.Item.superclass.constructor.call(this, config);
38468     if(this.menu){
38469         this.menu = Roo.menu.MenuMgr.get(this.menu);
38470     }
38471 };
38472 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
38473     
38474     /**
38475      * @cfg {String} text
38476      * The text to show on the menu item.
38477      */
38478     text: '',
38479      /**
38480      * @cfg {String} HTML to render in menu
38481      * The text to show on the menu item (HTML version).
38482      */
38483     html: '',
38484     /**
38485      * @cfg {String} icon
38486      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
38487      */
38488     icon: undefined,
38489     /**
38490      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
38491      */
38492     itemCls : "x-menu-item",
38493     /**
38494      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
38495      */
38496     canActivate : true,
38497     /**
38498      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
38499      */
38500     showDelay: 200,
38501     // doc'd in BaseItem
38502     hideDelay: 200,
38503
38504     // private
38505     ctype: "Roo.menu.Item",
38506     
38507     // private
38508     onRender : function(container, position){
38509         var el = document.createElement("a");
38510         el.hideFocus = true;
38511         el.unselectable = "on";
38512         el.href = this.href || "#";
38513         if(this.hrefTarget){
38514             el.target = this.hrefTarget;
38515         }
38516         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
38517         
38518         var html = this.html.length ? this.html  : String.format('{0}',this.text);
38519         
38520         el.innerHTML = String.format(
38521                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
38522                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
38523         this.el = el;
38524         Roo.menu.Item.superclass.onRender.call(this, container, position);
38525     },
38526
38527     /**
38528      * Sets the text to display in this menu item
38529      * @param {String} text The text to display
38530      * @param {Boolean} isHTML true to indicate text is pure html.
38531      */
38532     setText : function(text, isHTML){
38533         if (isHTML) {
38534             this.html = text;
38535         } else {
38536             this.text = text;
38537             this.html = '';
38538         }
38539         if(this.rendered){
38540             var html = this.html.length ? this.html  : String.format('{0}',this.text);
38541      
38542             this.el.update(String.format(
38543                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
38544                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
38545             this.parentMenu.autoWidth();
38546         }
38547     },
38548
38549     // private
38550     handleClick : function(e){
38551         if(!this.href){ // if no link defined, stop the event automatically
38552             e.stopEvent();
38553         }
38554         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
38555     },
38556
38557     // private
38558     activate : function(autoExpand){
38559         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
38560             this.focus();
38561             if(autoExpand){
38562                 this.expandMenu();
38563             }
38564         }
38565         return true;
38566     },
38567
38568     // private
38569     shouldDeactivate : function(e){
38570         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
38571             if(this.menu && this.menu.isVisible()){
38572                 return !this.menu.getEl().getRegion().contains(e.getPoint());
38573             }
38574             return true;
38575         }
38576         return false;
38577     },
38578
38579     // private
38580     deactivate : function(){
38581         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
38582         this.hideMenu();
38583     },
38584
38585     // private
38586     expandMenu : function(autoActivate){
38587         if(!this.disabled && this.menu){
38588             clearTimeout(this.hideTimer);
38589             delete this.hideTimer;
38590             if(!this.menu.isVisible() && !this.showTimer){
38591                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
38592             }else if (this.menu.isVisible() && autoActivate){
38593                 this.menu.tryActivate(0, 1);
38594             }
38595         }
38596     },
38597
38598     // private
38599     deferExpand : function(autoActivate){
38600         delete this.showTimer;
38601         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
38602         if(autoActivate){
38603             this.menu.tryActivate(0, 1);
38604         }
38605     },
38606
38607     // private
38608     hideMenu : function(){
38609         clearTimeout(this.showTimer);
38610         delete this.showTimer;
38611         if(!this.hideTimer && this.menu && this.menu.isVisible()){
38612             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
38613         }
38614     },
38615
38616     // private
38617     deferHide : function(){
38618         delete this.hideTimer;
38619         this.menu.hide();
38620     }
38621 });/*
38622  * Based on:
38623  * Ext JS Library 1.1.1
38624  * Copyright(c) 2006-2007, Ext JS, LLC.
38625  *
38626  * Originally Released Under LGPL - original licence link has changed is not relivant.
38627  *
38628  * Fork - LGPL
38629  * <script type="text/javascript">
38630  */
38631  
38632 /**
38633  * @class Roo.menu.CheckItem
38634  * @extends Roo.menu.Item
38635  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
38636  * @constructor
38637  * Creates a new CheckItem
38638  * @param {Object} config Configuration options
38639  */
38640 Roo.menu.CheckItem = function(config){
38641     Roo.menu.CheckItem.superclass.constructor.call(this, config);
38642     this.addEvents({
38643         /**
38644          * @event beforecheckchange
38645          * Fires before the checked value is set, providing an opportunity to cancel if needed
38646          * @param {Roo.menu.CheckItem} this
38647          * @param {Boolean} checked The new checked value that will be set
38648          */
38649         "beforecheckchange" : true,
38650         /**
38651          * @event checkchange
38652          * Fires after the checked value has been set
38653          * @param {Roo.menu.CheckItem} this
38654          * @param {Boolean} checked The checked value that was set
38655          */
38656         "checkchange" : true
38657     });
38658     if(this.checkHandler){
38659         this.on('checkchange', this.checkHandler, this.scope);
38660     }
38661 };
38662 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
38663     /**
38664      * @cfg {String} group
38665      * All check items with the same group name will automatically be grouped into a single-select
38666      * radio button group (defaults to '')
38667      */
38668     /**
38669      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
38670      */
38671     itemCls : "x-menu-item x-menu-check-item",
38672     /**
38673      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
38674      */
38675     groupClass : "x-menu-group-item",
38676
38677     /**
38678      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
38679      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
38680      * initialized with checked = true will be rendered as checked.
38681      */
38682     checked: false,
38683
38684     // private
38685     ctype: "Roo.menu.CheckItem",
38686
38687     // private
38688     onRender : function(c){
38689         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
38690         if(this.group){
38691             this.el.addClass(this.groupClass);
38692         }
38693         Roo.menu.MenuMgr.registerCheckable(this);
38694         if(this.checked){
38695             this.checked = false;
38696             this.setChecked(true, true);
38697         }
38698     },
38699
38700     // private
38701     destroy : function(){
38702         if(this.rendered){
38703             Roo.menu.MenuMgr.unregisterCheckable(this);
38704         }
38705         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
38706     },
38707
38708     /**
38709      * Set the checked state of this item
38710      * @param {Boolean} checked The new checked value
38711      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
38712      */
38713     setChecked : function(state, suppressEvent){
38714         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
38715             if(this.container){
38716                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
38717             }
38718             this.checked = state;
38719             if(suppressEvent !== true){
38720                 this.fireEvent("checkchange", this, state);
38721             }
38722         }
38723     },
38724
38725     // private
38726     handleClick : function(e){
38727        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
38728            this.setChecked(!this.checked);
38729        }
38730        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
38731     }
38732 });/*
38733  * Based on:
38734  * Ext JS Library 1.1.1
38735  * Copyright(c) 2006-2007, Ext JS, LLC.
38736  *
38737  * Originally Released Under LGPL - original licence link has changed is not relivant.
38738  *
38739  * Fork - LGPL
38740  * <script type="text/javascript">
38741  */
38742  
38743 /**
38744  * @class Roo.menu.DateItem
38745  * @extends Roo.menu.Adapter
38746  * A menu item that wraps the {@link Roo.DatPicker} component.
38747  * @constructor
38748  * Creates a new DateItem
38749  * @param {Object} config Configuration options
38750  */
38751 Roo.menu.DateItem = function(config){
38752     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
38753     /** The Roo.DatePicker object @type Roo.DatePicker */
38754     this.picker = this.component;
38755     this.addEvents({select: true});
38756     
38757     this.picker.on("render", function(picker){
38758         picker.getEl().swallowEvent("click");
38759         picker.container.addClass("x-menu-date-item");
38760     });
38761
38762     this.picker.on("select", this.onSelect, this);
38763 };
38764
38765 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
38766     // private
38767     onSelect : function(picker, date){
38768         this.fireEvent("select", this, date, picker);
38769         Roo.menu.DateItem.superclass.handleClick.call(this);
38770     }
38771 });/*
38772  * Based on:
38773  * Ext JS Library 1.1.1
38774  * Copyright(c) 2006-2007, Ext JS, LLC.
38775  *
38776  * Originally Released Under LGPL - original licence link has changed is not relivant.
38777  *
38778  * Fork - LGPL
38779  * <script type="text/javascript">
38780  */
38781  
38782 /**
38783  * @class Roo.menu.ColorItem
38784  * @extends Roo.menu.Adapter
38785  * A menu item that wraps the {@link Roo.ColorPalette} component.
38786  * @constructor
38787  * Creates a new ColorItem
38788  * @param {Object} config Configuration options
38789  */
38790 Roo.menu.ColorItem = function(config){
38791     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
38792     /** The Roo.ColorPalette object @type Roo.ColorPalette */
38793     this.palette = this.component;
38794     this.relayEvents(this.palette, ["select"]);
38795     if(this.selectHandler){
38796         this.on('select', this.selectHandler, this.scope);
38797     }
38798 };
38799 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
38800  * Based on:
38801  * Ext JS Library 1.1.1
38802  * Copyright(c) 2006-2007, Ext JS, LLC.
38803  *
38804  * Originally Released Under LGPL - original licence link has changed is not relivant.
38805  *
38806  * Fork - LGPL
38807  * <script type="text/javascript">
38808  */
38809  
38810
38811 /**
38812  * @class Roo.menu.DateMenu
38813  * @extends Roo.menu.Menu
38814  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
38815  * @constructor
38816  * Creates a new DateMenu
38817  * @param {Object} config Configuration options
38818  */
38819 Roo.menu.DateMenu = function(config){
38820     Roo.menu.DateMenu.superclass.constructor.call(this, config);
38821     this.plain = true;
38822     var di = new Roo.menu.DateItem(config);
38823     this.add(di);
38824     /**
38825      * The {@link Roo.DatePicker} instance for this DateMenu
38826      * @type DatePicker
38827      */
38828     this.picker = di.picker;
38829     /**
38830      * @event select
38831      * @param {DatePicker} picker
38832      * @param {Date} date
38833      */
38834     this.relayEvents(di, ["select"]);
38835     this.on('beforeshow', function(){
38836         if(this.picker){
38837             this.picker.hideMonthPicker(false);
38838         }
38839     }, this);
38840 };
38841 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
38842     cls:'x-date-menu'
38843 });/*
38844  * Based on:
38845  * Ext JS Library 1.1.1
38846  * Copyright(c) 2006-2007, Ext JS, LLC.
38847  *
38848  * Originally Released Under LGPL - original licence link has changed is not relivant.
38849  *
38850  * Fork - LGPL
38851  * <script type="text/javascript">
38852  */
38853  
38854
38855 /**
38856  * @class Roo.menu.ColorMenu
38857  * @extends Roo.menu.Menu
38858  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
38859  * @constructor
38860  * Creates a new ColorMenu
38861  * @param {Object} config Configuration options
38862  */
38863 Roo.menu.ColorMenu = function(config){
38864     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
38865     this.plain = true;
38866     var ci = new Roo.menu.ColorItem(config);
38867     this.add(ci);
38868     /**
38869      * The {@link Roo.ColorPalette} instance for this ColorMenu
38870      * @type ColorPalette
38871      */
38872     this.palette = ci.palette;
38873     /**
38874      * @event select
38875      * @param {ColorPalette} palette
38876      * @param {String} color
38877      */
38878     this.relayEvents(ci, ["select"]);
38879 };
38880 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
38881  * Based on:
38882  * Ext JS Library 1.1.1
38883  * Copyright(c) 2006-2007, Ext JS, LLC.
38884  *
38885  * Originally Released Under LGPL - original licence link has changed is not relivant.
38886  *
38887  * Fork - LGPL
38888  * <script type="text/javascript">
38889  */
38890  
38891 /**
38892  * @class Roo.form.TextItem
38893  * @extends Roo.BoxComponent
38894  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
38895  * @constructor
38896  * Creates a new TextItem
38897  * @param {Object} config Configuration options
38898  */
38899 Roo.form.TextItem = function(config){
38900     Roo.form.TextItem.superclass.constructor.call(this, config);
38901 };
38902
38903 Roo.extend(Roo.form.TextItem, Roo.BoxComponent,  {
38904     
38905     /**
38906      * @cfg {String} tag the tag for this item (default div)
38907      */
38908     tag : 'div',
38909     /**
38910      * @cfg {String} html the content for this item
38911      */
38912     html : '',
38913     
38914     getAutoCreate : function()
38915     {
38916         var cfg = {
38917             id: this.id,
38918             tag: this.tag,
38919             html: this.html,
38920             cls: 'x-form-item'
38921         };
38922         
38923         return cfg;
38924         
38925     },
38926     
38927     onRender : function(ct, position)
38928     {
38929         Roo.form.TextItem.superclass.onRender.call(this, ct, position);
38930         
38931         if(!this.el){
38932             var cfg = this.getAutoCreate();
38933             if(!cfg.name){
38934                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
38935             }
38936             if (!cfg.name.length) {
38937                 delete cfg.name;
38938             }
38939             this.el = ct.createChild(cfg, position);
38940         }
38941     },
38942     /*
38943      * setHTML
38944      * @param {String} html update the Contents of the element.
38945      */
38946     setHTML : function(html)
38947     {
38948         this.fieldEl.dom.innerHTML = html;
38949     }
38950     
38951 });/*
38952  * Based on:
38953  * Ext JS Library 1.1.1
38954  * Copyright(c) 2006-2007, Ext JS, LLC.
38955  *
38956  * Originally Released Under LGPL - original licence link has changed is not relivant.
38957  *
38958  * Fork - LGPL
38959  * <script type="text/javascript">
38960  */
38961  
38962 /**
38963  * @class Roo.form.Field
38964  * @extends Roo.BoxComponent
38965  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
38966  * @constructor
38967  * Creates a new Field
38968  * @param {Object} config Configuration options
38969  */
38970 Roo.form.Field = function(config){
38971     Roo.form.Field.superclass.constructor.call(this, config);
38972 };
38973
38974 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
38975     /**
38976      * @cfg {String} fieldLabel Label to use when rendering a form.
38977      */
38978        /**
38979      * @cfg {String} qtip Mouse over tip
38980      */
38981      
38982     /**
38983      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
38984      */
38985     invalidClass : "x-form-invalid",
38986     /**
38987      * @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")
38988      */
38989     invalidText : "The value in this field is invalid",
38990     /**
38991      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
38992      */
38993     focusClass : "x-form-focus",
38994     /**
38995      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
38996       automatic validation (defaults to "keyup").
38997      */
38998     validationEvent : "keyup",
38999     /**
39000      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
39001      */
39002     validateOnBlur : true,
39003     /**
39004      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
39005      */
39006     validationDelay : 250,
39007     /**
39008      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
39009      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
39010      */
39011     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
39012     /**
39013      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
39014      */
39015     fieldClass : "x-form-field",
39016     /**
39017      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
39018      *<pre>
39019 Value         Description
39020 -----------   ----------------------------------------------------------------------
39021 qtip          Display a quick tip when the user hovers over the field
39022 title         Display a default browser title attribute popup
39023 under         Add a block div beneath the field containing the error text
39024 side          Add an error icon to the right of the field with a popup on hover
39025 [element id]  Add the error text directly to the innerHTML of the specified element
39026 </pre>
39027      */
39028     msgTarget : 'qtip',
39029     /**
39030      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
39031      */
39032     msgFx : 'normal',
39033
39034     /**
39035      * @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.
39036      */
39037     readOnly : false,
39038
39039     /**
39040      * @cfg {Boolean} disabled True to disable the field (defaults to false).
39041      */
39042     disabled : false,
39043
39044     /**
39045      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
39046      */
39047     inputType : undefined,
39048     
39049     /**
39050      * @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).
39051          */
39052         tabIndex : undefined,
39053         
39054     // private
39055     isFormField : true,
39056
39057     // private
39058     hasFocus : false,
39059     /**
39060      * @property {Roo.Element} fieldEl
39061      * Element Containing the rendered Field (with label etc.)
39062      */
39063     /**
39064      * @cfg {Mixed} value A value to initialize this field with.
39065      */
39066     value : undefined,
39067
39068     /**
39069      * @cfg {String} name The field's HTML name attribute.
39070      */
39071     /**
39072      * @cfg {String} cls A CSS class to apply to the field's underlying element.
39073      */
39074     // private
39075     loadedValue : false,
39076      
39077      
39078         // private ??
39079         initComponent : function(){
39080         Roo.form.Field.superclass.initComponent.call(this);
39081         this.addEvents({
39082             /**
39083              * @event focus
39084              * Fires when this field receives input focus.
39085              * @param {Roo.form.Field} this
39086              */
39087             focus : true,
39088             /**
39089              * @event blur
39090              * Fires when this field loses input focus.
39091              * @param {Roo.form.Field} this
39092              */
39093             blur : true,
39094             /**
39095              * @event specialkey
39096              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
39097              * {@link Roo.EventObject#getKey} to determine which key was pressed.
39098              * @param {Roo.form.Field} this
39099              * @param {Roo.EventObject} e The event object
39100              */
39101             specialkey : true,
39102             /**
39103              * @event change
39104              * Fires just before the field blurs if the field value has changed.
39105              * @param {Roo.form.Field} this
39106              * @param {Mixed} newValue The new value
39107              * @param {Mixed} oldValue The original value
39108              */
39109             change : true,
39110             /**
39111              * @event invalid
39112              * Fires after the field has been marked as invalid.
39113              * @param {Roo.form.Field} this
39114              * @param {String} msg The validation message
39115              */
39116             invalid : true,
39117             /**
39118              * @event valid
39119              * Fires after the field has been validated with no errors.
39120              * @param {Roo.form.Field} this
39121              */
39122             valid : true,
39123              /**
39124              * @event keyup
39125              * Fires after the key up
39126              * @param {Roo.form.Field} this
39127              * @param {Roo.EventObject}  e The event Object
39128              */
39129             keyup : true
39130         });
39131     },
39132
39133     /**
39134      * Returns the name attribute of the field if available
39135      * @return {String} name The field name
39136      */
39137     getName: function(){
39138          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
39139     },
39140
39141     // private
39142     onRender : function(ct, position){
39143         Roo.form.Field.superclass.onRender.call(this, ct, position);
39144         if(!this.el){
39145             var cfg = this.getAutoCreate();
39146             if(!cfg.name){
39147                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
39148             }
39149             if (!cfg.name.length) {
39150                 delete cfg.name;
39151             }
39152             if(this.inputType){
39153                 cfg.type = this.inputType;
39154             }
39155             this.el = ct.createChild(cfg, position);
39156         }
39157         var type = this.el.dom.type;
39158         if(type){
39159             if(type == 'password'){
39160                 type = 'text';
39161             }
39162             this.el.addClass('x-form-'+type);
39163         }
39164         if(this.readOnly){
39165             this.el.dom.readOnly = true;
39166         }
39167         if(this.tabIndex !== undefined){
39168             this.el.dom.setAttribute('tabIndex', this.tabIndex);
39169         }
39170
39171         this.el.addClass([this.fieldClass, this.cls]);
39172         this.initValue();
39173     },
39174
39175     /**
39176      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
39177      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
39178      * @return {Roo.form.Field} this
39179      */
39180     applyTo : function(target){
39181         this.allowDomMove = false;
39182         this.el = Roo.get(target);
39183         this.render(this.el.dom.parentNode);
39184         return this;
39185     },
39186
39187     // private
39188     initValue : function(){
39189         if(this.value !== undefined){
39190             this.setValue(this.value);
39191         }else if(this.el.dom.value.length > 0){
39192             this.setValue(this.el.dom.value);
39193         }
39194     },
39195
39196     /**
39197      * Returns true if this field has been changed since it was originally loaded and is not disabled.
39198      * DEPRICATED  - it never worked well - use hasChanged/resetHasChanged.
39199      */
39200     isDirty : function() {
39201         if(this.disabled) {
39202             return false;
39203         }
39204         return String(this.getValue()) !== String(this.originalValue);
39205     },
39206
39207     /**
39208      * stores the current value in loadedValue
39209      */
39210     resetHasChanged : function()
39211     {
39212         this.loadedValue = String(this.getValue());
39213     },
39214     /**
39215      * checks the current value against the 'loaded' value.
39216      * Note - will return false if 'resetHasChanged' has not been called first.
39217      */
39218     hasChanged : function()
39219     {
39220         if(this.disabled || this.readOnly) {
39221             return false;
39222         }
39223         return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
39224     },
39225     
39226     
39227     
39228     // private
39229     afterRender : function(){
39230         Roo.form.Field.superclass.afterRender.call(this);
39231         this.initEvents();
39232     },
39233
39234     // private
39235     fireKey : function(e){
39236         //Roo.log('field ' + e.getKey());
39237         if(e.isNavKeyPress()){
39238             this.fireEvent("specialkey", this, e);
39239         }
39240     },
39241
39242     /**
39243      * Resets the current field value to the originally loaded value and clears any validation messages
39244      */
39245     reset : function(){
39246         this.setValue(this.resetValue);
39247         this.originalValue = this.getValue();
39248         this.clearInvalid();
39249     },
39250
39251     // private
39252     initEvents : function(){
39253         // safari killled keypress - so keydown is now used..
39254         this.el.on("keydown" , this.fireKey,  this);
39255         this.el.on("focus", this.onFocus,  this);
39256         this.el.on("blur", this.onBlur,  this);
39257         this.el.relayEvent('keyup', this);
39258
39259         // reference to original value for reset
39260         this.originalValue = this.getValue();
39261         this.resetValue =  this.getValue();
39262     },
39263
39264     // private
39265     onFocus : function(){
39266         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
39267             this.el.addClass(this.focusClass);
39268         }
39269         if(!this.hasFocus){
39270             this.hasFocus = true;
39271             this.startValue = this.getValue();
39272             this.fireEvent("focus", this);
39273         }
39274     },
39275
39276     beforeBlur : Roo.emptyFn,
39277
39278     // private
39279     onBlur : function(){
39280         this.beforeBlur();
39281         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
39282             this.el.removeClass(this.focusClass);
39283         }
39284         this.hasFocus = false;
39285         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
39286             this.validate();
39287         }
39288         var v = this.getValue();
39289         if(String(v) !== String(this.startValue)){
39290             this.fireEvent('change', this, v, this.startValue);
39291         }
39292         this.fireEvent("blur", this);
39293     },
39294
39295     /**
39296      * Returns whether or not the field value is currently valid
39297      * @param {Boolean} preventMark True to disable marking the field invalid
39298      * @return {Boolean} True if the value is valid, else false
39299      */
39300     isValid : function(preventMark){
39301         if(this.disabled){
39302             return true;
39303         }
39304         var restore = this.preventMark;
39305         this.preventMark = preventMark === true;
39306         var v = this.validateValue(this.processValue(this.getRawValue()));
39307         this.preventMark = restore;
39308         return v;
39309     },
39310
39311     /**
39312      * Validates the field value
39313      * @return {Boolean} True if the value is valid, else false
39314      */
39315     validate : function(){
39316         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
39317             this.clearInvalid();
39318             return true;
39319         }
39320         return false;
39321     },
39322
39323     processValue : function(value){
39324         return value;
39325     },
39326
39327     // private
39328     // Subclasses should provide the validation implementation by overriding this
39329     validateValue : function(value){
39330         return true;
39331     },
39332
39333     /**
39334      * Mark this field as invalid
39335      * @param {String} msg The validation message
39336      */
39337     markInvalid : function(msg){
39338         if(!this.rendered || this.preventMark){ // not rendered
39339             return;
39340         }
39341         
39342         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
39343         
39344         obj.el.addClass(this.invalidClass);
39345         msg = msg || this.invalidText;
39346         switch(this.msgTarget){
39347             case 'qtip':
39348                 obj.el.dom.qtip = msg;
39349                 obj.el.dom.qclass = 'x-form-invalid-tip';
39350                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
39351                     Roo.QuickTips.enable();
39352                 }
39353                 break;
39354             case 'title':
39355                 this.el.dom.title = msg;
39356                 break;
39357             case 'under':
39358                 if(!this.errorEl){
39359                     var elp = this.el.findParent('.x-form-element', 5, true);
39360                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
39361                     this.errorEl.setWidth(elp.getWidth(true)-20);
39362                 }
39363                 this.errorEl.update(msg);
39364                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
39365                 break;
39366             case 'side':
39367                 if(!this.errorIcon){
39368                     var elp = this.el.findParent('.x-form-element', 5, true);
39369                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
39370                 }
39371                 this.alignErrorIcon();
39372                 this.errorIcon.dom.qtip = msg;
39373                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
39374                 this.errorIcon.show();
39375                 this.on('resize', this.alignErrorIcon, this);
39376                 break;
39377             default:
39378                 var t = Roo.getDom(this.msgTarget);
39379                 t.innerHTML = msg;
39380                 t.style.display = this.msgDisplay;
39381                 break;
39382         }
39383         this.fireEvent('invalid', this, msg);
39384     },
39385
39386     // private
39387     alignErrorIcon : function(){
39388         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
39389     },
39390
39391     /**
39392      * Clear any invalid styles/messages for this field
39393      */
39394     clearInvalid : function(){
39395         if(!this.rendered || this.preventMark){ // not rendered
39396             return;
39397         }
39398         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
39399         
39400         obj.el.removeClass(this.invalidClass);
39401         switch(this.msgTarget){
39402             case 'qtip':
39403                 obj.el.dom.qtip = '';
39404                 break;
39405             case 'title':
39406                 this.el.dom.title = '';
39407                 break;
39408             case 'under':
39409                 if(this.errorEl){
39410                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
39411                 }
39412                 break;
39413             case 'side':
39414                 if(this.errorIcon){
39415                     this.errorIcon.dom.qtip = '';
39416                     this.errorIcon.hide();
39417                     this.un('resize', this.alignErrorIcon, this);
39418                 }
39419                 break;
39420             default:
39421                 var t = Roo.getDom(this.msgTarget);
39422                 t.innerHTML = '';
39423                 t.style.display = 'none';
39424                 break;
39425         }
39426         this.fireEvent('valid', this);
39427     },
39428
39429     /**
39430      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
39431      * @return {Mixed} value The field value
39432      */
39433     getRawValue : function(){
39434         var v = this.el.getValue();
39435         
39436         return v;
39437     },
39438
39439     /**
39440      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
39441      * @return {Mixed} value The field value
39442      */
39443     getValue : function(){
39444         var v = this.el.getValue();
39445          
39446         return v;
39447     },
39448
39449     /**
39450      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
39451      * @param {Mixed} value The value to set
39452      */
39453     setRawValue : function(v){
39454         return this.el.dom.value = (v === null || v === undefined ? '' : v);
39455     },
39456
39457     /**
39458      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
39459      * @param {Mixed} value The value to set
39460      */
39461     setValue : function(v){
39462         this.value = v;
39463         if(this.rendered){
39464             this.el.dom.value = (v === null || v === undefined ? '' : v);
39465              this.validate();
39466         }
39467     },
39468
39469     adjustSize : function(w, h){
39470         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
39471         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
39472         return s;
39473     },
39474
39475     adjustWidth : function(tag, w){
39476         tag = tag.toLowerCase();
39477         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
39478             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
39479                 if(tag == 'input'){
39480                     return w + 2;
39481                 }
39482                 if(tag == 'textarea'){
39483                     return w-2;
39484                 }
39485             }else if(Roo.isOpera){
39486                 if(tag == 'input'){
39487                     return w + 2;
39488                 }
39489                 if(tag == 'textarea'){
39490                     return w-2;
39491                 }
39492             }
39493         }
39494         return w;
39495     }
39496 });
39497
39498
39499 // anything other than normal should be considered experimental
39500 Roo.form.Field.msgFx = {
39501     normal : {
39502         show: function(msgEl, f){
39503             msgEl.setDisplayed('block');
39504         },
39505
39506         hide : function(msgEl, f){
39507             msgEl.setDisplayed(false).update('');
39508         }
39509     },
39510
39511     slide : {
39512         show: function(msgEl, f){
39513             msgEl.slideIn('t', {stopFx:true});
39514         },
39515
39516         hide : function(msgEl, f){
39517             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
39518         }
39519     },
39520
39521     slideRight : {
39522         show: function(msgEl, f){
39523             msgEl.fixDisplay();
39524             msgEl.alignTo(f.el, 'tl-tr');
39525             msgEl.slideIn('l', {stopFx:true});
39526         },
39527
39528         hide : function(msgEl, f){
39529             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
39530         }
39531     }
39532 };/*
39533  * Based on:
39534  * Ext JS Library 1.1.1
39535  * Copyright(c) 2006-2007, Ext JS, LLC.
39536  *
39537  * Originally Released Under LGPL - original licence link has changed is not relivant.
39538  *
39539  * Fork - LGPL
39540  * <script type="text/javascript">
39541  */
39542  
39543
39544 /**
39545  * @class Roo.form.TextField
39546  * @extends Roo.form.Field
39547  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
39548  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
39549  * @constructor
39550  * Creates a new TextField
39551  * @param {Object} config Configuration options
39552  */
39553 Roo.form.TextField = function(config){
39554     Roo.form.TextField.superclass.constructor.call(this, config);
39555     this.addEvents({
39556         /**
39557          * @event autosize
39558          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
39559          * according to the default logic, but this event provides a hook for the developer to apply additional
39560          * logic at runtime to resize the field if needed.
39561              * @param {Roo.form.Field} this This text field
39562              * @param {Number} width The new field width
39563              */
39564         autosize : true
39565     });
39566 };
39567
39568 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
39569     /**
39570      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
39571      */
39572     grow : false,
39573     /**
39574      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
39575      */
39576     growMin : 30,
39577     /**
39578      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
39579      */
39580     growMax : 800,
39581     /**
39582      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
39583      */
39584     vtype : null,
39585     /**
39586      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
39587      */
39588     maskRe : null,
39589     /**
39590      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
39591      */
39592     disableKeyFilter : false,
39593     /**
39594      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
39595      */
39596     allowBlank : true,
39597     /**
39598      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
39599      */
39600     minLength : 0,
39601     /**
39602      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
39603      */
39604     maxLength : Number.MAX_VALUE,
39605     /**
39606      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
39607      */
39608     minLengthText : "The minimum length for this field is {0}",
39609     /**
39610      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
39611      */
39612     maxLengthText : "The maximum length for this field is {0}",
39613     /**
39614      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
39615      */
39616     selectOnFocus : false,
39617     /**
39618      * @cfg {Boolean} allowLeadingSpace True to prevent the stripping of leading white space 
39619      */    
39620     allowLeadingSpace : false,
39621     /**
39622      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
39623      */
39624     blankText : "This field is required",
39625     /**
39626      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
39627      * If available, this function will be called only after the basic validators all return true, and will be passed the
39628      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
39629      */
39630     validator : null,
39631     /**
39632      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
39633      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
39634      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
39635      */
39636     regex : null,
39637     /**
39638      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
39639      */
39640     regexText : "",
39641     /**
39642      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
39643      */
39644     emptyText : null,
39645    
39646
39647     // private
39648     initEvents : function()
39649     {
39650         if (this.emptyText) {
39651             this.el.attr('placeholder', this.emptyText);
39652         }
39653         
39654         Roo.form.TextField.superclass.initEvents.call(this);
39655         if(this.validationEvent == 'keyup'){
39656             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
39657             this.el.on('keyup', this.filterValidation, this);
39658         }
39659         else if(this.validationEvent !== false){
39660             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
39661         }
39662         
39663         if(this.selectOnFocus){
39664             this.on("focus", this.preFocus, this);
39665         }
39666         if (!this.allowLeadingSpace) {
39667             this.on('blur', this.cleanLeadingSpace, this);
39668         }
39669         
39670         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
39671             this.el.on("keypress", this.filterKeys, this);
39672         }
39673         if(this.grow){
39674             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
39675             this.el.on("click", this.autoSize,  this);
39676         }
39677         if(this.el.is('input[type=password]') && Roo.isSafari){
39678             this.el.on('keydown', this.SafariOnKeyDown, this);
39679         }
39680     },
39681
39682     processValue : function(value){
39683         if(this.stripCharsRe){
39684             var newValue = value.replace(this.stripCharsRe, '');
39685             if(newValue !== value){
39686                 this.setRawValue(newValue);
39687                 return newValue;
39688             }
39689         }
39690         return value;
39691     },
39692
39693     filterValidation : function(e){
39694         if(!e.isNavKeyPress()){
39695             this.validationTask.delay(this.validationDelay);
39696         }
39697     },
39698
39699     // private
39700     onKeyUp : function(e){
39701         if(!e.isNavKeyPress()){
39702             this.autoSize();
39703         }
39704     },
39705     // private - clean the leading white space
39706     cleanLeadingSpace : function(e)
39707     {
39708         if ( this.inputType == 'file') {
39709             return;
39710         }
39711         
39712         this.setValue((this.getValue() + '').replace(/^\s+/,''));
39713     },
39714     /**
39715      * Resets the current field value to the originally-loaded value and clears any validation messages.
39716      *  
39717      */
39718     reset : function(){
39719         Roo.form.TextField.superclass.reset.call(this);
39720        
39721     }, 
39722     // private
39723     preFocus : function(){
39724         
39725         if(this.selectOnFocus){
39726             this.el.dom.select();
39727         }
39728     },
39729
39730     
39731     // private
39732     filterKeys : function(e){
39733         var k = e.getKey();
39734         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
39735             return;
39736         }
39737         var c = e.getCharCode(), cc = String.fromCharCode(c);
39738         if(Roo.isIE && (e.isSpecialKey() || !cc)){
39739             return;
39740         }
39741         if(!this.maskRe.test(cc)){
39742             e.stopEvent();
39743         }
39744     },
39745
39746     setValue : function(v){
39747         
39748         Roo.form.TextField.superclass.setValue.apply(this, arguments);
39749         
39750         this.autoSize();
39751     },
39752
39753     /**
39754      * Validates a value according to the field's validation rules and marks the field as invalid
39755      * if the validation fails
39756      * @param {Mixed} value The value to validate
39757      * @return {Boolean} True if the value is valid, else false
39758      */
39759     validateValue : function(value){
39760         if(value.length < 1)  { // if it's blank
39761              if(this.allowBlank){
39762                 this.clearInvalid();
39763                 return true;
39764              }else{
39765                 this.markInvalid(this.blankText);
39766                 return false;
39767              }
39768         }
39769         if(value.length < this.minLength){
39770             this.markInvalid(String.format(this.minLengthText, this.minLength));
39771             return false;
39772         }
39773         if(value.length > this.maxLength){
39774             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
39775             return false;
39776         }
39777         if(this.vtype){
39778             var vt = Roo.form.VTypes;
39779             if(!vt[this.vtype](value, this)){
39780                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
39781                 return false;
39782             }
39783         }
39784         if(typeof this.validator == "function"){
39785             var msg = this.validator(value);
39786             if(msg !== true){
39787                 this.markInvalid(msg);
39788                 return false;
39789             }
39790         }
39791         if(this.regex && !this.regex.test(value)){
39792             this.markInvalid(this.regexText);
39793             return false;
39794         }
39795         return true;
39796     },
39797
39798     /**
39799      * Selects text in this field
39800      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
39801      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
39802      */
39803     selectText : function(start, end){
39804         var v = this.getRawValue();
39805         if(v.length > 0){
39806             start = start === undefined ? 0 : start;
39807             end = end === undefined ? v.length : end;
39808             var d = this.el.dom;
39809             if(d.setSelectionRange){
39810                 d.setSelectionRange(start, end);
39811             }else if(d.createTextRange){
39812                 var range = d.createTextRange();
39813                 range.moveStart("character", start);
39814                 range.moveEnd("character", v.length-end);
39815                 range.select();
39816             }
39817         }
39818     },
39819
39820     /**
39821      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
39822      * This only takes effect if grow = true, and fires the autosize event.
39823      */
39824     autoSize : function(){
39825         if(!this.grow || !this.rendered){
39826             return;
39827         }
39828         if(!this.metrics){
39829             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
39830         }
39831         var el = this.el;
39832         var v = el.dom.value;
39833         var d = document.createElement('div');
39834         d.appendChild(document.createTextNode(v));
39835         v = d.innerHTML;
39836         d = null;
39837         v += "&#160;";
39838         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
39839         this.el.setWidth(w);
39840         this.fireEvent("autosize", this, w);
39841     },
39842     
39843     // private
39844     SafariOnKeyDown : function(event)
39845     {
39846         // this is a workaround for a password hang bug on chrome/ webkit.
39847         
39848         var isSelectAll = false;
39849         
39850         if(this.el.dom.selectionEnd > 0){
39851             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
39852         }
39853         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
39854             event.preventDefault();
39855             this.setValue('');
39856             return;
39857         }
39858         
39859         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
39860             
39861             event.preventDefault();
39862             // this is very hacky as keydown always get's upper case.
39863             
39864             var cc = String.fromCharCode(event.getCharCode());
39865             
39866             
39867             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
39868             
39869         }
39870         
39871         
39872     }
39873 });/*
39874  * Based on:
39875  * Ext JS Library 1.1.1
39876  * Copyright(c) 2006-2007, Ext JS, LLC.
39877  *
39878  * Originally Released Under LGPL - original licence link has changed is not relivant.
39879  *
39880  * Fork - LGPL
39881  * <script type="text/javascript">
39882  */
39883  
39884 /**
39885  * @class Roo.form.Hidden
39886  * @extends Roo.form.TextField
39887  * Simple Hidden element used on forms 
39888  * 
39889  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
39890  * 
39891  * @constructor
39892  * Creates a new Hidden form element.
39893  * @param {Object} config Configuration options
39894  */
39895
39896
39897
39898 // easy hidden field...
39899 Roo.form.Hidden = function(config){
39900     Roo.form.Hidden.superclass.constructor.call(this, config);
39901 };
39902   
39903 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
39904     fieldLabel:      '',
39905     inputType:      'hidden',
39906     width:          50,
39907     allowBlank:     true,
39908     labelSeparator: '',
39909     hidden:         true,
39910     itemCls :       'x-form-item-display-none'
39911
39912
39913 });
39914
39915
39916 /*
39917  * Based on:
39918  * Ext JS Library 1.1.1
39919  * Copyright(c) 2006-2007, Ext JS, LLC.
39920  *
39921  * Originally Released Under LGPL - original licence link has changed is not relivant.
39922  *
39923  * Fork - LGPL
39924  * <script type="text/javascript">
39925  */
39926  
39927 /**
39928  * @class Roo.form.TriggerField
39929  * @extends Roo.form.TextField
39930  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
39931  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
39932  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
39933  * for which you can provide a custom implementation.  For example:
39934  * <pre><code>
39935 var trigger = new Roo.form.TriggerField();
39936 trigger.onTriggerClick = myTriggerFn;
39937 trigger.applyTo('my-field');
39938 </code></pre>
39939  *
39940  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
39941  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
39942  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
39943  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
39944  * @constructor
39945  * Create a new TriggerField.
39946  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
39947  * to the base TextField)
39948  */
39949 Roo.form.TriggerField = function(config){
39950     this.mimicing = false;
39951     Roo.form.TriggerField.superclass.constructor.call(this, config);
39952 };
39953
39954 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
39955     /**
39956      * @cfg {String} triggerClass A CSS class to apply to the trigger
39957      */
39958     /**
39959      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
39960      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
39961      */
39962     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
39963     /**
39964      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
39965      */
39966     hideTrigger:false,
39967
39968     /** @cfg {Boolean} grow @hide */
39969     /** @cfg {Number} growMin @hide */
39970     /** @cfg {Number} growMax @hide */
39971
39972     /**
39973      * @hide 
39974      * @method
39975      */
39976     autoSize: Roo.emptyFn,
39977     // private
39978     monitorTab : true,
39979     // private
39980     deferHeight : true,
39981
39982     
39983     actionMode : 'wrap',
39984     // private
39985     onResize : function(w, h){
39986         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
39987         if(typeof w == 'number'){
39988             var x = w - this.trigger.getWidth();
39989             this.el.setWidth(this.adjustWidth('input', x));
39990             this.trigger.setStyle('left', x+'px');
39991         }
39992     },
39993
39994     // private
39995     adjustSize : Roo.BoxComponent.prototype.adjustSize,
39996
39997     // private
39998     getResizeEl : function(){
39999         return this.wrap;
40000     },
40001
40002     // private
40003     getPositionEl : function(){
40004         return this.wrap;
40005     },
40006
40007     // private
40008     alignErrorIcon : function(){
40009         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
40010     },
40011
40012     // private
40013     onRender : function(ct, position){
40014         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
40015         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
40016         this.trigger = this.wrap.createChild(this.triggerConfig ||
40017                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
40018         if(this.hideTrigger){
40019             this.trigger.setDisplayed(false);
40020         }
40021         this.initTrigger();
40022         if(!this.width){
40023             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
40024         }
40025     },
40026
40027     // private
40028     initTrigger : function(){
40029         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40030         this.trigger.addClassOnOver('x-form-trigger-over');
40031         this.trigger.addClassOnClick('x-form-trigger-click');
40032     },
40033
40034     // private
40035     onDestroy : function(){
40036         if(this.trigger){
40037             this.trigger.removeAllListeners();
40038             this.trigger.remove();
40039         }
40040         if(this.wrap){
40041             this.wrap.remove();
40042         }
40043         Roo.form.TriggerField.superclass.onDestroy.call(this);
40044     },
40045
40046     // private
40047     onFocus : function(){
40048         Roo.form.TriggerField.superclass.onFocus.call(this);
40049         if(!this.mimicing){
40050             this.wrap.addClass('x-trigger-wrap-focus');
40051             this.mimicing = true;
40052             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
40053             if(this.monitorTab){
40054                 this.el.on("keydown", this.checkTab, this);
40055             }
40056         }
40057     },
40058
40059     // private
40060     checkTab : function(e){
40061         if(e.getKey() == e.TAB){
40062             this.triggerBlur();
40063         }
40064     },
40065
40066     // private
40067     onBlur : function(){
40068         // do nothing
40069     },
40070
40071     // private
40072     mimicBlur : function(e, t){
40073         if(!this.wrap.contains(t) && this.validateBlur()){
40074             this.triggerBlur();
40075         }
40076     },
40077
40078     // private
40079     triggerBlur : function(){
40080         this.mimicing = false;
40081         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
40082         if(this.monitorTab){
40083             this.el.un("keydown", this.checkTab, this);
40084         }
40085         this.wrap.removeClass('x-trigger-wrap-focus');
40086         Roo.form.TriggerField.superclass.onBlur.call(this);
40087     },
40088
40089     // private
40090     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
40091     validateBlur : function(e, t){
40092         return true;
40093     },
40094
40095     // private
40096     onDisable : function(){
40097         Roo.form.TriggerField.superclass.onDisable.call(this);
40098         if(this.wrap){
40099             this.wrap.addClass('x-item-disabled');
40100         }
40101     },
40102
40103     // private
40104     onEnable : function(){
40105         Roo.form.TriggerField.superclass.onEnable.call(this);
40106         if(this.wrap){
40107             this.wrap.removeClass('x-item-disabled');
40108         }
40109     },
40110
40111     // private
40112     onShow : function(){
40113         var ae = this.getActionEl();
40114         
40115         if(ae){
40116             ae.dom.style.display = '';
40117             ae.dom.style.visibility = 'visible';
40118         }
40119     },
40120
40121     // private
40122     
40123     onHide : function(){
40124         var ae = this.getActionEl();
40125         ae.dom.style.display = 'none';
40126     },
40127
40128     /**
40129      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
40130      * by an implementing function.
40131      * @method
40132      * @param {EventObject} e
40133      */
40134     onTriggerClick : Roo.emptyFn
40135 });
40136
40137 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
40138 // to be extended by an implementing class.  For an example of implementing this class, see the custom
40139 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
40140 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
40141     initComponent : function(){
40142         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
40143
40144         this.triggerConfig = {
40145             tag:'span', cls:'x-form-twin-triggers', cn:[
40146             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
40147             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
40148         ]};
40149     },
40150
40151     getTrigger : function(index){
40152         return this.triggers[index];
40153     },
40154
40155     initTrigger : function(){
40156         var ts = this.trigger.select('.x-form-trigger', true);
40157         this.wrap.setStyle('overflow', 'hidden');
40158         var triggerField = this;
40159         ts.each(function(t, all, index){
40160             t.hide = function(){
40161                 var w = triggerField.wrap.getWidth();
40162                 this.dom.style.display = 'none';
40163                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
40164             };
40165             t.show = function(){
40166                 var w = triggerField.wrap.getWidth();
40167                 this.dom.style.display = '';
40168                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
40169             };
40170             var triggerIndex = 'Trigger'+(index+1);
40171
40172             if(this['hide'+triggerIndex]){
40173                 t.dom.style.display = 'none';
40174             }
40175             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
40176             t.addClassOnOver('x-form-trigger-over');
40177             t.addClassOnClick('x-form-trigger-click');
40178         }, this);
40179         this.triggers = ts.elements;
40180     },
40181
40182     onTrigger1Click : Roo.emptyFn,
40183     onTrigger2Click : Roo.emptyFn
40184 });/*
40185  * Based on:
40186  * Ext JS Library 1.1.1
40187  * Copyright(c) 2006-2007, Ext JS, LLC.
40188  *
40189  * Originally Released Under LGPL - original licence link has changed is not relivant.
40190  *
40191  * Fork - LGPL
40192  * <script type="text/javascript">
40193  */
40194  
40195 /**
40196  * @class Roo.form.TextArea
40197  * @extends Roo.form.TextField
40198  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
40199  * support for auto-sizing.
40200  * @constructor
40201  * Creates a new TextArea
40202  * @param {Object} config Configuration options
40203  */
40204 Roo.form.TextArea = function(config){
40205     Roo.form.TextArea.superclass.constructor.call(this, config);
40206     // these are provided exchanges for backwards compat
40207     // minHeight/maxHeight were replaced by growMin/growMax to be
40208     // compatible with TextField growing config values
40209     if(this.minHeight !== undefined){
40210         this.growMin = this.minHeight;
40211     }
40212     if(this.maxHeight !== undefined){
40213         this.growMax = this.maxHeight;
40214     }
40215 };
40216
40217 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
40218     /**
40219      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
40220      */
40221     growMin : 60,
40222     /**
40223      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
40224      */
40225     growMax: 1000,
40226     /**
40227      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
40228      * in the field (equivalent to setting overflow: hidden, defaults to false)
40229      */
40230     preventScrollbars: false,
40231     /**
40232      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
40233      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
40234      */
40235
40236     // private
40237     onRender : function(ct, position){
40238         if(!this.el){
40239             this.defaultAutoCreate = {
40240                 tag: "textarea",
40241                 style:"width:300px;height:60px;",
40242                 autocomplete: "new-password"
40243             };
40244         }
40245         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
40246         if(this.grow){
40247             this.textSizeEl = Roo.DomHelper.append(document.body, {
40248                 tag: "pre", cls: "x-form-grow-sizer"
40249             });
40250             if(this.preventScrollbars){
40251                 this.el.setStyle("overflow", "hidden");
40252             }
40253             this.el.setHeight(this.growMin);
40254         }
40255     },
40256
40257     onDestroy : function(){
40258         if(this.textSizeEl){
40259             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
40260         }
40261         Roo.form.TextArea.superclass.onDestroy.call(this);
40262     },
40263
40264     // private
40265     onKeyUp : function(e){
40266         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
40267             this.autoSize();
40268         }
40269     },
40270
40271     /**
40272      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
40273      * This only takes effect if grow = true, and fires the autosize event if the height changes.
40274      */
40275     autoSize : function(){
40276         if(!this.grow || !this.textSizeEl){
40277             return;
40278         }
40279         var el = this.el;
40280         var v = el.dom.value;
40281         var ts = this.textSizeEl;
40282
40283         ts.innerHTML = '';
40284         ts.appendChild(document.createTextNode(v));
40285         v = ts.innerHTML;
40286
40287         Roo.fly(ts).setWidth(this.el.getWidth());
40288         if(v.length < 1){
40289             v = "&#160;&#160;";
40290         }else{
40291             if(Roo.isIE){
40292                 v = v.replace(/\n/g, '<p>&#160;</p>');
40293             }
40294             v += "&#160;\n&#160;";
40295         }
40296         ts.innerHTML = v;
40297         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
40298         if(h != this.lastHeight){
40299             this.lastHeight = h;
40300             this.el.setHeight(h);
40301             this.fireEvent("autosize", this, h);
40302         }
40303     }
40304 });/*
40305  * Based on:
40306  * Ext JS Library 1.1.1
40307  * Copyright(c) 2006-2007, Ext JS, LLC.
40308  *
40309  * Originally Released Under LGPL - original licence link has changed is not relivant.
40310  *
40311  * Fork - LGPL
40312  * <script type="text/javascript">
40313  */
40314  
40315
40316 /**
40317  * @class Roo.form.NumberField
40318  * @extends Roo.form.TextField
40319  * Numeric text field that provides automatic keystroke filtering and numeric validation.
40320  * @constructor
40321  * Creates a new NumberField
40322  * @param {Object} config Configuration options
40323  */
40324 Roo.form.NumberField = function(config){
40325     Roo.form.NumberField.superclass.constructor.call(this, config);
40326 };
40327
40328 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
40329     /**
40330      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
40331      */
40332     fieldClass: "x-form-field x-form-num-field",
40333     /**
40334      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40335      */
40336     allowDecimals : true,
40337     /**
40338      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40339      */
40340     decimalSeparator : ".",
40341     /**
40342      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40343      */
40344     decimalPrecision : 2,
40345     /**
40346      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40347      */
40348     allowNegative : true,
40349     /**
40350      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40351      */
40352     minValue : Number.NEGATIVE_INFINITY,
40353     /**
40354      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40355      */
40356     maxValue : Number.MAX_VALUE,
40357     /**
40358      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40359      */
40360     minText : "The minimum value for this field is {0}",
40361     /**
40362      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40363      */
40364     maxText : "The maximum value for this field is {0}",
40365     /**
40366      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40367      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40368      */
40369     nanText : "{0} is not a valid number",
40370
40371     // private
40372     initEvents : function(){
40373         Roo.form.NumberField.superclass.initEvents.call(this);
40374         var allowed = "0123456789";
40375         if(this.allowDecimals){
40376             allowed += this.decimalSeparator;
40377         }
40378         if(this.allowNegative){
40379             allowed += "-";
40380         }
40381         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40382         var keyPress = function(e){
40383             var k = e.getKey();
40384             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40385                 return;
40386             }
40387             var c = e.getCharCode();
40388             if(allowed.indexOf(String.fromCharCode(c)) === -1){
40389                 e.stopEvent();
40390             }
40391         };
40392         this.el.on("keypress", keyPress, this);
40393     },
40394
40395     // private
40396     validateValue : function(value){
40397         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
40398             return false;
40399         }
40400         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
40401              return true;
40402         }
40403         var num = this.parseValue(value);
40404         if(isNaN(num)){
40405             this.markInvalid(String.format(this.nanText, value));
40406             return false;
40407         }
40408         if(num < this.minValue){
40409             this.markInvalid(String.format(this.minText, this.minValue));
40410             return false;
40411         }
40412         if(num > this.maxValue){
40413             this.markInvalid(String.format(this.maxText, this.maxValue));
40414             return false;
40415         }
40416         return true;
40417     },
40418
40419     getValue : function(){
40420         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
40421     },
40422
40423     // private
40424     parseValue : function(value){
40425         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40426         return isNaN(value) ? '' : value;
40427     },
40428
40429     // private
40430     fixPrecision : function(value){
40431         var nan = isNaN(value);
40432         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40433             return nan ? '' : value;
40434         }
40435         return parseFloat(value).toFixed(this.decimalPrecision);
40436     },
40437
40438     setValue : function(v){
40439         v = this.fixPrecision(v);
40440         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
40441     },
40442
40443     // private
40444     decimalPrecisionFcn : function(v){
40445         return Math.floor(v);
40446     },
40447
40448     beforeBlur : function(){
40449         var v = this.parseValue(this.getRawValue());
40450         if(v){
40451             this.setValue(v);
40452         }
40453     }
40454 });/*
40455  * Based on:
40456  * Ext JS Library 1.1.1
40457  * Copyright(c) 2006-2007, Ext JS, LLC.
40458  *
40459  * Originally Released Under LGPL - original licence link has changed is not relivant.
40460  *
40461  * Fork - LGPL
40462  * <script type="text/javascript">
40463  */
40464  
40465 /**
40466  * @class Roo.form.DateField
40467  * @extends Roo.form.TriggerField
40468  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
40469 * @constructor
40470 * Create a new DateField
40471 * @param {Object} config
40472  */
40473 Roo.form.DateField = function(config)
40474 {
40475     Roo.form.DateField.superclass.constructor.call(this, config);
40476     
40477       this.addEvents({
40478          
40479         /**
40480          * @event select
40481          * Fires when a date is selected
40482              * @param {Roo.form.DateField} combo This combo box
40483              * @param {Date} date The date selected
40484              */
40485         'select' : true
40486          
40487     });
40488     
40489     
40490     if(typeof this.minValue == "string") {
40491         this.minValue = this.parseDate(this.minValue);
40492     }
40493     if(typeof this.maxValue == "string") {
40494         this.maxValue = this.parseDate(this.maxValue);
40495     }
40496     this.ddMatch = null;
40497     if(this.disabledDates){
40498         var dd = this.disabledDates;
40499         var re = "(?:";
40500         for(var i = 0; i < dd.length; i++){
40501             re += dd[i];
40502             if(i != dd.length-1) {
40503                 re += "|";
40504             }
40505         }
40506         this.ddMatch = new RegExp(re + ")");
40507     }
40508 };
40509
40510 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
40511     /**
40512      * @cfg {String} format
40513      * The default date format string which can be overriden for localization support.  The format must be
40514      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
40515      */
40516     format : "m/d/y",
40517     /**
40518      * @cfg {String} altFormats
40519      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
40520      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
40521      */
40522     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
40523     /**
40524      * @cfg {Array} disabledDays
40525      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
40526      */
40527     disabledDays : null,
40528     /**
40529      * @cfg {String} disabledDaysText
40530      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
40531      */
40532     disabledDaysText : "Disabled",
40533     /**
40534      * @cfg {Array} disabledDates
40535      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
40536      * expression so they are very powerful. Some examples:
40537      * <ul>
40538      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
40539      * <li>["03/08", "09/16"] would disable those days for every year</li>
40540      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
40541      * <li>["03/../2006"] would disable every day in March 2006</li>
40542      * <li>["^03"] would disable every day in every March</li>
40543      * </ul>
40544      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
40545      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
40546      */
40547     disabledDates : null,
40548     /**
40549      * @cfg {String} disabledDatesText
40550      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
40551      */
40552     disabledDatesText : "Disabled",
40553     /**
40554      * @cfg {Date/String} minValue
40555      * The minimum allowed date. Can be either a Javascript date object or a string date in a
40556      * valid format (defaults to null).
40557      */
40558     minValue : null,
40559     /**
40560      * @cfg {Date/String} maxValue
40561      * The maximum allowed date. Can be either a Javascript date object or a string date in a
40562      * valid format (defaults to null).
40563      */
40564     maxValue : null,
40565     /**
40566      * @cfg {String} minText
40567      * The error text to display when the date in the cell is before minValue (defaults to
40568      * 'The date in this field must be after {minValue}').
40569      */
40570     minText : "The date in this field must be equal to or after {0}",
40571     /**
40572      * @cfg {String} maxText
40573      * The error text to display when the date in the cell is after maxValue (defaults to
40574      * 'The date in this field must be before {maxValue}').
40575      */
40576     maxText : "The date in this field must be equal to or before {0}",
40577     /**
40578      * @cfg {String} invalidText
40579      * The error text to display when the date in the field is invalid (defaults to
40580      * '{value} is not a valid date - it must be in the format {format}').
40581      */
40582     invalidText : "{0} is not a valid date - it must be in the format {1}",
40583     /**
40584      * @cfg {String} triggerClass
40585      * An additional CSS class used to style the trigger button.  The trigger will always get the
40586      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
40587      * which displays a calendar icon).
40588      */
40589     triggerClass : 'x-form-date-trigger',
40590     
40591
40592     /**
40593      * @cfg {Boolean} useIso
40594      * if enabled, then the date field will use a hidden field to store the 
40595      * real value as iso formated date. default (false)
40596      */ 
40597     useIso : false,
40598     /**
40599      * @cfg {String/Object} autoCreate
40600      * A DomHelper element spec, or true for a default element spec (defaults to
40601      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
40602      */ 
40603     // private
40604     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
40605     
40606     // private
40607     hiddenField: false,
40608     
40609     onRender : function(ct, position)
40610     {
40611         Roo.form.DateField.superclass.onRender.call(this, ct, position);
40612         if (this.useIso) {
40613             //this.el.dom.removeAttribute('name'); 
40614             Roo.log("Changing name?");
40615             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
40616             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
40617                     'before', true);
40618             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
40619             // prevent input submission
40620             this.hiddenName = this.name;
40621         }
40622             
40623             
40624     },
40625     
40626     // private
40627     validateValue : function(value)
40628     {
40629         value = this.formatDate(value);
40630         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
40631             Roo.log('super failed');
40632             return false;
40633         }
40634         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
40635              return true;
40636         }
40637         var svalue = value;
40638         value = this.parseDate(value);
40639         if(!value){
40640             Roo.log('parse date failed' + svalue);
40641             this.markInvalid(String.format(this.invalidText, svalue, this.format));
40642             return false;
40643         }
40644         var time = value.getTime();
40645         if(this.minValue && time < this.minValue.getTime()){
40646             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
40647             return false;
40648         }
40649         if(this.maxValue && time > this.maxValue.getTime()){
40650             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
40651             return false;
40652         }
40653         if(this.disabledDays){
40654             var day = value.getDay();
40655             for(var i = 0; i < this.disabledDays.length; i++) {
40656                 if(day === this.disabledDays[i]){
40657                     this.markInvalid(this.disabledDaysText);
40658                     return false;
40659                 }
40660             }
40661         }
40662         var fvalue = this.formatDate(value);
40663         if(this.ddMatch && this.ddMatch.test(fvalue)){
40664             this.markInvalid(String.format(this.disabledDatesText, fvalue));
40665             return false;
40666         }
40667         return true;
40668     },
40669
40670     // private
40671     // Provides logic to override the default TriggerField.validateBlur which just returns true
40672     validateBlur : function(){
40673         return !this.menu || !this.menu.isVisible();
40674     },
40675     
40676     getName: function()
40677     {
40678         // returns hidden if it's set..
40679         if (!this.rendered) {return ''};
40680         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
40681         
40682     },
40683
40684     /**
40685      * Returns the current date value of the date field.
40686      * @return {Date} The date value
40687      */
40688     getValue : function(){
40689         
40690         return  this.hiddenField ?
40691                 this.hiddenField.value :
40692                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
40693     },
40694
40695     /**
40696      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
40697      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
40698      * (the default format used is "m/d/y").
40699      * <br />Usage:
40700      * <pre><code>
40701 //All of these calls set the same date value (May 4, 2006)
40702
40703 //Pass a date object:
40704 var dt = new Date('5/4/06');
40705 dateField.setValue(dt);
40706
40707 //Pass a date string (default format):
40708 dateField.setValue('5/4/06');
40709
40710 //Pass a date string (custom format):
40711 dateField.format = 'Y-m-d';
40712 dateField.setValue('2006-5-4');
40713 </code></pre>
40714      * @param {String/Date} date The date or valid date string
40715      */
40716     setValue : function(date){
40717         if (this.hiddenField) {
40718             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
40719         }
40720         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
40721         // make sure the value field is always stored as a date..
40722         this.value = this.parseDate(date);
40723         
40724         
40725     },
40726
40727     // private
40728     parseDate : function(value){
40729         if(!value || value instanceof Date){
40730             return value;
40731         }
40732         var v = Date.parseDate(value, this.format);
40733          if (!v && this.useIso) {
40734             v = Date.parseDate(value, 'Y-m-d');
40735         }
40736         if(!v && this.altFormats){
40737             if(!this.altFormatsArray){
40738                 this.altFormatsArray = this.altFormats.split("|");
40739             }
40740             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
40741                 v = Date.parseDate(value, this.altFormatsArray[i]);
40742             }
40743         }
40744         return v;
40745     },
40746
40747     // private
40748     formatDate : function(date, fmt){
40749         return (!date || !(date instanceof Date)) ?
40750                date : date.dateFormat(fmt || this.format);
40751     },
40752
40753     // private
40754     menuListeners : {
40755         select: function(m, d){
40756             
40757             this.setValue(d);
40758             this.fireEvent('select', this, d);
40759         },
40760         show : function(){ // retain focus styling
40761             this.onFocus();
40762         },
40763         hide : function(){
40764             this.focus.defer(10, this);
40765             var ml = this.menuListeners;
40766             this.menu.un("select", ml.select,  this);
40767             this.menu.un("show", ml.show,  this);
40768             this.menu.un("hide", ml.hide,  this);
40769         }
40770     },
40771
40772     // private
40773     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
40774     onTriggerClick : function(){
40775         if(this.disabled){
40776             return;
40777         }
40778         if(this.menu == null){
40779             this.menu = new Roo.menu.DateMenu();
40780         }
40781         Roo.apply(this.menu.picker,  {
40782             showClear: this.allowBlank,
40783             minDate : this.minValue,
40784             maxDate : this.maxValue,
40785             disabledDatesRE : this.ddMatch,
40786             disabledDatesText : this.disabledDatesText,
40787             disabledDays : this.disabledDays,
40788             disabledDaysText : this.disabledDaysText,
40789             format : this.useIso ? 'Y-m-d' : this.format,
40790             minText : String.format(this.minText, this.formatDate(this.minValue)),
40791             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
40792         });
40793         this.menu.on(Roo.apply({}, this.menuListeners, {
40794             scope:this
40795         }));
40796         this.menu.picker.setValue(this.getValue() || new Date());
40797         this.menu.show(this.el, "tl-bl?");
40798     },
40799
40800     beforeBlur : function(){
40801         var v = this.parseDate(this.getRawValue());
40802         if(v){
40803             this.setValue(v);
40804         }
40805     },
40806
40807     /*@
40808      * overide
40809      * 
40810      */
40811     isDirty : function() {
40812         if(this.disabled) {
40813             return false;
40814         }
40815         
40816         if(typeof(this.startValue) === 'undefined'){
40817             return false;
40818         }
40819         
40820         return String(this.getValue()) !== String(this.startValue);
40821         
40822     },
40823     // @overide
40824     cleanLeadingSpace : function(e)
40825     {
40826        return;
40827     }
40828     
40829 });/*
40830  * Based on:
40831  * Ext JS Library 1.1.1
40832  * Copyright(c) 2006-2007, Ext JS, LLC.
40833  *
40834  * Originally Released Under LGPL - original licence link has changed is not relivant.
40835  *
40836  * Fork - LGPL
40837  * <script type="text/javascript">
40838  */
40839  
40840 /**
40841  * @class Roo.form.MonthField
40842  * @extends Roo.form.TriggerField
40843  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
40844 * @constructor
40845 * Create a new MonthField
40846 * @param {Object} config
40847  */
40848 Roo.form.MonthField = function(config){
40849     
40850     Roo.form.MonthField.superclass.constructor.call(this, config);
40851     
40852       this.addEvents({
40853          
40854         /**
40855          * @event select
40856          * Fires when a date is selected
40857              * @param {Roo.form.MonthFieeld} combo This combo box
40858              * @param {Date} date The date selected
40859              */
40860         'select' : true
40861          
40862     });
40863     
40864     
40865     if(typeof this.minValue == "string") {
40866         this.minValue = this.parseDate(this.minValue);
40867     }
40868     if(typeof this.maxValue == "string") {
40869         this.maxValue = this.parseDate(this.maxValue);
40870     }
40871     this.ddMatch = null;
40872     if(this.disabledDates){
40873         var dd = this.disabledDates;
40874         var re = "(?:";
40875         for(var i = 0; i < dd.length; i++){
40876             re += dd[i];
40877             if(i != dd.length-1) {
40878                 re += "|";
40879             }
40880         }
40881         this.ddMatch = new RegExp(re + ")");
40882     }
40883 };
40884
40885 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
40886     /**
40887      * @cfg {String} format
40888      * The default date format string which can be overriden for localization support.  The format must be
40889      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
40890      */
40891     format : "M Y",
40892     /**
40893      * @cfg {String} altFormats
40894      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
40895      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
40896      */
40897     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
40898     /**
40899      * @cfg {Array} disabledDays
40900      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
40901      */
40902     disabledDays : [0,1,2,3,4,5,6],
40903     /**
40904      * @cfg {String} disabledDaysText
40905      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
40906      */
40907     disabledDaysText : "Disabled",
40908     /**
40909      * @cfg {Array} disabledDates
40910      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
40911      * expression so they are very powerful. Some examples:
40912      * <ul>
40913      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
40914      * <li>["03/08", "09/16"] would disable those days for every year</li>
40915      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
40916      * <li>["03/../2006"] would disable every day in March 2006</li>
40917      * <li>["^03"] would disable every day in every March</li>
40918      * </ul>
40919      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
40920      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
40921      */
40922     disabledDates : null,
40923     /**
40924      * @cfg {String} disabledDatesText
40925      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
40926      */
40927     disabledDatesText : "Disabled",
40928     /**
40929      * @cfg {Date/String} minValue
40930      * The minimum allowed date. Can be either a Javascript date object or a string date in a
40931      * valid format (defaults to null).
40932      */
40933     minValue : null,
40934     /**
40935      * @cfg {Date/String} maxValue
40936      * The maximum allowed date. Can be either a Javascript date object or a string date in a
40937      * valid format (defaults to null).
40938      */
40939     maxValue : null,
40940     /**
40941      * @cfg {String} minText
40942      * The error text to display when the date in the cell is before minValue (defaults to
40943      * 'The date in this field must be after {minValue}').
40944      */
40945     minText : "The date in this field must be equal to or after {0}",
40946     /**
40947      * @cfg {String} maxTextf
40948      * The error text to display when the date in the cell is after maxValue (defaults to
40949      * 'The date in this field must be before {maxValue}').
40950      */
40951     maxText : "The date in this field must be equal to or before {0}",
40952     /**
40953      * @cfg {String} invalidText
40954      * The error text to display when the date in the field is invalid (defaults to
40955      * '{value} is not a valid date - it must be in the format {format}').
40956      */
40957     invalidText : "{0} is not a valid date - it must be in the format {1}",
40958     /**
40959      * @cfg {String} triggerClass
40960      * An additional CSS class used to style the trigger button.  The trigger will always get the
40961      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
40962      * which displays a calendar icon).
40963      */
40964     triggerClass : 'x-form-date-trigger',
40965     
40966
40967     /**
40968      * @cfg {Boolean} useIso
40969      * if enabled, then the date field will use a hidden field to store the 
40970      * real value as iso formated date. default (true)
40971      */ 
40972     useIso : true,
40973     /**
40974      * @cfg {String/Object} autoCreate
40975      * A DomHelper element spec, or true for a default element spec (defaults to
40976      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
40977      */ 
40978     // private
40979     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
40980     
40981     // private
40982     hiddenField: false,
40983     
40984     hideMonthPicker : false,
40985     
40986     onRender : function(ct, position)
40987     {
40988         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
40989         if (this.useIso) {
40990             this.el.dom.removeAttribute('name'); 
40991             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
40992                     'before', true);
40993             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
40994             // prevent input submission
40995             this.hiddenName = this.name;
40996         }
40997             
40998             
40999     },
41000     
41001     // private
41002     validateValue : function(value)
41003     {
41004         value = this.formatDate(value);
41005         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
41006             return false;
41007         }
41008         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
41009              return true;
41010         }
41011         var svalue = value;
41012         value = this.parseDate(value);
41013         if(!value){
41014             this.markInvalid(String.format(this.invalidText, svalue, this.format));
41015             return false;
41016         }
41017         var time = value.getTime();
41018         if(this.minValue && time < this.minValue.getTime()){
41019             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
41020             return false;
41021         }
41022         if(this.maxValue && time > this.maxValue.getTime()){
41023             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
41024             return false;
41025         }
41026         /*if(this.disabledDays){
41027             var day = value.getDay();
41028             for(var i = 0; i < this.disabledDays.length; i++) {
41029                 if(day === this.disabledDays[i]){
41030                     this.markInvalid(this.disabledDaysText);
41031                     return false;
41032                 }
41033             }
41034         }
41035         */
41036         var fvalue = this.formatDate(value);
41037         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
41038             this.markInvalid(String.format(this.disabledDatesText, fvalue));
41039             return false;
41040         }
41041         */
41042         return true;
41043     },
41044
41045     // private
41046     // Provides logic to override the default TriggerField.validateBlur which just returns true
41047     validateBlur : function(){
41048         return !this.menu || !this.menu.isVisible();
41049     },
41050
41051     /**
41052      * Returns the current date value of the date field.
41053      * @return {Date} The date value
41054      */
41055     getValue : function(){
41056         
41057         
41058         
41059         return  this.hiddenField ?
41060                 this.hiddenField.value :
41061                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
41062     },
41063
41064     /**
41065      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
41066      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
41067      * (the default format used is "m/d/y").
41068      * <br />Usage:
41069      * <pre><code>
41070 //All of these calls set the same date value (May 4, 2006)
41071
41072 //Pass a date object:
41073 var dt = new Date('5/4/06');
41074 monthField.setValue(dt);
41075
41076 //Pass a date string (default format):
41077 monthField.setValue('5/4/06');
41078
41079 //Pass a date string (custom format):
41080 monthField.format = 'Y-m-d';
41081 monthField.setValue('2006-5-4');
41082 </code></pre>
41083      * @param {String/Date} date The date or valid date string
41084      */
41085     setValue : function(date){
41086         Roo.log('month setValue' + date);
41087         // can only be first of month..
41088         
41089         var val = this.parseDate(date);
41090         
41091         if (this.hiddenField) {
41092             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
41093         }
41094         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
41095         this.value = this.parseDate(date);
41096     },
41097
41098     // private
41099     parseDate : function(value){
41100         if(!value || value instanceof Date){
41101             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
41102             return value;
41103         }
41104         var v = Date.parseDate(value, this.format);
41105         if (!v && this.useIso) {
41106             v = Date.parseDate(value, 'Y-m-d');
41107         }
41108         if (v) {
41109             // 
41110             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
41111         }
41112         
41113         
41114         if(!v && this.altFormats){
41115             if(!this.altFormatsArray){
41116                 this.altFormatsArray = this.altFormats.split("|");
41117             }
41118             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
41119                 v = Date.parseDate(value, this.altFormatsArray[i]);
41120             }
41121         }
41122         return v;
41123     },
41124
41125     // private
41126     formatDate : function(date, fmt){
41127         return (!date || !(date instanceof Date)) ?
41128                date : date.dateFormat(fmt || this.format);
41129     },
41130
41131     // private
41132     menuListeners : {
41133         select: function(m, d){
41134             this.setValue(d);
41135             this.fireEvent('select', this, d);
41136         },
41137         show : function(){ // retain focus styling
41138             this.onFocus();
41139         },
41140         hide : function(){
41141             this.focus.defer(10, this);
41142             var ml = this.menuListeners;
41143             this.menu.un("select", ml.select,  this);
41144             this.menu.un("show", ml.show,  this);
41145             this.menu.un("hide", ml.hide,  this);
41146         }
41147     },
41148     // private
41149     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
41150     onTriggerClick : function(){
41151         if(this.disabled){
41152             return;
41153         }
41154         if(this.menu == null){
41155             this.menu = new Roo.menu.DateMenu();
41156            
41157         }
41158         
41159         Roo.apply(this.menu.picker,  {
41160             
41161             showClear: this.allowBlank,
41162             minDate : this.minValue,
41163             maxDate : this.maxValue,
41164             disabledDatesRE : this.ddMatch,
41165             disabledDatesText : this.disabledDatesText,
41166             
41167             format : this.useIso ? 'Y-m-d' : this.format,
41168             minText : String.format(this.minText, this.formatDate(this.minValue)),
41169             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
41170             
41171         });
41172          this.menu.on(Roo.apply({}, this.menuListeners, {
41173             scope:this
41174         }));
41175        
41176         
41177         var m = this.menu;
41178         var p = m.picker;
41179         
41180         // hide month picker get's called when we called by 'before hide';
41181         
41182         var ignorehide = true;
41183         p.hideMonthPicker  = function(disableAnim){
41184             if (ignorehide) {
41185                 return;
41186             }
41187              if(this.monthPicker){
41188                 Roo.log("hideMonthPicker called");
41189                 if(disableAnim === true){
41190                     this.monthPicker.hide();
41191                 }else{
41192                     this.monthPicker.slideOut('t', {duration:.2});
41193                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
41194                     p.fireEvent("select", this, this.value);
41195                     m.hide();
41196                 }
41197             }
41198         }
41199         
41200         Roo.log('picker set value');
41201         Roo.log(this.getValue());
41202         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
41203         m.show(this.el, 'tl-bl?');
41204         ignorehide  = false;
41205         // this will trigger hideMonthPicker..
41206         
41207         
41208         // hidden the day picker
41209         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
41210         
41211         
41212         
41213       
41214         
41215         p.showMonthPicker.defer(100, p);
41216     
41217         
41218        
41219     },
41220
41221     beforeBlur : function(){
41222         var v = this.parseDate(this.getRawValue());
41223         if(v){
41224             this.setValue(v);
41225         }
41226     }
41227
41228     /** @cfg {Boolean} grow @hide */
41229     /** @cfg {Number} growMin @hide */
41230     /** @cfg {Number} growMax @hide */
41231     /**
41232      * @hide
41233      * @method autoSize
41234      */
41235 });/*
41236  * Based on:
41237  * Ext JS Library 1.1.1
41238  * Copyright(c) 2006-2007, Ext JS, LLC.
41239  *
41240  * Originally Released Under LGPL - original licence link has changed is not relivant.
41241  *
41242  * Fork - LGPL
41243  * <script type="text/javascript">
41244  */
41245  
41246
41247 /**
41248  * @class Roo.form.ComboBox
41249  * @extends Roo.form.TriggerField
41250  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
41251  * @constructor
41252  * Create a new ComboBox.
41253  * @param {Object} config Configuration options
41254  */
41255 Roo.form.ComboBox = function(config){
41256     Roo.form.ComboBox.superclass.constructor.call(this, config);
41257     this.addEvents({
41258         /**
41259          * @event expand
41260          * Fires when the dropdown list is expanded
41261              * @param {Roo.form.ComboBox} combo This combo box
41262              */
41263         'expand' : true,
41264         /**
41265          * @event collapse
41266          * Fires when the dropdown list is collapsed
41267              * @param {Roo.form.ComboBox} combo This combo box
41268              */
41269         'collapse' : true,
41270         /**
41271          * @event beforeselect
41272          * Fires before a list item is selected. Return false to cancel the selection.
41273              * @param {Roo.form.ComboBox} combo This combo box
41274              * @param {Roo.data.Record} record The data record returned from the underlying store
41275              * @param {Number} index The index of the selected item in the dropdown list
41276              */
41277         'beforeselect' : true,
41278         /**
41279          * @event select
41280          * Fires when a list item is selected
41281              * @param {Roo.form.ComboBox} combo This combo box
41282              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
41283              * @param {Number} index The index of the selected item in the dropdown list
41284              */
41285         'select' : true,
41286         /**
41287          * @event beforequery
41288          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
41289          * The event object passed has these properties:
41290              * @param {Roo.form.ComboBox} combo This combo box
41291              * @param {String} query The query
41292              * @param {Boolean} forceAll true to force "all" query
41293              * @param {Boolean} cancel true to cancel the query
41294              * @param {Object} e The query event object
41295              */
41296         'beforequery': true,
41297          /**
41298          * @event add
41299          * Fires when the 'add' icon is pressed (add a listener to enable add button)
41300              * @param {Roo.form.ComboBox} combo This combo box
41301              */
41302         'add' : true,
41303         /**
41304          * @event edit
41305          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
41306              * @param {Roo.form.ComboBox} combo This combo box
41307              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
41308              */
41309         'edit' : true
41310         
41311         
41312     });
41313     if(this.transform){
41314         this.allowDomMove = false;
41315         var s = Roo.getDom(this.transform);
41316         if(!this.hiddenName){
41317             this.hiddenName = s.name;
41318         }
41319         if(!this.store){
41320             this.mode = 'local';
41321             var d = [], opts = s.options;
41322             for(var i = 0, len = opts.length;i < len; i++){
41323                 var o = opts[i];
41324                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
41325                 if(o.selected) {
41326                     this.value = value;
41327                 }
41328                 d.push([value, o.text]);
41329             }
41330             this.store = new Roo.data.SimpleStore({
41331                 'id': 0,
41332                 fields: ['value', 'text'],
41333                 data : d
41334             });
41335             this.valueField = 'value';
41336             this.displayField = 'text';
41337         }
41338         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
41339         if(!this.lazyRender){
41340             this.target = true;
41341             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
41342             s.parentNode.removeChild(s); // remove it
41343             this.render(this.el.parentNode);
41344         }else{
41345             s.parentNode.removeChild(s); // remove it
41346         }
41347
41348     }
41349     if (this.store) {
41350         this.store = Roo.factory(this.store, Roo.data);
41351     }
41352     
41353     this.selectedIndex = -1;
41354     if(this.mode == 'local'){
41355         if(config.queryDelay === undefined){
41356             this.queryDelay = 10;
41357         }
41358         if(config.minChars === undefined){
41359             this.minChars = 0;
41360         }
41361     }
41362 };
41363
41364 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
41365     /**
41366      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
41367      */
41368     /**
41369      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
41370      * rendering into an Roo.Editor, defaults to false)
41371      */
41372     /**
41373      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
41374      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
41375      */
41376     /**
41377      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
41378      */
41379     /**
41380      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
41381      * the dropdown list (defaults to undefined, with no header element)
41382      */
41383
41384      /**
41385      * @cfg {String/Roo.Template} tpl The template to use to render the output
41386      */
41387      
41388     // private
41389     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
41390     /**
41391      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
41392      */
41393     listWidth: undefined,
41394     /**
41395      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
41396      * mode = 'remote' or 'text' if mode = 'local')
41397      */
41398     displayField: undefined,
41399     /**
41400      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
41401      * mode = 'remote' or 'value' if mode = 'local'). 
41402      * Note: use of a valueField requires the user make a selection
41403      * in order for a value to be mapped.
41404      */
41405     valueField: undefined,
41406     
41407     
41408     /**
41409      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
41410      * field's data value (defaults to the underlying DOM element's name)
41411      */
41412     hiddenName: undefined,
41413     /**
41414      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
41415      */
41416     listClass: '',
41417     /**
41418      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
41419      */
41420     selectedClass: 'x-combo-selected',
41421     /**
41422      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
41423      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
41424      * which displays a downward arrow icon).
41425      */
41426     triggerClass : 'x-form-arrow-trigger',
41427     /**
41428      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
41429      */
41430     shadow:'sides',
41431     /**
41432      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
41433      * anchor positions (defaults to 'tl-bl')
41434      */
41435     listAlign: 'tl-bl?',
41436     /**
41437      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
41438      */
41439     maxHeight: 300,
41440     /**
41441      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
41442      * query specified by the allQuery config option (defaults to 'query')
41443      */
41444     triggerAction: 'query',
41445     /**
41446      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
41447      * (defaults to 4, does not apply if editable = false)
41448      */
41449     minChars : 4,
41450     /**
41451      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
41452      * delay (typeAheadDelay) if it matches a known value (defaults to false)
41453      */
41454     typeAhead: false,
41455     /**
41456      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
41457      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
41458      */
41459     queryDelay: 500,
41460     /**
41461      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
41462      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
41463      */
41464     pageSize: 0,
41465     /**
41466      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
41467      * when editable = true (defaults to false)
41468      */
41469     selectOnFocus:false,
41470     /**
41471      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
41472      */
41473     queryParam: 'query',
41474     /**
41475      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
41476      * when mode = 'remote' (defaults to 'Loading...')
41477      */
41478     loadingText: 'Loading...',
41479     /**
41480      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
41481      */
41482     resizable: false,
41483     /**
41484      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
41485      */
41486     handleHeight : 8,
41487     /**
41488      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
41489      * traditional select (defaults to true)
41490      */
41491     editable: true,
41492     /**
41493      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
41494      */
41495     allQuery: '',
41496     /**
41497      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
41498      */
41499     mode: 'remote',
41500     /**
41501      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
41502      * listWidth has a higher value)
41503      */
41504     minListWidth : 70,
41505     /**
41506      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
41507      * allow the user to set arbitrary text into the field (defaults to false)
41508      */
41509     forceSelection:false,
41510     /**
41511      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
41512      * if typeAhead = true (defaults to 250)
41513      */
41514     typeAheadDelay : 250,
41515     /**
41516      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
41517      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
41518      */
41519     valueNotFoundText : undefined,
41520     /**
41521      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
41522      */
41523     blockFocus : false,
41524     
41525     /**
41526      * @cfg {Boolean} disableClear Disable showing of clear button.
41527      */
41528     disableClear : false,
41529     /**
41530      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
41531      */
41532     alwaysQuery : false,
41533     
41534     //private
41535     addicon : false,
41536     editicon: false,
41537     
41538     // element that contains real text value.. (when hidden is used..)
41539      
41540     // private
41541     onRender : function(ct, position)
41542     {
41543         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
41544         
41545         if(this.hiddenName){
41546             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
41547                     'before', true);
41548             this.hiddenField.value =
41549                 this.hiddenValue !== undefined ? this.hiddenValue :
41550                 this.value !== undefined ? this.value : '';
41551
41552             // prevent input submission
41553             this.el.dom.removeAttribute('name');
41554              
41555              
41556         }
41557         
41558         if(Roo.isGecko){
41559             this.el.dom.setAttribute('autocomplete', 'off');
41560         }
41561
41562         var cls = 'x-combo-list';
41563
41564         this.list = new Roo.Layer({
41565             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
41566         });
41567
41568         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
41569         this.list.setWidth(lw);
41570         this.list.swallowEvent('mousewheel');
41571         this.assetHeight = 0;
41572
41573         if(this.title){
41574             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
41575             this.assetHeight += this.header.getHeight();
41576         }
41577
41578         this.innerList = this.list.createChild({cls:cls+'-inner'});
41579         this.innerList.on('mouseover', this.onViewOver, this);
41580         this.innerList.on('mousemove', this.onViewMove, this);
41581         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
41582         
41583         if(this.allowBlank && !this.pageSize && !this.disableClear){
41584             this.footer = this.list.createChild({cls:cls+'-ft'});
41585             this.pageTb = new Roo.Toolbar(this.footer);
41586            
41587         }
41588         if(this.pageSize){
41589             this.footer = this.list.createChild({cls:cls+'-ft'});
41590             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
41591                     {pageSize: this.pageSize});
41592             
41593         }
41594         
41595         if (this.pageTb && this.allowBlank && !this.disableClear) {
41596             var _this = this;
41597             this.pageTb.add(new Roo.Toolbar.Fill(), {
41598                 cls: 'x-btn-icon x-btn-clear',
41599                 text: '&#160;',
41600                 handler: function()
41601                 {
41602                     _this.collapse();
41603                     _this.clearValue();
41604                     _this.onSelect(false, -1);
41605                 }
41606             });
41607         }
41608         if (this.footer) {
41609             this.assetHeight += this.footer.getHeight();
41610         }
41611         
41612
41613         if(!this.tpl){
41614             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
41615         }
41616
41617         this.view = new Roo.View(this.innerList, this.tpl, {
41618             singleSelect:true,
41619             store: this.store,
41620             selectedClass: this.selectedClass
41621         });
41622
41623         this.view.on('click', this.onViewClick, this);
41624
41625         this.store.on('beforeload', this.onBeforeLoad, this);
41626         this.store.on('load', this.onLoad, this);
41627         this.store.on('loadexception', this.onLoadException, this);
41628
41629         if(this.resizable){
41630             this.resizer = new Roo.Resizable(this.list,  {
41631                pinned:true, handles:'se'
41632             });
41633             this.resizer.on('resize', function(r, w, h){
41634                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
41635                 this.listWidth = w;
41636                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
41637                 this.restrictHeight();
41638             }, this);
41639             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
41640         }
41641         if(!this.editable){
41642             this.editable = true;
41643             this.setEditable(false);
41644         }  
41645         
41646         
41647         if (typeof(this.events.add.listeners) != 'undefined') {
41648             
41649             this.addicon = this.wrap.createChild(
41650                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
41651        
41652             this.addicon.on('click', function(e) {
41653                 this.fireEvent('add', this);
41654             }, this);
41655         }
41656         if (typeof(this.events.edit.listeners) != 'undefined') {
41657             
41658             this.editicon = this.wrap.createChild(
41659                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
41660             if (this.addicon) {
41661                 this.editicon.setStyle('margin-left', '40px');
41662             }
41663             this.editicon.on('click', function(e) {
41664                 
41665                 // we fire even  if inothing is selected..
41666                 this.fireEvent('edit', this, this.lastData );
41667                 
41668             }, this);
41669         }
41670         
41671         
41672         
41673     },
41674
41675     // private
41676     initEvents : function(){
41677         Roo.form.ComboBox.superclass.initEvents.call(this);
41678
41679         this.keyNav = new Roo.KeyNav(this.el, {
41680             "up" : function(e){
41681                 this.inKeyMode = true;
41682                 this.selectPrev();
41683             },
41684
41685             "down" : function(e){
41686                 if(!this.isExpanded()){
41687                     this.onTriggerClick();
41688                 }else{
41689                     this.inKeyMode = true;
41690                     this.selectNext();
41691                 }
41692             },
41693
41694             "enter" : function(e){
41695                 this.onViewClick();
41696                 //return true;
41697             },
41698
41699             "esc" : function(e){
41700                 this.collapse();
41701             },
41702
41703             "tab" : function(e){
41704                 this.onViewClick(false);
41705                 this.fireEvent("specialkey", this, e);
41706                 return true;
41707             },
41708
41709             scope : this,
41710
41711             doRelay : function(foo, bar, hname){
41712                 if(hname == 'down' || this.scope.isExpanded()){
41713                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
41714                 }
41715                 return true;
41716             },
41717
41718             forceKeyDown: true
41719         });
41720         this.queryDelay = Math.max(this.queryDelay || 10,
41721                 this.mode == 'local' ? 10 : 250);
41722         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
41723         if(this.typeAhead){
41724             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
41725         }
41726         if(this.editable !== false){
41727             this.el.on("keyup", this.onKeyUp, this);
41728         }
41729         if(this.forceSelection){
41730             this.on('blur', this.doForce, this);
41731         }
41732     },
41733
41734     onDestroy : function(){
41735         if(this.view){
41736             this.view.setStore(null);
41737             this.view.el.removeAllListeners();
41738             this.view.el.remove();
41739             this.view.purgeListeners();
41740         }
41741         if(this.list){
41742             this.list.destroy();
41743         }
41744         if(this.store){
41745             this.store.un('beforeload', this.onBeforeLoad, this);
41746             this.store.un('load', this.onLoad, this);
41747             this.store.un('loadexception', this.onLoadException, this);
41748         }
41749         Roo.form.ComboBox.superclass.onDestroy.call(this);
41750     },
41751
41752     // private
41753     fireKey : function(e){
41754         if(e.isNavKeyPress() && !this.list.isVisible()){
41755             this.fireEvent("specialkey", this, e);
41756         }
41757     },
41758
41759     // private
41760     onResize: function(w, h){
41761         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
41762         
41763         if(typeof w != 'number'){
41764             // we do not handle it!?!?
41765             return;
41766         }
41767         var tw = this.trigger.getWidth();
41768         tw += this.addicon ? this.addicon.getWidth() : 0;
41769         tw += this.editicon ? this.editicon.getWidth() : 0;
41770         var x = w - tw;
41771         this.el.setWidth( this.adjustWidth('input', x));
41772             
41773         this.trigger.setStyle('left', x+'px');
41774         
41775         if(this.list && this.listWidth === undefined){
41776             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
41777             this.list.setWidth(lw);
41778             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
41779         }
41780         
41781     
41782         
41783     },
41784
41785     /**
41786      * Allow or prevent the user from directly editing the field text.  If false is passed,
41787      * the user will only be able to select from the items defined in the dropdown list.  This method
41788      * is the runtime equivalent of setting the 'editable' config option at config time.
41789      * @param {Boolean} value True to allow the user to directly edit the field text
41790      */
41791     setEditable : function(value){
41792         if(value == this.editable){
41793             return;
41794         }
41795         this.editable = value;
41796         if(!value){
41797             this.el.dom.setAttribute('readOnly', true);
41798             this.el.on('mousedown', this.onTriggerClick,  this);
41799             this.el.addClass('x-combo-noedit');
41800         }else{
41801             this.el.dom.setAttribute('readOnly', false);
41802             this.el.un('mousedown', this.onTriggerClick,  this);
41803             this.el.removeClass('x-combo-noedit');
41804         }
41805     },
41806
41807     // private
41808     onBeforeLoad : function(){
41809         if(!this.hasFocus){
41810             return;
41811         }
41812         this.innerList.update(this.loadingText ?
41813                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
41814         this.restrictHeight();
41815         this.selectedIndex = -1;
41816     },
41817
41818     // private
41819     onLoad : function(){
41820         if(!this.hasFocus){
41821             return;
41822         }
41823         if(this.store.getCount() > 0){
41824             this.expand();
41825             this.restrictHeight();
41826             if(this.lastQuery == this.allQuery){
41827                 if(this.editable){
41828                     this.el.dom.select();
41829                 }
41830                 if(!this.selectByValue(this.value, true)){
41831                     this.select(0, true);
41832                 }
41833             }else{
41834                 this.selectNext();
41835                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
41836                     this.taTask.delay(this.typeAheadDelay);
41837                 }
41838             }
41839         }else{
41840             this.onEmptyResults();
41841         }
41842         //this.el.focus();
41843     },
41844     // private
41845     onLoadException : function()
41846     {
41847         this.collapse();
41848         Roo.log(this.store.reader.jsonData);
41849         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
41850             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
41851         }
41852         
41853         
41854     },
41855     // private
41856     onTypeAhead : function(){
41857         if(this.store.getCount() > 0){
41858             var r = this.store.getAt(0);
41859             var newValue = r.data[this.displayField];
41860             var len = newValue.length;
41861             var selStart = this.getRawValue().length;
41862             if(selStart != len){
41863                 this.setRawValue(newValue);
41864                 this.selectText(selStart, newValue.length);
41865             }
41866         }
41867     },
41868
41869     // private
41870     onSelect : function(record, index){
41871         if(this.fireEvent('beforeselect', this, record, index) !== false){
41872             this.setFromData(index > -1 ? record.data : false);
41873             this.collapse();
41874             this.fireEvent('select', this, record, index);
41875         }
41876     },
41877
41878     /**
41879      * Returns the currently selected field value or empty string if no value is set.
41880      * @return {String} value The selected value
41881      */
41882     getValue : function(){
41883         if(this.valueField){
41884             return typeof this.value != 'undefined' ? this.value : '';
41885         }
41886         return Roo.form.ComboBox.superclass.getValue.call(this);
41887     },
41888
41889     /**
41890      * Clears any text/value currently set in the field
41891      */
41892     clearValue : function(){
41893         if(this.hiddenField){
41894             this.hiddenField.value = '';
41895         }
41896         this.value = '';
41897         this.setRawValue('');
41898         this.lastSelectionText = '';
41899         
41900     },
41901
41902     /**
41903      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
41904      * will be displayed in the field.  If the value does not match the data value of an existing item,
41905      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
41906      * Otherwise the field will be blank (although the value will still be set).
41907      * @param {String} value The value to match
41908      */
41909     setValue : function(v){
41910         var text = v;
41911         if(this.valueField){
41912             var r = this.findRecord(this.valueField, v);
41913             if(r){
41914                 text = r.data[this.displayField];
41915             }else if(this.valueNotFoundText !== undefined){
41916                 text = this.valueNotFoundText;
41917             }
41918         }
41919         this.lastSelectionText = text;
41920         if(this.hiddenField){
41921             this.hiddenField.value = v;
41922         }
41923         Roo.form.ComboBox.superclass.setValue.call(this, text);
41924         this.value = v;
41925     },
41926     /**
41927      * @property {Object} the last set data for the element
41928      */
41929     
41930     lastData : false,
41931     /**
41932      * Sets the value of the field based on a object which is related to the record format for the store.
41933      * @param {Object} value the value to set as. or false on reset?
41934      */
41935     setFromData : function(o){
41936         var dv = ''; // display value
41937         var vv = ''; // value value..
41938         this.lastData = o;
41939         if (this.displayField) {
41940             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
41941         } else {
41942             // this is an error condition!!!
41943             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
41944         }
41945         
41946         if(this.valueField){
41947             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
41948         }
41949         if(this.hiddenField){
41950             this.hiddenField.value = vv;
41951             
41952             this.lastSelectionText = dv;
41953             Roo.form.ComboBox.superclass.setValue.call(this, dv);
41954             this.value = vv;
41955             return;
41956         }
41957         // no hidden field.. - we store the value in 'value', but still display
41958         // display field!!!!
41959         this.lastSelectionText = dv;
41960         Roo.form.ComboBox.superclass.setValue.call(this, dv);
41961         this.value = vv;
41962         
41963         
41964     },
41965     // private
41966     reset : function(){
41967         // overridden so that last data is reset..
41968         this.setValue(this.resetValue);
41969         this.originalValue = this.getValue();
41970         this.clearInvalid();
41971         this.lastData = false;
41972         if (this.view) {
41973             this.view.clearSelections();
41974         }
41975     },
41976     // private
41977     findRecord : function(prop, value){
41978         var record;
41979         if(this.store.getCount() > 0){
41980             this.store.each(function(r){
41981                 if(r.data[prop] == value){
41982                     record = r;
41983                     return false;
41984                 }
41985                 return true;
41986             });
41987         }
41988         return record;
41989     },
41990     
41991     getName: function()
41992     {
41993         // returns hidden if it's set..
41994         if (!this.rendered) {return ''};
41995         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
41996         
41997     },
41998     // private
41999     onViewMove : function(e, t){
42000         this.inKeyMode = false;
42001     },
42002
42003     // private
42004     onViewOver : function(e, t){
42005         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
42006             return;
42007         }
42008         var item = this.view.findItemFromChild(t);
42009         if(item){
42010             var index = this.view.indexOf(item);
42011             this.select(index, false);
42012         }
42013     },
42014
42015     // private
42016     onViewClick : function(doFocus)
42017     {
42018         var index = this.view.getSelectedIndexes()[0];
42019         var r = this.store.getAt(index);
42020         if(r){
42021             this.onSelect(r, index);
42022         }
42023         if(doFocus !== false && !this.blockFocus){
42024             this.el.focus();
42025         }
42026     },
42027
42028     // private
42029     restrictHeight : function(){
42030         this.innerList.dom.style.height = '';
42031         var inner = this.innerList.dom;
42032         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
42033         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
42034         this.list.beginUpdate();
42035         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
42036         this.list.alignTo(this.el, this.listAlign);
42037         this.list.endUpdate();
42038     },
42039
42040     // private
42041     onEmptyResults : function(){
42042         this.collapse();
42043     },
42044
42045     /**
42046      * Returns true if the dropdown list is expanded, else false.
42047      */
42048     isExpanded : function(){
42049         return this.list.isVisible();
42050     },
42051
42052     /**
42053      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
42054      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
42055      * @param {String} value The data value of the item to select
42056      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
42057      * selected item if it is not currently in view (defaults to true)
42058      * @return {Boolean} True if the value matched an item in the list, else false
42059      */
42060     selectByValue : function(v, scrollIntoView){
42061         if(v !== undefined && v !== null){
42062             var r = this.findRecord(this.valueField || this.displayField, v);
42063             if(r){
42064                 this.select(this.store.indexOf(r), scrollIntoView);
42065                 return true;
42066             }
42067         }
42068         return false;
42069     },
42070
42071     /**
42072      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
42073      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
42074      * @param {Number} index The zero-based index of the list item to select
42075      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
42076      * selected item if it is not currently in view (defaults to true)
42077      */
42078     select : function(index, scrollIntoView){
42079         this.selectedIndex = index;
42080         this.view.select(index);
42081         if(scrollIntoView !== false){
42082             var el = this.view.getNode(index);
42083             if(el){
42084                 this.innerList.scrollChildIntoView(el, false);
42085             }
42086         }
42087     },
42088
42089     // private
42090     selectNext : function(){
42091         var ct = this.store.getCount();
42092         if(ct > 0){
42093             if(this.selectedIndex == -1){
42094                 this.select(0);
42095             }else if(this.selectedIndex < ct-1){
42096                 this.select(this.selectedIndex+1);
42097             }
42098         }
42099     },
42100
42101     // private
42102     selectPrev : function(){
42103         var ct = this.store.getCount();
42104         if(ct > 0){
42105             if(this.selectedIndex == -1){
42106                 this.select(0);
42107             }else if(this.selectedIndex != 0){
42108                 this.select(this.selectedIndex-1);
42109             }
42110         }
42111     },
42112
42113     // private
42114     onKeyUp : function(e){
42115         if(this.editable !== false && !e.isSpecialKey()){
42116             this.lastKey = e.getKey();
42117             this.dqTask.delay(this.queryDelay);
42118         }
42119     },
42120
42121     // private
42122     validateBlur : function(){
42123         return !this.list || !this.list.isVisible();   
42124     },
42125
42126     // private
42127     initQuery : function(){
42128         this.doQuery(this.getRawValue());
42129     },
42130
42131     // private
42132     doForce : function(){
42133         if(this.el.dom.value.length > 0){
42134             this.el.dom.value =
42135                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
42136              
42137         }
42138     },
42139
42140     /**
42141      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
42142      * query allowing the query action to be canceled if needed.
42143      * @param {String} query The SQL query to execute
42144      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
42145      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
42146      * saved in the current store (defaults to false)
42147      */
42148     doQuery : function(q, forceAll){
42149         if(q === undefined || q === null){
42150             q = '';
42151         }
42152         var qe = {
42153             query: q,
42154             forceAll: forceAll,
42155             combo: this,
42156             cancel:false
42157         };
42158         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
42159             return false;
42160         }
42161         q = qe.query;
42162         forceAll = qe.forceAll;
42163         if(forceAll === true || (q.length >= this.minChars)){
42164             if(this.lastQuery != q || this.alwaysQuery){
42165                 this.lastQuery = q;
42166                 if(this.mode == 'local'){
42167                     this.selectedIndex = -1;
42168                     if(forceAll){
42169                         this.store.clearFilter();
42170                     }else{
42171                         this.store.filter(this.displayField, q);
42172                     }
42173                     this.onLoad();
42174                 }else{
42175                     this.store.baseParams[this.queryParam] = q;
42176                     this.store.load({
42177                         params: this.getParams(q)
42178                     });
42179                     this.expand();
42180                 }
42181             }else{
42182                 this.selectedIndex = -1;
42183                 this.onLoad();   
42184             }
42185         }
42186     },
42187
42188     // private
42189     getParams : function(q){
42190         var p = {};
42191         //p[this.queryParam] = q;
42192         if(this.pageSize){
42193             p.start = 0;
42194             p.limit = this.pageSize;
42195         }
42196         return p;
42197     },
42198
42199     /**
42200      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
42201      */
42202     collapse : function(){
42203         if(!this.isExpanded()){
42204             return;
42205         }
42206         this.list.hide();
42207         Roo.get(document).un('mousedown', this.collapseIf, this);
42208         Roo.get(document).un('mousewheel', this.collapseIf, this);
42209         if (!this.editable) {
42210             Roo.get(document).un('keydown', this.listKeyPress, this);
42211         }
42212         this.fireEvent('collapse', this);
42213     },
42214
42215     // private
42216     collapseIf : function(e){
42217         if(!e.within(this.wrap) && !e.within(this.list)){
42218             this.collapse();
42219         }
42220     },
42221
42222     /**
42223      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
42224      */
42225     expand : function(){
42226         if(this.isExpanded() || !this.hasFocus){
42227             return;
42228         }
42229         this.list.alignTo(this.el, this.listAlign);
42230         this.list.show();
42231         Roo.get(document).on('mousedown', this.collapseIf, this);
42232         Roo.get(document).on('mousewheel', this.collapseIf, this);
42233         if (!this.editable) {
42234             Roo.get(document).on('keydown', this.listKeyPress, this);
42235         }
42236         
42237         this.fireEvent('expand', this);
42238     },
42239
42240     // private
42241     // Implements the default empty TriggerField.onTriggerClick function
42242     onTriggerClick : function(){
42243         if(this.disabled){
42244             return;
42245         }
42246         if(this.isExpanded()){
42247             this.collapse();
42248             if (!this.blockFocus) {
42249                 this.el.focus();
42250             }
42251             
42252         }else {
42253             this.hasFocus = true;
42254             if(this.triggerAction == 'all') {
42255                 this.doQuery(this.allQuery, true);
42256             } else {
42257                 this.doQuery(this.getRawValue());
42258             }
42259             if (!this.blockFocus) {
42260                 this.el.focus();
42261             }
42262         }
42263     },
42264     listKeyPress : function(e)
42265     {
42266         //Roo.log('listkeypress');
42267         // scroll to first matching element based on key pres..
42268         if (e.isSpecialKey()) {
42269             return false;
42270         }
42271         var k = String.fromCharCode(e.getKey()).toUpperCase();
42272         //Roo.log(k);
42273         var match  = false;
42274         var csel = this.view.getSelectedNodes();
42275         var cselitem = false;
42276         if (csel.length) {
42277             var ix = this.view.indexOf(csel[0]);
42278             cselitem  = this.store.getAt(ix);
42279             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
42280                 cselitem = false;
42281             }
42282             
42283         }
42284         
42285         this.store.each(function(v) { 
42286             if (cselitem) {
42287                 // start at existing selection.
42288                 if (cselitem.id == v.id) {
42289                     cselitem = false;
42290                 }
42291                 return;
42292             }
42293                 
42294             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
42295                 match = this.store.indexOf(v);
42296                 return false;
42297             }
42298         }, this);
42299         
42300         if (match === false) {
42301             return true; // no more action?
42302         }
42303         // scroll to?
42304         this.view.select(match);
42305         var sn = Roo.get(this.view.getSelectedNodes()[0]);
42306         sn.scrollIntoView(sn.dom.parentNode, false);
42307     } 
42308
42309     /** 
42310     * @cfg {Boolean} grow 
42311     * @hide 
42312     */
42313     /** 
42314     * @cfg {Number} growMin 
42315     * @hide 
42316     */
42317     /** 
42318     * @cfg {Number} growMax 
42319     * @hide 
42320     */
42321     /**
42322      * @hide
42323      * @method autoSize
42324      */
42325 });/*
42326  * Copyright(c) 2010-2012, Roo J Solutions Limited
42327  *
42328  * Licence LGPL
42329  *
42330  */
42331
42332 /**
42333  * @class Roo.form.ComboBoxArray
42334  * @extends Roo.form.TextField
42335  * A facebook style adder... for lists of email / people / countries  etc...
42336  * pick multiple items from a combo box, and shows each one.
42337  *
42338  *  Fred [x]  Brian [x]  [Pick another |v]
42339  *
42340  *
42341  *  For this to work: it needs various extra information
42342  *    - normal combo problay has
42343  *      name, hiddenName
42344  *    + displayField, valueField
42345  *
42346  *    For our purpose...
42347  *
42348  *
42349  *   If we change from 'extends' to wrapping...
42350  *   
42351  *  
42352  *
42353  
42354  
42355  * @constructor
42356  * Create a new ComboBoxArray.
42357  * @param {Object} config Configuration options
42358  */
42359  
42360
42361 Roo.form.ComboBoxArray = function(config)
42362 {
42363     this.addEvents({
42364         /**
42365          * @event beforeremove
42366          * Fires before remove the value from the list
42367              * @param {Roo.form.ComboBoxArray} _self This combo box array
42368              * @param {Roo.form.ComboBoxArray.Item} item removed item
42369              */
42370         'beforeremove' : true,
42371         /**
42372          * @event remove
42373          * Fires when remove the value from the list
42374              * @param {Roo.form.ComboBoxArray} _self This combo box array
42375              * @param {Roo.form.ComboBoxArray.Item} item removed item
42376              */
42377         'remove' : true
42378         
42379         
42380     });
42381     
42382     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
42383     
42384     this.items = new Roo.util.MixedCollection(false);
42385     
42386     // construct the child combo...
42387     
42388     
42389     
42390     
42391    
42392     
42393 }
42394
42395  
42396 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
42397
42398     /**
42399      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
42400      */
42401     
42402     lastData : false,
42403     
42404     // behavies liek a hiddne field
42405     inputType:      'hidden',
42406     /**
42407      * @cfg {Number} width The width of the box that displays the selected element
42408      */ 
42409     width:          300,
42410
42411     
42412     
42413     /**
42414      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
42415      */
42416     name : false,
42417     /**
42418      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
42419      */
42420     hiddenName : false,
42421       /**
42422      * @cfg {String} seperator    The value seperator normally ',' 
42423      */
42424     seperator : ',',
42425     
42426     // private the array of items that are displayed..
42427     items  : false,
42428     // private - the hidden field el.
42429     hiddenEl : false,
42430     // private - the filed el..
42431     el : false,
42432     
42433     //validateValue : function() { return true; }, // all values are ok!
42434     //onAddClick: function() { },
42435     
42436     onRender : function(ct, position) 
42437     {
42438         
42439         // create the standard hidden element
42440         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
42441         
42442         
42443         // give fake names to child combo;
42444         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
42445         this.combo.name = this.name ? (this.name+'-subcombo') : this.name;
42446         
42447         this.combo = Roo.factory(this.combo, Roo.form);
42448         this.combo.onRender(ct, position);
42449         if (typeof(this.combo.width) != 'undefined') {
42450             this.combo.onResize(this.combo.width,0);
42451         }
42452         
42453         this.combo.initEvents();
42454         
42455         // assigned so form know we need to do this..
42456         this.store          = this.combo.store;
42457         this.valueField     = this.combo.valueField;
42458         this.displayField   = this.combo.displayField ;
42459         
42460         
42461         this.combo.wrap.addClass('x-cbarray-grp');
42462         
42463         var cbwrap = this.combo.wrap.createChild(
42464             {tag: 'div', cls: 'x-cbarray-cb'},
42465             this.combo.el.dom
42466         );
42467         
42468              
42469         this.hiddenEl = this.combo.wrap.createChild({
42470             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
42471         });
42472         this.el = this.combo.wrap.createChild({
42473             tag: 'input',  type:'hidden' , name: this.name, value : ''
42474         });
42475          //   this.el.dom.removeAttribute("name");
42476         
42477         
42478         this.outerWrap = this.combo.wrap;
42479         this.wrap = cbwrap;
42480         
42481         this.outerWrap.setWidth(this.width);
42482         this.outerWrap.dom.removeChild(this.el.dom);
42483         
42484         this.wrap.dom.appendChild(this.el.dom);
42485         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
42486         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
42487         
42488         this.combo.trigger.setStyle('position','relative');
42489         this.combo.trigger.setStyle('left', '0px');
42490         this.combo.trigger.setStyle('top', '2px');
42491         
42492         this.combo.el.setStyle('vertical-align', 'text-bottom');
42493         
42494         //this.trigger.setStyle('vertical-align', 'top');
42495         
42496         // this should use the code from combo really... on('add' ....)
42497         if (this.adder) {
42498             
42499         
42500             this.adder = this.outerWrap.createChild(
42501                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
42502             var _t = this;
42503             this.adder.on('click', function(e) {
42504                 _t.fireEvent('adderclick', this, e);
42505             }, _t);
42506         }
42507         //var _t = this;
42508         //this.adder.on('click', this.onAddClick, _t);
42509         
42510         
42511         this.combo.on('select', function(cb, rec, ix) {
42512             this.addItem(rec.data);
42513             
42514             cb.setValue('');
42515             cb.el.dom.value = '';
42516             //cb.lastData = rec.data;
42517             // add to list
42518             
42519         }, this);
42520         
42521         
42522     },
42523     
42524     
42525     getName: function()
42526     {
42527         // returns hidden if it's set..
42528         if (!this.rendered) {return ''};
42529         return  this.hiddenName ? this.hiddenName : this.name;
42530         
42531     },
42532     
42533     
42534     onResize: function(w, h){
42535         
42536         return;
42537         // not sure if this is needed..
42538         //this.combo.onResize(w,h);
42539         
42540         if(typeof w != 'number'){
42541             // we do not handle it!?!?
42542             return;
42543         }
42544         var tw = this.combo.trigger.getWidth();
42545         tw += this.addicon ? this.addicon.getWidth() : 0;
42546         tw += this.editicon ? this.editicon.getWidth() : 0;
42547         var x = w - tw;
42548         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
42549             
42550         this.combo.trigger.setStyle('left', '0px');
42551         
42552         if(this.list && this.listWidth === undefined){
42553             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
42554             this.list.setWidth(lw);
42555             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
42556         }
42557         
42558     
42559         
42560     },
42561     
42562     addItem: function(rec)
42563     {
42564         var valueField = this.combo.valueField;
42565         var displayField = this.combo.displayField;
42566         
42567         if (this.items.indexOfKey(rec[valueField]) > -1) {
42568             //console.log("GOT " + rec.data.id);
42569             return;
42570         }
42571         
42572         var x = new Roo.form.ComboBoxArray.Item({
42573             //id : rec[this.idField],
42574             data : rec,
42575             displayField : displayField ,
42576             tipField : displayField ,
42577             cb : this
42578         });
42579         // use the 
42580         this.items.add(rec[valueField],x);
42581         // add it before the element..
42582         this.updateHiddenEl();
42583         x.render(this.outerWrap, this.wrap.dom);
42584         // add the image handler..
42585     },
42586     
42587     updateHiddenEl : function()
42588     {
42589         this.validate();
42590         if (!this.hiddenEl) {
42591             return;
42592         }
42593         var ar = [];
42594         var idField = this.combo.valueField;
42595         
42596         this.items.each(function(f) {
42597             ar.push(f.data[idField]);
42598         });
42599         this.hiddenEl.dom.value = ar.join(this.seperator);
42600         this.validate();
42601     },
42602     
42603     reset : function()
42604     {
42605         this.items.clear();
42606         
42607         Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
42608            el.remove();
42609         });
42610         
42611         this.el.dom.value = '';
42612         if (this.hiddenEl) {
42613             this.hiddenEl.dom.value = '';
42614         }
42615         
42616     },
42617     getValue: function()
42618     {
42619         return this.hiddenEl ? this.hiddenEl.dom.value : '';
42620     },
42621     setValue: function(v) // not a valid action - must use addItems..
42622     {
42623         
42624         this.reset();
42625          
42626         if (this.store.isLocal && (typeof(v) == 'string')) {
42627             // then we can use the store to find the values..
42628             // comma seperated at present.. this needs to allow JSON based encoding..
42629             this.hiddenEl.value  = v;
42630             var v_ar = [];
42631             Roo.each(v.split(this.seperator), function(k) {
42632                 Roo.log("CHECK " + this.valueField + ',' + k);
42633                 var li = this.store.query(this.valueField, k);
42634                 if (!li.length) {
42635                     return;
42636                 }
42637                 var add = {};
42638                 add[this.valueField] = k;
42639                 add[this.displayField] = li.item(0).data[this.displayField];
42640                 
42641                 this.addItem(add);
42642             }, this) 
42643              
42644         }
42645         if (typeof(v) == 'object' ) {
42646             // then let's assume it's an array of objects..
42647             Roo.each(v, function(l) {
42648                 var add = l;
42649                 if (typeof(l) == 'string') {
42650                     add = {};
42651                     add[this.valueField] = l;
42652                     add[this.displayField] = l
42653                 }
42654                 this.addItem(add);
42655             }, this);
42656              
42657         }
42658         
42659         
42660     },
42661     setFromData: function(v)
42662     {
42663         // this recieves an object, if setValues is called.
42664         this.reset();
42665         this.el.dom.value = v[this.displayField];
42666         this.hiddenEl.dom.value = v[this.valueField];
42667         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
42668             return;
42669         }
42670         var kv = v[this.valueField];
42671         var dv = v[this.displayField];
42672         kv = typeof(kv) != 'string' ? '' : kv;
42673         dv = typeof(dv) != 'string' ? '' : dv;
42674         
42675         
42676         var keys = kv.split(this.seperator);
42677         var display = dv.split(this.seperator);
42678         for (var i = 0 ; i < keys.length; i++) {
42679             add = {};
42680             add[this.valueField] = keys[i];
42681             add[this.displayField] = display[i];
42682             this.addItem(add);
42683         }
42684       
42685         
42686     },
42687     
42688     /**
42689      * Validates the combox array value
42690      * @return {Boolean} True if the value is valid, else false
42691      */
42692     validate : function(){
42693         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
42694             this.clearInvalid();
42695             return true;
42696         }
42697         return false;
42698     },
42699     
42700     validateValue : function(value){
42701         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
42702         
42703     },
42704     
42705     /*@
42706      * overide
42707      * 
42708      */
42709     isDirty : function() {
42710         if(this.disabled) {
42711             return false;
42712         }
42713         
42714         try {
42715             var d = Roo.decode(String(this.originalValue));
42716         } catch (e) {
42717             return String(this.getValue()) !== String(this.originalValue);
42718         }
42719         
42720         var originalValue = [];
42721         
42722         for (var i = 0; i < d.length; i++){
42723             originalValue.push(d[i][this.valueField]);
42724         }
42725         
42726         return String(this.getValue()) !== String(originalValue.join(this.seperator));
42727         
42728     }
42729     
42730 });
42731
42732
42733
42734 /**
42735  * @class Roo.form.ComboBoxArray.Item
42736  * @extends Roo.BoxComponent
42737  * A selected item in the list
42738  *  Fred [x]  Brian [x]  [Pick another |v]
42739  * 
42740  * @constructor
42741  * Create a new item.
42742  * @param {Object} config Configuration options
42743  */
42744  
42745 Roo.form.ComboBoxArray.Item = function(config) {
42746     config.id = Roo.id();
42747     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
42748 }
42749
42750 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
42751     data : {},
42752     cb: false,
42753     displayField : false,
42754     tipField : false,
42755     
42756     
42757     defaultAutoCreate : {
42758         tag: 'div',
42759         cls: 'x-cbarray-item',
42760         cn : [ 
42761             { tag: 'div' },
42762             {
42763                 tag: 'img',
42764                 width:16,
42765                 height : 16,
42766                 src : Roo.BLANK_IMAGE_URL ,
42767                 align: 'center'
42768             }
42769         ]
42770         
42771     },
42772     
42773  
42774     onRender : function(ct, position)
42775     {
42776         Roo.form.Field.superclass.onRender.call(this, ct, position);
42777         
42778         if(!this.el){
42779             var cfg = this.getAutoCreate();
42780             this.el = ct.createChild(cfg, position);
42781         }
42782         
42783         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
42784         
42785         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
42786             this.cb.renderer(this.data) :
42787             String.format('{0}',this.data[this.displayField]);
42788         
42789             
42790         this.el.child('div').dom.setAttribute('qtip',
42791                         String.format('{0}',this.data[this.tipField])
42792         );
42793         
42794         this.el.child('img').on('click', this.remove, this);
42795         
42796     },
42797    
42798     remove : function()
42799     {
42800         if(this.cb.disabled){
42801             return;
42802         }
42803         
42804         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
42805             this.cb.items.remove(this);
42806             this.el.child('img').un('click', this.remove, this);
42807             this.el.remove();
42808             this.cb.updateHiddenEl();
42809
42810             this.cb.fireEvent('remove', this.cb, this);
42811         }
42812         
42813     }
42814 });/*
42815  * RooJS Library 1.1.1
42816  * Copyright(c) 2008-2011  Alan Knowles
42817  *
42818  * License - LGPL
42819  */
42820  
42821
42822 /**
42823  * @class Roo.form.ComboNested
42824  * @extends Roo.form.ComboBox
42825  * A combobox for that allows selection of nested items in a list,
42826  * eg.
42827  *
42828  *  Book
42829  *    -> red
42830  *    -> green
42831  *  Table
42832  *    -> square
42833  *      ->red
42834  *      ->green
42835  *    -> rectangle
42836  *      ->green
42837  *      
42838  * 
42839  * @constructor
42840  * Create a new ComboNested
42841  * @param {Object} config Configuration options
42842  */
42843 Roo.form.ComboNested = function(config){
42844     Roo.form.ComboCheck.superclass.constructor.call(this, config);
42845     // should verify some data...
42846     // like
42847     // hiddenName = required..
42848     // displayField = required
42849     // valudField == required
42850     var req= [ 'hiddenName', 'displayField', 'valueField' ];
42851     var _t = this;
42852     Roo.each(req, function(e) {
42853         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
42854             throw "Roo.form.ComboNested : missing value for: " + e;
42855         }
42856     });
42857      
42858     
42859 };
42860
42861 Roo.extend(Roo.form.ComboNested, Roo.form.ComboBox, {
42862    
42863     /*
42864      * @config {Number} max Number of columns to show
42865      */
42866     
42867     maxColumns : 3,
42868    
42869     list : null, // the outermost div..
42870     innerLists : null, // the
42871     views : null,
42872     stores : null,
42873     // private
42874     loadingChildren : false,
42875     
42876     onRender : function(ct, position)
42877     {
42878         Roo.form.ComboBox.superclass.onRender.call(this, ct, position); // skip parent call - got to above..
42879         
42880         if(this.hiddenName){
42881             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
42882                     'before', true);
42883             this.hiddenField.value =
42884                 this.hiddenValue !== undefined ? this.hiddenValue :
42885                 this.value !== undefined ? this.value : '';
42886
42887             // prevent input submission
42888             this.el.dom.removeAttribute('name');
42889              
42890              
42891         }
42892         
42893         if(Roo.isGecko){
42894             this.el.dom.setAttribute('autocomplete', 'off');
42895         }
42896
42897         var cls = 'x-combo-list';
42898
42899         this.list = new Roo.Layer({
42900             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
42901         });
42902
42903         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
42904         this.list.setWidth(lw);
42905         this.list.swallowEvent('mousewheel');
42906         this.assetHeight = 0;
42907
42908         if(this.title){
42909             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
42910             this.assetHeight += this.header.getHeight();
42911         }
42912         this.innerLists = [];
42913         this.views = [];
42914         this.stores = [];
42915         for (var i =0 ; i < this.maxColumns; i++) {
42916             this.onRenderList( cls, i);
42917         }
42918         
42919         // always needs footer, as we are going to have an 'OK' button.
42920         this.footer = this.list.createChild({cls:cls+'-ft'});
42921         this.pageTb = new Roo.Toolbar(this.footer);  
42922         var _this = this;
42923         this.pageTb.add(  {
42924             
42925             text: 'Done',
42926             handler: function()
42927             {
42928                 _this.collapse();
42929             }
42930         });
42931         
42932         if ( this.allowBlank && !this.disableClear) {
42933             
42934             this.pageTb.add(new Roo.Toolbar.Fill(), {
42935                 cls: 'x-btn-icon x-btn-clear',
42936                 text: '&#160;',
42937                 handler: function()
42938                 {
42939                     _this.collapse();
42940                     _this.clearValue();
42941                     _this.onSelect(false, -1);
42942                 }
42943             });
42944         }
42945         if (this.footer) {
42946             this.assetHeight += this.footer.getHeight();
42947         }
42948         
42949     },
42950     onRenderList : function (  cls, i)
42951     {
42952         
42953         var lw = Math.floor(
42954                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
42955         );
42956         
42957         this.list.setWidth(lw); // default to '1'
42958
42959         var il = this.innerLists[i] = this.list.createChild({cls:cls+'-inner'});
42960         //il.on('mouseover', this.onViewOver, this, { list:  i });
42961         //il.on('mousemove', this.onViewMove, this, { list:  i });
42962         il.setWidth(lw);
42963         il.setStyle({ 'overflow-x' : 'hidden'});
42964
42965         if(!this.tpl){
42966             this.tpl = new Roo.Template({
42967                 html :  '<div class="'+cls+'-item '+cls+'-item-{cn:this.isEmpty}">{' + this.displayField + '}</div>',
42968                 isEmpty: function (value, allValues) {
42969                     //Roo.log(value);
42970                     var dl = typeof(value.data) != 'undefined' ? value.data.length : value.length; ///json is a nested response..
42971                     return dl ? 'has-children' : 'no-children'
42972                 }
42973             });
42974         }
42975         
42976         var store  = this.store;
42977         if (i > 0) {
42978             store  = new Roo.data.SimpleStore({
42979                 //fields : this.store.reader.meta.fields,
42980                 reader : this.store.reader,
42981                 data : [ ]
42982             });
42983         }
42984         this.stores[i]  = store;
42985                   
42986         var view = this.views[i] = new Roo.View(
42987             il,
42988             this.tpl,
42989             {
42990                 singleSelect:true,
42991                 store: store,
42992                 selectedClass: this.selectedClass
42993             }
42994         );
42995         view.getEl().setWidth(lw);
42996         view.getEl().setStyle({
42997             position: i < 1 ? 'relative' : 'absolute',
42998             top: 0,
42999             left: (i * lw ) + 'px',
43000             display : i > 0 ? 'none' : 'block'
43001         });
43002         view.on('selectionchange', this.onSelectChange.createDelegate(this, {list : i }, true));
43003         view.on('dblclick', this.onDoubleClick.createDelegate(this, {list : i }, true));
43004         //view.on('click', this.onViewClick, this, { list : i });
43005
43006         store.on('beforeload', this.onBeforeLoad, this);
43007         store.on('load',  this.onLoad, this, { list  : i});
43008         store.on('loadexception', this.onLoadException, this);
43009
43010         // hide the other vies..
43011         
43012         
43013         
43014     },
43015       
43016     restrictHeight : function()
43017     {
43018         var mh = 0;
43019         Roo.each(this.innerLists, function(il,i) {
43020             var el = this.views[i].getEl();
43021             el.dom.style.height = '';
43022             var inner = el.dom;
43023             var h = Math.max(il.clientHeight, il.offsetHeight, il.scrollHeight);
43024             // only adjust heights on other ones..
43025             mh = Math.max(h, mh);
43026             if (i < 1) {
43027                 
43028                 el.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
43029                 il.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
43030                
43031             }
43032             
43033             
43034         }, this);
43035         
43036         this.list.beginUpdate();
43037         this.list.setHeight(mh+this.list.getFrameWidth('tb')+this.assetHeight);
43038         this.list.alignTo(this.el, this.listAlign);
43039         this.list.endUpdate();
43040         
43041     },
43042      
43043     
43044     // -- store handlers..
43045     // private
43046     onBeforeLoad : function()
43047     {
43048         if(!this.hasFocus){
43049             return;
43050         }
43051         this.innerLists[0].update(this.loadingText ?
43052                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
43053         this.restrictHeight();
43054         this.selectedIndex = -1;
43055     },
43056     // private
43057     onLoad : function(a,b,c,d)
43058     {
43059         if (!this.loadingChildren) {
43060             // then we are loading the top level. - hide the children
43061             for (var i = 1;i < this.views.length; i++) {
43062                 this.views[i].getEl().setStyle({ display : 'none' });
43063             }
43064             var lw = Math.floor(
43065                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
43066             );
43067         
43068              this.list.setWidth(lw); // default to '1'
43069
43070             
43071         }
43072         if(!this.hasFocus){
43073             return;
43074         }
43075         
43076         if(this.store.getCount() > 0) {
43077             this.expand();
43078             this.restrictHeight();   
43079         } else {
43080             this.onEmptyResults();
43081         }
43082         
43083         if (!this.loadingChildren) {
43084             this.selectActive();
43085         }
43086         /*
43087         this.stores[1].loadData([]);
43088         this.stores[2].loadData([]);
43089         this.views
43090         */    
43091     
43092         //this.el.focus();
43093     },
43094     
43095     
43096     // private
43097     onLoadException : function()
43098     {
43099         this.collapse();
43100         Roo.log(this.store.reader.jsonData);
43101         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
43102             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
43103         }
43104         
43105         
43106     },
43107     // no cleaning of leading spaces on blur here.
43108     cleanLeadingSpace : function(e) { },
43109     
43110
43111     onSelectChange : function (view, sels, opts )
43112     {
43113         var ix = view.getSelectedIndexes();
43114          
43115         if (opts.list > this.maxColumns - 2) {
43116             if (view.store.getCount()<  1) {
43117                 this.views[opts.list ].getEl().setStyle({ display :   'none' });
43118
43119             } else  {
43120                 if (ix.length) {
43121                     // used to clear ?? but if we are loading unselected 
43122                     this.setFromData(view.store.getAt(ix[0]).data);
43123                 }
43124                 
43125             }
43126             
43127             return;
43128         }
43129         
43130         if (!ix.length) {
43131             // this get's fired when trigger opens..
43132            // this.setFromData({});
43133             var str = this.stores[opts.list+1];
43134             str.data.clear(); // removeall wihtout the fire events..
43135             return;
43136         }
43137         
43138         var rec = view.store.getAt(ix[0]);
43139          
43140         this.setFromData(rec.data);
43141         this.fireEvent('select', this, rec, ix[0]);
43142         
43143         var lw = Math.floor(
43144              (
43145                 (this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')
43146              ) / this.maxColumns
43147         );
43148         this.loadingChildren = true;
43149         this.stores[opts.list+1].loadDataFromChildren( rec );
43150         this.loadingChildren = false;
43151         var dl = this.stores[opts.list+1]. getTotalCount();
43152         
43153         this.views[opts.list+1].getEl().setHeight( this.innerLists[0].getHeight());
43154         
43155         this.views[opts.list+1].getEl().setStyle({ display : dl ? 'block' : 'none' });
43156         for (var i = opts.list+2; i < this.views.length;i++) {
43157             this.views[i].getEl().setStyle({ display : 'none' });
43158         }
43159         
43160         this.innerLists[opts.list+1].setHeight( this.innerLists[0].getHeight());
43161         this.list.setWidth(lw * (opts.list + (dl ? 2 : 1)));
43162         
43163         if (this.isLoading) {
43164            // this.selectActive(opts.list);
43165         }
43166          
43167     },
43168     
43169     
43170     
43171     
43172     onDoubleClick : function()
43173     {
43174         this.collapse(); //??
43175     },
43176     
43177      
43178     
43179     
43180     
43181     // private
43182     recordToStack : function(store, prop, value, stack)
43183     {
43184         var cstore = new Roo.data.SimpleStore({
43185             //fields : this.store.reader.meta.fields, // we need array reader.. for
43186             reader : this.store.reader,
43187             data : [ ]
43188         });
43189         var _this = this;
43190         var record  = false;
43191         var srec = false;
43192         if(store.getCount() < 1){
43193             return false;
43194         }
43195         store.each(function(r){
43196             if(r.data[prop] == value){
43197                 record = r;
43198             srec = r;
43199                 return false;
43200             }
43201             if (r.data.cn && r.data.cn.length) {
43202                 cstore.loadDataFromChildren( r);
43203                 var cret = _this.recordToStack(cstore, prop, value, stack);
43204                 if (cret !== false) {
43205                     record = cret;
43206                     srec = r;
43207                     return false;
43208                 }
43209             }
43210              
43211             return true;
43212         });
43213         if (record == false) {
43214             return false
43215         }
43216         stack.unshift(srec);
43217         return record;
43218     },
43219     
43220     /*
43221      * find the stack of stores that match our value.
43222      *
43223      * 
43224      */
43225     
43226     selectActive : function ()
43227     {
43228         // if store is not loaded, then we will need to wait for that to happen first.
43229         var stack = [];
43230         this.recordToStack(this.store, this.valueField, this.getValue(), stack);
43231         for (var i = 0; i < stack.length; i++ ) {
43232             this.views[i].select(stack[i].store.indexOf(stack[i]), false, false );
43233         }
43234         
43235     }
43236         
43237          
43238     
43239     
43240     
43241     
43242 });/*
43243  * Based on:
43244  * Ext JS Library 1.1.1
43245  * Copyright(c) 2006-2007, Ext JS, LLC.
43246  *
43247  * Originally Released Under LGPL - original licence link has changed is not relivant.
43248  *
43249  * Fork - LGPL
43250  * <script type="text/javascript">
43251  */
43252 /**
43253  * @class Roo.form.Checkbox
43254  * @extends Roo.form.Field
43255  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
43256  * @constructor
43257  * Creates a new Checkbox
43258  * @param {Object} config Configuration options
43259  */
43260 Roo.form.Checkbox = function(config){
43261     Roo.form.Checkbox.superclass.constructor.call(this, config);
43262     this.addEvents({
43263         /**
43264          * @event check
43265          * Fires when the checkbox is checked or unchecked.
43266              * @param {Roo.form.Checkbox} this This checkbox
43267              * @param {Boolean} checked The new checked value
43268              */
43269         check : true
43270     });
43271 };
43272
43273 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
43274     /**
43275      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
43276      */
43277     focusClass : undefined,
43278     /**
43279      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
43280      */
43281     fieldClass: "x-form-field",
43282     /**
43283      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
43284      */
43285     checked: false,
43286     /**
43287      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
43288      * {tag: "input", type: "checkbox", autocomplete: "off"})
43289      */
43290     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
43291     /**
43292      * @cfg {String} boxLabel The text that appears beside the checkbox
43293      */
43294     boxLabel : "",
43295     /**
43296      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
43297      */  
43298     inputValue : '1',
43299     /**
43300      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
43301      */
43302      valueOff: '0', // value when not checked..
43303
43304     actionMode : 'viewEl', 
43305     //
43306     // private
43307     itemCls : 'x-menu-check-item x-form-item',
43308     groupClass : 'x-menu-group-item',
43309     inputType : 'hidden',
43310     
43311     
43312     inSetChecked: false, // check that we are not calling self...
43313     
43314     inputElement: false, // real input element?
43315     basedOn: false, // ????
43316     
43317     isFormField: true, // not sure where this is needed!!!!
43318
43319     onResize : function(){
43320         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
43321         if(!this.boxLabel){
43322             this.el.alignTo(this.wrap, 'c-c');
43323         }
43324     },
43325
43326     initEvents : function(){
43327         Roo.form.Checkbox.superclass.initEvents.call(this);
43328         this.el.on("click", this.onClick,  this);
43329         this.el.on("change", this.onClick,  this);
43330     },
43331
43332
43333     getResizeEl : function(){
43334         return this.wrap;
43335     },
43336
43337     getPositionEl : function(){
43338         return this.wrap;
43339     },
43340
43341     // private
43342     onRender : function(ct, position){
43343         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
43344         /*
43345         if(this.inputValue !== undefined){
43346             this.el.dom.value = this.inputValue;
43347         }
43348         */
43349         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
43350         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
43351         var viewEl = this.wrap.createChild({ 
43352             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
43353         this.viewEl = viewEl;   
43354         this.wrap.on('click', this.onClick,  this); 
43355         
43356         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
43357         this.el.on('propertychange', this.setFromHidden,  this);  //ie
43358         
43359         
43360         
43361         if(this.boxLabel){
43362             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
43363         //    viewEl.on('click', this.onClick,  this); 
43364         }
43365         //if(this.checked){
43366             this.setChecked(this.checked);
43367         //}else{
43368             //this.checked = this.el.dom;
43369         //}
43370
43371     },
43372
43373     // private
43374     initValue : Roo.emptyFn,
43375
43376     /**
43377      * Returns the checked state of the checkbox.
43378      * @return {Boolean} True if checked, else false
43379      */
43380     getValue : function(){
43381         if(this.el){
43382             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
43383         }
43384         return this.valueOff;
43385         
43386     },
43387
43388         // private
43389     onClick : function(){ 
43390         if (this.disabled) {
43391             return;
43392         }
43393         this.setChecked(!this.checked);
43394
43395         //if(this.el.dom.checked != this.checked){
43396         //    this.setValue(this.el.dom.checked);
43397        // }
43398     },
43399
43400     /**
43401      * Sets the checked state of the checkbox.
43402      * On is always based on a string comparison between inputValue and the param.
43403      * @param {Boolean/String} value - the value to set 
43404      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
43405      */
43406     setValue : function(v,suppressEvent){
43407         
43408         
43409         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
43410         //if(this.el && this.el.dom){
43411         //    this.el.dom.checked = this.checked;
43412         //    this.el.dom.defaultChecked = this.checked;
43413         //}
43414         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
43415         //this.fireEvent("check", this, this.checked);
43416     },
43417     // private..
43418     setChecked : function(state,suppressEvent)
43419     {
43420         if (this.inSetChecked) {
43421             this.checked = state;
43422             return;
43423         }
43424         
43425     
43426         if(this.wrap){
43427             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
43428         }
43429         this.checked = state;
43430         if(suppressEvent !== true){
43431             this.fireEvent('check', this, state);
43432         }
43433         this.inSetChecked = true;
43434         this.el.dom.value = state ? this.inputValue : this.valueOff;
43435         this.inSetChecked = false;
43436         
43437     },
43438     // handle setting of hidden value by some other method!!?!?
43439     setFromHidden: function()
43440     {
43441         if(!this.el){
43442             return;
43443         }
43444         //console.log("SET FROM HIDDEN");
43445         //alert('setFrom hidden');
43446         this.setValue(this.el.dom.value);
43447     },
43448     
43449     onDestroy : function()
43450     {
43451         if(this.viewEl){
43452             Roo.get(this.viewEl).remove();
43453         }
43454          
43455         Roo.form.Checkbox.superclass.onDestroy.call(this);
43456     },
43457     
43458     setBoxLabel : function(str)
43459     {
43460         this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
43461     }
43462
43463 });/*
43464  * Based on:
43465  * Ext JS Library 1.1.1
43466  * Copyright(c) 2006-2007, Ext JS, LLC.
43467  *
43468  * Originally Released Under LGPL - original licence link has changed is not relivant.
43469  *
43470  * Fork - LGPL
43471  * <script type="text/javascript">
43472  */
43473  
43474 /**
43475  * @class Roo.form.Radio
43476  * @extends Roo.form.Checkbox
43477  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
43478  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
43479  * @constructor
43480  * Creates a new Radio
43481  * @param {Object} config Configuration options
43482  */
43483 Roo.form.Radio = function(){
43484     Roo.form.Radio.superclass.constructor.apply(this, arguments);
43485 };
43486 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
43487     inputType: 'radio',
43488
43489     /**
43490      * If this radio is part of a group, it will return the selected value
43491      * @return {String}
43492      */
43493     getGroupValue : function(){
43494         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
43495     },
43496     
43497     
43498     onRender : function(ct, position){
43499         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
43500         
43501         if(this.inputValue !== undefined){
43502             this.el.dom.value = this.inputValue;
43503         }
43504          
43505         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
43506         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
43507         //var viewEl = this.wrap.createChild({ 
43508         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
43509         //this.viewEl = viewEl;   
43510         //this.wrap.on('click', this.onClick,  this); 
43511         
43512         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
43513         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
43514         
43515         
43516         
43517         if(this.boxLabel){
43518             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
43519         //    viewEl.on('click', this.onClick,  this); 
43520         }
43521          if(this.checked){
43522             this.el.dom.checked =   'checked' ;
43523         }
43524          
43525     } 
43526     
43527     
43528 });//<script type="text/javascript">
43529
43530 /*
43531  * Based  Ext JS Library 1.1.1
43532  * Copyright(c) 2006-2007, Ext JS, LLC.
43533  * LGPL
43534  *
43535  */
43536  
43537 /**
43538  * @class Roo.HtmlEditorCore
43539  * @extends Roo.Component
43540  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
43541  *
43542  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
43543  */
43544
43545 Roo.HtmlEditorCore = function(config){
43546     
43547     
43548     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
43549     
43550     
43551     this.addEvents({
43552         /**
43553          * @event initialize
43554          * Fires when the editor is fully initialized (including the iframe)
43555          * @param {Roo.HtmlEditorCore} this
43556          */
43557         initialize: true,
43558         /**
43559          * @event activate
43560          * Fires when the editor is first receives the focus. Any insertion must wait
43561          * until after this event.
43562          * @param {Roo.HtmlEditorCore} this
43563          */
43564         activate: true,
43565          /**
43566          * @event beforesync
43567          * Fires before the textarea is updated with content from the editor iframe. Return false
43568          * to cancel the sync.
43569          * @param {Roo.HtmlEditorCore} this
43570          * @param {String} html
43571          */
43572         beforesync: true,
43573          /**
43574          * @event beforepush
43575          * Fires before the iframe editor is updated with content from the textarea. Return false
43576          * to cancel the push.
43577          * @param {Roo.HtmlEditorCore} this
43578          * @param {String} html
43579          */
43580         beforepush: true,
43581          /**
43582          * @event sync
43583          * Fires when the textarea is updated with content from the editor iframe.
43584          * @param {Roo.HtmlEditorCore} this
43585          * @param {String} html
43586          */
43587         sync: true,
43588          /**
43589          * @event push
43590          * Fires when the iframe editor is updated with content from the textarea.
43591          * @param {Roo.HtmlEditorCore} this
43592          * @param {String} html
43593          */
43594         push: true,
43595         
43596         /**
43597          * @event editorevent
43598          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
43599          * @param {Roo.HtmlEditorCore} this
43600          */
43601         editorevent: true
43602         
43603     });
43604     
43605     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
43606     
43607     // defaults : white / black...
43608     this.applyBlacklists();
43609     
43610     
43611     
43612 };
43613
43614
43615 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
43616
43617
43618      /**
43619      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
43620      */
43621     
43622     owner : false,
43623     
43624      /**
43625      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
43626      *                        Roo.resizable.
43627      */
43628     resizable : false,
43629      /**
43630      * @cfg {Number} height (in pixels)
43631      */   
43632     height: 300,
43633    /**
43634      * @cfg {Number} width (in pixels)
43635      */   
43636     width: 500,
43637     
43638     /**
43639      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
43640      * 
43641      */
43642     stylesheets: false,
43643     
43644     // id of frame..
43645     frameId: false,
43646     
43647     // private properties
43648     validationEvent : false,
43649     deferHeight: true,
43650     initialized : false,
43651     activated : false,
43652     sourceEditMode : false,
43653     onFocus : Roo.emptyFn,
43654     iframePad:3,
43655     hideMode:'offsets',
43656     
43657     clearUp: true,
43658     
43659     // blacklist + whitelisted elements..
43660     black: false,
43661     white: false,
43662      
43663     bodyCls : '',
43664
43665     /**
43666      * Protected method that will not generally be called directly. It
43667      * is called when the editor initializes the iframe with HTML contents. Override this method if you
43668      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
43669      */
43670     getDocMarkup : function(){
43671         // body styles..
43672         var st = '';
43673         
43674         // inherit styels from page...?? 
43675         if (this.stylesheets === false) {
43676             
43677             Roo.get(document.head).select('style').each(function(node) {
43678                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
43679             });
43680             
43681             Roo.get(document.head).select('link').each(function(node) { 
43682                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
43683             });
43684             
43685         } else if (!this.stylesheets.length) {
43686                 // simple..
43687                 st = '<style type="text/css">' +
43688                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
43689                    '</style>';
43690         } else {
43691             for (var i in this.stylesheets) { 
43692                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
43693             }
43694             
43695         }
43696         
43697         st +=  '<style type="text/css">' +
43698             'IMG { cursor: pointer } ' +
43699         '</style>';
43700
43701         var cls = 'roo-htmleditor-body';
43702         
43703         if(this.bodyCls.length){
43704             cls += ' ' + this.bodyCls;
43705         }
43706         
43707         return '<html><head>' + st  +
43708             //<style type="text/css">' +
43709             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
43710             //'</style>' +
43711             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
43712     },
43713
43714     // private
43715     onRender : function(ct, position)
43716     {
43717         var _t = this;
43718         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
43719         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
43720         
43721         
43722         this.el.dom.style.border = '0 none';
43723         this.el.dom.setAttribute('tabIndex', -1);
43724         this.el.addClass('x-hidden hide');
43725         
43726         
43727         
43728         if(Roo.isIE){ // fix IE 1px bogus margin
43729             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
43730         }
43731        
43732         
43733         this.frameId = Roo.id();
43734         
43735          
43736         
43737         var iframe = this.owner.wrap.createChild({
43738             tag: 'iframe',
43739             cls: 'form-control', // bootstrap..
43740             id: this.frameId,
43741             name: this.frameId,
43742             frameBorder : 'no',
43743             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
43744         }, this.el
43745         );
43746         
43747         
43748         this.iframe = iframe.dom;
43749
43750          this.assignDocWin();
43751         
43752         this.doc.designMode = 'on';
43753        
43754         this.doc.open();
43755         this.doc.write(this.getDocMarkup());
43756         this.doc.close();
43757
43758         
43759         var task = { // must defer to wait for browser to be ready
43760             run : function(){
43761                 //console.log("run task?" + this.doc.readyState);
43762                 this.assignDocWin();
43763                 if(this.doc.body || this.doc.readyState == 'complete'){
43764                     try {
43765                         this.doc.designMode="on";
43766                     } catch (e) {
43767                         return;
43768                     }
43769                     Roo.TaskMgr.stop(task);
43770                     this.initEditor.defer(10, this);
43771                 }
43772             },
43773             interval : 10,
43774             duration: 10000,
43775             scope: this
43776         };
43777         Roo.TaskMgr.start(task);
43778
43779     },
43780
43781     // private
43782     onResize : function(w, h)
43783     {
43784          Roo.log('resize: ' +w + ',' + h );
43785         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
43786         if(!this.iframe){
43787             return;
43788         }
43789         if(typeof w == 'number'){
43790             
43791             this.iframe.style.width = w + 'px';
43792         }
43793         if(typeof h == 'number'){
43794             
43795             this.iframe.style.height = h + 'px';
43796             if(this.doc){
43797                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
43798             }
43799         }
43800         
43801     },
43802
43803     /**
43804      * Toggles the editor between standard and source edit mode.
43805      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
43806      */
43807     toggleSourceEdit : function(sourceEditMode){
43808         
43809         this.sourceEditMode = sourceEditMode === true;
43810         
43811         if(this.sourceEditMode){
43812  
43813             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
43814             
43815         }else{
43816             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
43817             //this.iframe.className = '';
43818             this.deferFocus();
43819         }
43820         //this.setSize(this.owner.wrap.getSize());
43821         //this.fireEvent('editmodechange', this, this.sourceEditMode);
43822     },
43823
43824     
43825   
43826
43827     /**
43828      * Protected method that will not generally be called directly. If you need/want
43829      * custom HTML cleanup, this is the method you should override.
43830      * @param {String} html The HTML to be cleaned
43831      * return {String} The cleaned HTML
43832      */
43833     cleanHtml : function(html){
43834         html = String(html);
43835         if(html.length > 5){
43836             if(Roo.isSafari){ // strip safari nonsense
43837                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
43838             }
43839         }
43840         if(html == '&nbsp;'){
43841             html = '';
43842         }
43843         return html;
43844     },
43845
43846     /**
43847      * HTML Editor -> Textarea
43848      * Protected method that will not generally be called directly. Syncs the contents
43849      * of the editor iframe with the textarea.
43850      */
43851     syncValue : function(){
43852         if(this.initialized){
43853             var bd = (this.doc.body || this.doc.documentElement);
43854             //this.cleanUpPaste(); -- this is done else where and causes havoc..
43855             var html = bd.innerHTML;
43856             if(Roo.isSafari){
43857                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
43858                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
43859                 if(m && m[1]){
43860                     html = '<div style="'+m[0]+'">' + html + '</div>';
43861                 }
43862             }
43863             html = this.cleanHtml(html);
43864             // fix up the special chars.. normaly like back quotes in word...
43865             // however we do not want to do this with chinese..
43866             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
43867                 
43868                 var cc = match.charCodeAt();
43869
43870                 // Get the character value, handling surrogate pairs
43871                 if (match.length == 2) {
43872                     // It's a surrogate pair, calculate the Unicode code point
43873                     var high = match.charCodeAt(0) - 0xD800;
43874                     var low  = match.charCodeAt(1) - 0xDC00;
43875                     cc = (high * 0x400) + low + 0x10000;
43876                 }  else if (
43877                     (cc >= 0x4E00 && cc < 0xA000 ) ||
43878                     (cc >= 0x3400 && cc < 0x4E00 ) ||
43879                     (cc >= 0xf900 && cc < 0xfb00 )
43880                 ) {
43881                         return match;
43882                 }  
43883          
43884                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
43885                 return "&#" + cc + ";";
43886                 
43887                 
43888             });
43889             
43890             
43891              
43892             if(this.owner.fireEvent('beforesync', this, html) !== false){
43893                 this.el.dom.value = html;
43894                 this.owner.fireEvent('sync', this, html);
43895             }
43896         }
43897     },
43898
43899     /**
43900      * Protected method that will not generally be called directly. Pushes the value of the textarea
43901      * into the iframe editor.
43902      */
43903     pushValue : function(){
43904         if(this.initialized){
43905             var v = this.el.dom.value.trim();
43906             
43907 //            if(v.length < 1){
43908 //                v = '&#160;';
43909 //            }
43910             
43911             if(this.owner.fireEvent('beforepush', this, v) !== false){
43912                 var d = (this.doc.body || this.doc.documentElement);
43913                 d.innerHTML = v;
43914                 this.cleanUpPaste();
43915                 this.el.dom.value = d.innerHTML;
43916                 this.owner.fireEvent('push', this, v);
43917             }
43918         }
43919     },
43920
43921     // private
43922     deferFocus : function(){
43923         this.focus.defer(10, this);
43924     },
43925
43926     // doc'ed in Field
43927     focus : function(){
43928         if(this.win && !this.sourceEditMode){
43929             this.win.focus();
43930         }else{
43931             this.el.focus();
43932         }
43933     },
43934     
43935     assignDocWin: function()
43936     {
43937         var iframe = this.iframe;
43938         
43939          if(Roo.isIE){
43940             this.doc = iframe.contentWindow.document;
43941             this.win = iframe.contentWindow;
43942         } else {
43943 //            if (!Roo.get(this.frameId)) {
43944 //                return;
43945 //            }
43946 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
43947 //            this.win = Roo.get(this.frameId).dom.contentWindow;
43948             
43949             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
43950                 return;
43951             }
43952             
43953             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
43954             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
43955         }
43956     },
43957     
43958     // private
43959     initEditor : function(){
43960         //console.log("INIT EDITOR");
43961         this.assignDocWin();
43962         
43963         
43964         
43965         this.doc.designMode="on";
43966         this.doc.open();
43967         this.doc.write(this.getDocMarkup());
43968         this.doc.close();
43969         
43970         var dbody = (this.doc.body || this.doc.documentElement);
43971         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
43972         // this copies styles from the containing element into thsi one..
43973         // not sure why we need all of this..
43974         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
43975         
43976         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
43977         //ss['background-attachment'] = 'fixed'; // w3c
43978         dbody.bgProperties = 'fixed'; // ie
43979         //Roo.DomHelper.applyStyles(dbody, ss);
43980         Roo.EventManager.on(this.doc, {
43981             //'mousedown': this.onEditorEvent,
43982             'mouseup': this.onEditorEvent,
43983             'dblclick': this.onEditorEvent,
43984             'click': this.onEditorEvent,
43985             'keyup': this.onEditorEvent,
43986             buffer:100,
43987             scope: this
43988         });
43989         if(Roo.isGecko){
43990             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
43991         }
43992         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
43993             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
43994         }
43995         this.initialized = true;
43996
43997         this.owner.fireEvent('initialize', this);
43998         this.pushValue();
43999     },
44000
44001     // private
44002     onDestroy : function(){
44003         
44004         
44005         
44006         if(this.rendered){
44007             
44008             //for (var i =0; i < this.toolbars.length;i++) {
44009             //    // fixme - ask toolbars for heights?
44010             //    this.toolbars[i].onDestroy();
44011            // }
44012             
44013             //this.wrap.dom.innerHTML = '';
44014             //this.wrap.remove();
44015         }
44016     },
44017
44018     // private
44019     onFirstFocus : function(){
44020         
44021         this.assignDocWin();
44022         
44023         
44024         this.activated = true;
44025          
44026     
44027         if(Roo.isGecko){ // prevent silly gecko errors
44028             this.win.focus();
44029             var s = this.win.getSelection();
44030             if(!s.focusNode || s.focusNode.nodeType != 3){
44031                 var r = s.getRangeAt(0);
44032                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
44033                 r.collapse(true);
44034                 this.deferFocus();
44035             }
44036             try{
44037                 this.execCmd('useCSS', true);
44038                 this.execCmd('styleWithCSS', false);
44039             }catch(e){}
44040         }
44041         this.owner.fireEvent('activate', this);
44042     },
44043
44044     // private
44045     adjustFont: function(btn){
44046         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
44047         //if(Roo.isSafari){ // safari
44048         //    adjust *= 2;
44049        // }
44050         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
44051         if(Roo.isSafari){ // safari
44052             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
44053             v =  (v < 10) ? 10 : v;
44054             v =  (v > 48) ? 48 : v;
44055             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
44056             
44057         }
44058         
44059         
44060         v = Math.max(1, v+adjust);
44061         
44062         this.execCmd('FontSize', v  );
44063     },
44064
44065     onEditorEvent : function(e)
44066     {
44067         this.owner.fireEvent('editorevent', this, e);
44068       //  this.updateToolbar();
44069         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
44070     },
44071
44072     insertTag : function(tg)
44073     {
44074         // could be a bit smarter... -> wrap the current selected tRoo..
44075         if (tg.toLowerCase() == 'span' ||
44076             tg.toLowerCase() == 'code' ||
44077             tg.toLowerCase() == 'sup' ||
44078             tg.toLowerCase() == 'sub' 
44079             ) {
44080             
44081             range = this.createRange(this.getSelection());
44082             var wrappingNode = this.doc.createElement(tg.toLowerCase());
44083             wrappingNode.appendChild(range.extractContents());
44084             range.insertNode(wrappingNode);
44085
44086             return;
44087             
44088             
44089             
44090         }
44091         this.execCmd("formatblock",   tg);
44092         
44093     },
44094     
44095     insertText : function(txt)
44096     {
44097         
44098         
44099         var range = this.createRange();
44100         range.deleteContents();
44101                //alert(Sender.getAttribute('label'));
44102                
44103         range.insertNode(this.doc.createTextNode(txt));
44104     } ,
44105     
44106      
44107
44108     /**
44109      * Executes a Midas editor command on the editor document and performs necessary focus and
44110      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
44111      * @param {String} cmd The Midas command
44112      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
44113      */
44114     relayCmd : function(cmd, value){
44115         this.win.focus();
44116         this.execCmd(cmd, value);
44117         this.owner.fireEvent('editorevent', this);
44118         //this.updateToolbar();
44119         this.owner.deferFocus();
44120     },
44121
44122     /**
44123      * Executes a Midas editor command directly on the editor document.
44124      * For visual commands, you should use {@link #relayCmd} instead.
44125      * <b>This should only be called after the editor is initialized.</b>
44126      * @param {String} cmd The Midas command
44127      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
44128      */
44129     execCmd : function(cmd, value){
44130         this.doc.execCommand(cmd, false, value === undefined ? null : value);
44131         this.syncValue();
44132     },
44133  
44134  
44135    
44136     /**
44137      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
44138      * to insert tRoo.
44139      * @param {String} text | dom node.. 
44140      */
44141     insertAtCursor : function(text)
44142     {
44143         
44144         if(!this.activated){
44145             return;
44146         }
44147         /*
44148         if(Roo.isIE){
44149             this.win.focus();
44150             var r = this.doc.selection.createRange();
44151             if(r){
44152                 r.collapse(true);
44153                 r.pasteHTML(text);
44154                 this.syncValue();
44155                 this.deferFocus();
44156             
44157             }
44158             return;
44159         }
44160         */
44161         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
44162             this.win.focus();
44163             
44164             
44165             // from jquery ui (MIT licenced)
44166             var range, node;
44167             var win = this.win;
44168             
44169             if (win.getSelection && win.getSelection().getRangeAt) {
44170                 range = win.getSelection().getRangeAt(0);
44171                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
44172                 range.insertNode(node);
44173             } else if (win.document.selection && win.document.selection.createRange) {
44174                 // no firefox support
44175                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
44176                 win.document.selection.createRange().pasteHTML(txt);
44177             } else {
44178                 // no firefox support
44179                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
44180                 this.execCmd('InsertHTML', txt);
44181             } 
44182             
44183             this.syncValue();
44184             
44185             this.deferFocus();
44186         }
44187     },
44188  // private
44189     mozKeyPress : function(e){
44190         if(e.ctrlKey){
44191             var c = e.getCharCode(), cmd;
44192           
44193             if(c > 0){
44194                 c = String.fromCharCode(c).toLowerCase();
44195                 switch(c){
44196                     case 'b':
44197                         cmd = 'bold';
44198                         break;
44199                     case 'i':
44200                         cmd = 'italic';
44201                         break;
44202                     
44203                     case 'u':
44204                         cmd = 'underline';
44205                         break;
44206                     
44207                     case 'v':
44208                         this.cleanUpPaste.defer(100, this);
44209                         return;
44210                         
44211                 }
44212                 if(cmd){
44213                     this.win.focus();
44214                     this.execCmd(cmd);
44215                     this.deferFocus();
44216                     e.preventDefault();
44217                 }
44218                 
44219             }
44220         }
44221     },
44222
44223     // private
44224     fixKeys : function(){ // load time branching for fastest keydown performance
44225         if(Roo.isIE){
44226             return function(e){
44227                 var k = e.getKey(), r;
44228                 if(k == e.TAB){
44229                     e.stopEvent();
44230                     r = this.doc.selection.createRange();
44231                     if(r){
44232                         r.collapse(true);
44233                         r.pasteHTML('&#160;&#160;&#160;&#160;');
44234                         this.deferFocus();
44235                     }
44236                     return;
44237                 }
44238                 
44239                 if(k == e.ENTER){
44240                     r = this.doc.selection.createRange();
44241                     if(r){
44242                         var target = r.parentElement();
44243                         if(!target || target.tagName.toLowerCase() != 'li'){
44244                             e.stopEvent();
44245                             r.pasteHTML('<br />');
44246                             r.collapse(false);
44247                             r.select();
44248                         }
44249                     }
44250                 }
44251                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
44252                     this.cleanUpPaste.defer(100, this);
44253                     return;
44254                 }
44255                 
44256                 
44257             };
44258         }else if(Roo.isOpera){
44259             return function(e){
44260                 var k = e.getKey();
44261                 if(k == e.TAB){
44262                     e.stopEvent();
44263                     this.win.focus();
44264                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
44265                     this.deferFocus();
44266                 }
44267                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
44268                     this.cleanUpPaste.defer(100, this);
44269                     return;
44270                 }
44271                 
44272             };
44273         }else if(Roo.isSafari){
44274             return function(e){
44275                 var k = e.getKey();
44276                 
44277                 if(k == e.TAB){
44278                     e.stopEvent();
44279                     this.execCmd('InsertText','\t');
44280                     this.deferFocus();
44281                     return;
44282                 }
44283                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
44284                     this.cleanUpPaste.defer(100, this);
44285                     return;
44286                 }
44287                 
44288              };
44289         }
44290     }(),
44291     
44292     getAllAncestors: function()
44293     {
44294         var p = this.getSelectedNode();
44295         var a = [];
44296         if (!p) {
44297             a.push(p); // push blank onto stack..
44298             p = this.getParentElement();
44299         }
44300         
44301         
44302         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
44303             a.push(p);
44304             p = p.parentNode;
44305         }
44306         a.push(this.doc.body);
44307         return a;
44308     },
44309     lastSel : false,
44310     lastSelNode : false,
44311     
44312     
44313     getSelection : function() 
44314     {
44315         this.assignDocWin();
44316         return Roo.isIE ? this.doc.selection : this.win.getSelection();
44317     },
44318     
44319     getSelectedNode: function() 
44320     {
44321         // this may only work on Gecko!!!
44322         
44323         // should we cache this!!!!
44324         
44325         
44326         
44327          
44328         var range = this.createRange(this.getSelection()).cloneRange();
44329         
44330         if (Roo.isIE) {
44331             var parent = range.parentElement();
44332             while (true) {
44333                 var testRange = range.duplicate();
44334                 testRange.moveToElementText(parent);
44335                 if (testRange.inRange(range)) {
44336                     break;
44337                 }
44338                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
44339                     break;
44340                 }
44341                 parent = parent.parentElement;
44342             }
44343             return parent;
44344         }
44345         
44346         // is ancestor a text element.
44347         var ac =  range.commonAncestorContainer;
44348         if (ac.nodeType == 3) {
44349             ac = ac.parentNode;
44350         }
44351         
44352         var ar = ac.childNodes;
44353          
44354         var nodes = [];
44355         var other_nodes = [];
44356         var has_other_nodes = false;
44357         for (var i=0;i<ar.length;i++) {
44358             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
44359                 continue;
44360             }
44361             // fullly contained node.
44362             
44363             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
44364                 nodes.push(ar[i]);
44365                 continue;
44366             }
44367             
44368             // probably selected..
44369             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
44370                 other_nodes.push(ar[i]);
44371                 continue;
44372             }
44373             // outer..
44374             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
44375                 continue;
44376             }
44377             
44378             
44379             has_other_nodes = true;
44380         }
44381         if (!nodes.length && other_nodes.length) {
44382             nodes= other_nodes;
44383         }
44384         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
44385             return false;
44386         }
44387         
44388         return nodes[0];
44389     },
44390     createRange: function(sel)
44391     {
44392         // this has strange effects when using with 
44393         // top toolbar - not sure if it's a great idea.
44394         //this.editor.contentWindow.focus();
44395         if (typeof sel != "undefined") {
44396             try {
44397                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
44398             } catch(e) {
44399                 return this.doc.createRange();
44400             }
44401         } else {
44402             return this.doc.createRange();
44403         }
44404     },
44405     getParentElement: function()
44406     {
44407         
44408         this.assignDocWin();
44409         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
44410         
44411         var range = this.createRange(sel);
44412          
44413         try {
44414             var p = range.commonAncestorContainer;
44415             while (p.nodeType == 3) { // text node
44416                 p = p.parentNode;
44417             }
44418             return p;
44419         } catch (e) {
44420             return null;
44421         }
44422     
44423     },
44424     /***
44425      *
44426      * Range intersection.. the hard stuff...
44427      *  '-1' = before
44428      *  '0' = hits..
44429      *  '1' = after.
44430      *         [ -- selected range --- ]
44431      *   [fail]                        [fail]
44432      *
44433      *    basically..
44434      *      if end is before start or  hits it. fail.
44435      *      if start is after end or hits it fail.
44436      *
44437      *   if either hits (but other is outside. - then it's not 
44438      *   
44439      *    
44440      **/
44441     
44442     
44443     // @see http://www.thismuchiknow.co.uk/?p=64.
44444     rangeIntersectsNode : function(range, node)
44445     {
44446         var nodeRange = node.ownerDocument.createRange();
44447         try {
44448             nodeRange.selectNode(node);
44449         } catch (e) {
44450             nodeRange.selectNodeContents(node);
44451         }
44452     
44453         var rangeStartRange = range.cloneRange();
44454         rangeStartRange.collapse(true);
44455     
44456         var rangeEndRange = range.cloneRange();
44457         rangeEndRange.collapse(false);
44458     
44459         var nodeStartRange = nodeRange.cloneRange();
44460         nodeStartRange.collapse(true);
44461     
44462         var nodeEndRange = nodeRange.cloneRange();
44463         nodeEndRange.collapse(false);
44464     
44465         return rangeStartRange.compareBoundaryPoints(
44466                  Range.START_TO_START, nodeEndRange) == -1 &&
44467                rangeEndRange.compareBoundaryPoints(
44468                  Range.START_TO_START, nodeStartRange) == 1;
44469         
44470          
44471     },
44472     rangeCompareNode : function(range, node)
44473     {
44474         var nodeRange = node.ownerDocument.createRange();
44475         try {
44476             nodeRange.selectNode(node);
44477         } catch (e) {
44478             nodeRange.selectNodeContents(node);
44479         }
44480         
44481         
44482         range.collapse(true);
44483     
44484         nodeRange.collapse(true);
44485      
44486         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
44487         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
44488          
44489         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
44490         
44491         var nodeIsBefore   =  ss == 1;
44492         var nodeIsAfter    = ee == -1;
44493         
44494         if (nodeIsBefore && nodeIsAfter) {
44495             return 0; // outer
44496         }
44497         if (!nodeIsBefore && nodeIsAfter) {
44498             return 1; //right trailed.
44499         }
44500         
44501         if (nodeIsBefore && !nodeIsAfter) {
44502             return 2;  // left trailed.
44503         }
44504         // fully contined.
44505         return 3;
44506     },
44507
44508     // private? - in a new class?
44509     cleanUpPaste :  function()
44510     {
44511         // cleans up the whole document..
44512         Roo.log('cleanuppaste');
44513         
44514         this.cleanUpChildren(this.doc.body);
44515         var clean = this.cleanWordChars(this.doc.body.innerHTML);
44516         if (clean != this.doc.body.innerHTML) {
44517             this.doc.body.innerHTML = clean;
44518         }
44519         
44520     },
44521     
44522     cleanWordChars : function(input) {// change the chars to hex code
44523         var he = Roo.HtmlEditorCore;
44524         
44525         var output = input;
44526         Roo.each(he.swapCodes, function(sw) { 
44527             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
44528             
44529             output = output.replace(swapper, sw[1]);
44530         });
44531         
44532         return output;
44533     },
44534     
44535     
44536     cleanUpChildren : function (n)
44537     {
44538         if (!n.childNodes.length) {
44539             return;
44540         }
44541         for (var i = n.childNodes.length-1; i > -1 ; i--) {
44542            this.cleanUpChild(n.childNodes[i]);
44543         }
44544     },
44545     
44546     
44547         
44548     
44549     cleanUpChild : function (node)
44550     {
44551         var ed = this;
44552         //console.log(node);
44553         if (node.nodeName == "#text") {
44554             // clean up silly Windows -- stuff?
44555             return; 
44556         }
44557         if (node.nodeName == "#comment") {
44558             node.parentNode.removeChild(node);
44559             // clean up silly Windows -- stuff?
44560             return; 
44561         }
44562         var lcname = node.tagName.toLowerCase();
44563         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
44564         // whitelist of tags..
44565         
44566         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
44567             // remove node.
44568             node.parentNode.removeChild(node);
44569             return;
44570             
44571         }
44572         
44573         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
44574         
44575         // spans with no attributes - just remove them..
44576         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
44577             remove_keep_children = true;
44578         }
44579         
44580         // remove <a name=....> as rendering on yahoo mailer is borked with this.
44581         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
44582         
44583         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
44584         //    remove_keep_children = true;
44585         //}
44586         
44587         if (remove_keep_children) {
44588             this.cleanUpChildren(node);
44589             // inserts everything just before this node...
44590             while (node.childNodes.length) {
44591                 var cn = node.childNodes[0];
44592                 node.removeChild(cn);
44593                 node.parentNode.insertBefore(cn, node);
44594             }
44595             node.parentNode.removeChild(node);
44596             return;
44597         }
44598         
44599         if (!node.attributes || !node.attributes.length) {
44600             
44601           
44602             
44603             
44604             this.cleanUpChildren(node);
44605             return;
44606         }
44607         
44608         function cleanAttr(n,v)
44609         {
44610             
44611             if (v.match(/^\./) || v.match(/^\//)) {
44612                 return;
44613             }
44614             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
44615                 return;
44616             }
44617             if (v.match(/^#/)) {
44618                 return;
44619             }
44620             if (v.match(/^\{/)) { // allow template editing.
44621                 return;
44622             }
44623 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
44624             node.removeAttribute(n);
44625             
44626         }
44627         
44628         var cwhite = this.cwhite;
44629         var cblack = this.cblack;
44630             
44631         function cleanStyle(n,v)
44632         {
44633             if (v.match(/expression/)) { //XSS?? should we even bother..
44634                 node.removeAttribute(n);
44635                 return;
44636             }
44637             
44638             var parts = v.split(/;/);
44639             var clean = [];
44640             
44641             Roo.each(parts, function(p) {
44642                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
44643                 if (!p.length) {
44644                     return true;
44645                 }
44646                 var l = p.split(':').shift().replace(/\s+/g,'');
44647                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
44648                 
44649                 if ( cwhite.length && cblack.indexOf(l) > -1) {
44650 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
44651                     //node.removeAttribute(n);
44652                     return true;
44653                 }
44654                 //Roo.log()
44655                 // only allow 'c whitelisted system attributes'
44656                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
44657 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
44658                     //node.removeAttribute(n);
44659                     return true;
44660                 }
44661                 
44662                 
44663                  
44664                 
44665                 clean.push(p);
44666                 return true;
44667             });
44668             if (clean.length) { 
44669                 node.setAttribute(n, clean.join(';'));
44670             } else {
44671                 node.removeAttribute(n);
44672             }
44673             
44674         }
44675         
44676         
44677         for (var i = node.attributes.length-1; i > -1 ; i--) {
44678             var a = node.attributes[i];
44679             //console.log(a);
44680             
44681             if (a.name.toLowerCase().substr(0,2)=='on')  {
44682                 node.removeAttribute(a.name);
44683                 continue;
44684             }
44685             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
44686                 node.removeAttribute(a.name);
44687                 continue;
44688             }
44689             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
44690                 cleanAttr(a.name,a.value); // fixme..
44691                 continue;
44692             }
44693             if (a.name == 'style') {
44694                 cleanStyle(a.name,a.value);
44695                 continue;
44696             }
44697             /// clean up MS crap..
44698             // tecnically this should be a list of valid class'es..
44699             
44700             
44701             if (a.name == 'class') {
44702                 if (a.value.match(/^Mso/)) {
44703                     node.removeAttribute('class');
44704                 }
44705                 
44706                 if (a.value.match(/^body$/)) {
44707                     node.removeAttribute('class');
44708                 }
44709                 continue;
44710             }
44711             
44712             // style cleanup!?
44713             // class cleanup?
44714             
44715         }
44716         
44717         
44718         this.cleanUpChildren(node);
44719         
44720         
44721     },
44722     
44723     /**
44724      * Clean up MS wordisms...
44725      */
44726     cleanWord : function(node)
44727     {
44728         if (!node) {
44729             this.cleanWord(this.doc.body);
44730             return;
44731         }
44732         
44733         if(
44734                 node.nodeName == 'SPAN' &&
44735                 !node.hasAttributes() &&
44736                 node.childNodes.length == 1 &&
44737                 node.firstChild.nodeName == "#text"  
44738         ) {
44739             var textNode = node.firstChild;
44740             node.removeChild(textNode);
44741             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
44742                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
44743             }
44744             node.parentNode.insertBefore(textNode, node);
44745             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
44746                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
44747             }
44748             node.parentNode.removeChild(node);
44749         }
44750         
44751         if (node.nodeName == "#text") {
44752             // clean up silly Windows -- stuff?
44753             return; 
44754         }
44755         if (node.nodeName == "#comment") {
44756             node.parentNode.removeChild(node);
44757             // clean up silly Windows -- stuff?
44758             return; 
44759         }
44760         
44761         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
44762             node.parentNode.removeChild(node);
44763             return;
44764         }
44765         //Roo.log(node.tagName);
44766         // remove - but keep children..
44767         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
44768             //Roo.log('-- removed');
44769             while (node.childNodes.length) {
44770                 var cn = node.childNodes[0];
44771                 node.removeChild(cn);
44772                 node.parentNode.insertBefore(cn, node);
44773                 // move node to parent - and clean it..
44774                 this.cleanWord(cn);
44775             }
44776             node.parentNode.removeChild(node);
44777             /// no need to iterate chidlren = it's got none..
44778             //this.iterateChildren(node, this.cleanWord);
44779             return;
44780         }
44781         // clean styles
44782         if (node.className.length) {
44783             
44784             var cn = node.className.split(/\W+/);
44785             var cna = [];
44786             Roo.each(cn, function(cls) {
44787                 if (cls.match(/Mso[a-zA-Z]+/)) {
44788                     return;
44789                 }
44790                 cna.push(cls);
44791             });
44792             node.className = cna.length ? cna.join(' ') : '';
44793             if (!cna.length) {
44794                 node.removeAttribute("class");
44795             }
44796         }
44797         
44798         if (node.hasAttribute("lang")) {
44799             node.removeAttribute("lang");
44800         }
44801         
44802         if (node.hasAttribute("style")) {
44803             
44804             var styles = node.getAttribute("style").split(";");
44805             var nstyle = [];
44806             Roo.each(styles, function(s) {
44807                 if (!s.match(/:/)) {
44808                     return;
44809                 }
44810                 var kv = s.split(":");
44811                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
44812                     return;
44813                 }
44814                 // what ever is left... we allow.
44815                 nstyle.push(s);
44816             });
44817             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
44818             if (!nstyle.length) {
44819                 node.removeAttribute('style');
44820             }
44821         }
44822         this.iterateChildren(node, this.cleanWord);
44823         
44824         
44825         
44826     },
44827     /**
44828      * iterateChildren of a Node, calling fn each time, using this as the scole..
44829      * @param {DomNode} node node to iterate children of.
44830      * @param {Function} fn method of this class to call on each item.
44831      */
44832     iterateChildren : function(node, fn)
44833     {
44834         if (!node.childNodes.length) {
44835                 return;
44836         }
44837         for (var i = node.childNodes.length-1; i > -1 ; i--) {
44838            fn.call(this, node.childNodes[i])
44839         }
44840     },
44841     
44842     
44843     /**
44844      * cleanTableWidths.
44845      *
44846      * Quite often pasting from word etc.. results in tables with column and widths.
44847      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
44848      *
44849      */
44850     cleanTableWidths : function(node)
44851     {
44852          
44853          
44854         if (!node) {
44855             this.cleanTableWidths(this.doc.body);
44856             return;
44857         }
44858         
44859         // ignore list...
44860         if (node.nodeName == "#text" || node.nodeName == "#comment") {
44861             return; 
44862         }
44863         Roo.log(node.tagName);
44864         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
44865             this.iterateChildren(node, this.cleanTableWidths);
44866             return;
44867         }
44868         if (node.hasAttribute('width')) {
44869             node.removeAttribute('width');
44870         }
44871         
44872          
44873         if (node.hasAttribute("style")) {
44874             // pretty basic...
44875             
44876             var styles = node.getAttribute("style").split(";");
44877             var nstyle = [];
44878             Roo.each(styles, function(s) {
44879                 if (!s.match(/:/)) {
44880                     return;
44881                 }
44882                 var kv = s.split(":");
44883                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
44884                     return;
44885                 }
44886                 // what ever is left... we allow.
44887                 nstyle.push(s);
44888             });
44889             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
44890             if (!nstyle.length) {
44891                 node.removeAttribute('style');
44892             }
44893         }
44894         
44895         this.iterateChildren(node, this.cleanTableWidths);
44896         
44897         
44898     },
44899     
44900     
44901     
44902     
44903     domToHTML : function(currentElement, depth, nopadtext) {
44904         
44905         depth = depth || 0;
44906         nopadtext = nopadtext || false;
44907     
44908         if (!currentElement) {
44909             return this.domToHTML(this.doc.body);
44910         }
44911         
44912         //Roo.log(currentElement);
44913         var j;
44914         var allText = false;
44915         var nodeName = currentElement.nodeName;
44916         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
44917         
44918         if  (nodeName == '#text') {
44919             
44920             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
44921         }
44922         
44923         
44924         var ret = '';
44925         if (nodeName != 'BODY') {
44926              
44927             var i = 0;
44928             // Prints the node tagName, such as <A>, <IMG>, etc
44929             if (tagName) {
44930                 var attr = [];
44931                 for(i = 0; i < currentElement.attributes.length;i++) {
44932                     // quoting?
44933                     var aname = currentElement.attributes.item(i).name;
44934                     if (!currentElement.attributes.item(i).value.length) {
44935                         continue;
44936                     }
44937                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
44938                 }
44939                 
44940                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
44941             } 
44942             else {
44943                 
44944                 // eack
44945             }
44946         } else {
44947             tagName = false;
44948         }
44949         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
44950             return ret;
44951         }
44952         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
44953             nopadtext = true;
44954         }
44955         
44956         
44957         // Traverse the tree
44958         i = 0;
44959         var currentElementChild = currentElement.childNodes.item(i);
44960         var allText = true;
44961         var innerHTML  = '';
44962         lastnode = '';
44963         while (currentElementChild) {
44964             // Formatting code (indent the tree so it looks nice on the screen)
44965             var nopad = nopadtext;
44966             if (lastnode == 'SPAN') {
44967                 nopad  = true;
44968             }
44969             // text
44970             if  (currentElementChild.nodeName == '#text') {
44971                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
44972                 toadd = nopadtext ? toadd : toadd.trim();
44973                 if (!nopad && toadd.length > 80) {
44974                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
44975                 }
44976                 innerHTML  += toadd;
44977                 
44978                 i++;
44979                 currentElementChild = currentElement.childNodes.item(i);
44980                 lastNode = '';
44981                 continue;
44982             }
44983             allText = false;
44984             
44985             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
44986                 
44987             // Recursively traverse the tree structure of the child node
44988             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
44989             lastnode = currentElementChild.nodeName;
44990             i++;
44991             currentElementChild=currentElement.childNodes.item(i);
44992         }
44993         
44994         ret += innerHTML;
44995         
44996         if (!allText) {
44997                 // The remaining code is mostly for formatting the tree
44998             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
44999         }
45000         
45001         
45002         if (tagName) {
45003             ret+= "</"+tagName+">";
45004         }
45005         return ret;
45006         
45007     },
45008         
45009     applyBlacklists : function()
45010     {
45011         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
45012         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
45013         
45014         this.white = [];
45015         this.black = [];
45016         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
45017             if (b.indexOf(tag) > -1) {
45018                 return;
45019             }
45020             this.white.push(tag);
45021             
45022         }, this);
45023         
45024         Roo.each(w, function(tag) {
45025             if (b.indexOf(tag) > -1) {
45026                 return;
45027             }
45028             if (this.white.indexOf(tag) > -1) {
45029                 return;
45030             }
45031             this.white.push(tag);
45032             
45033         }, this);
45034         
45035         
45036         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
45037             if (w.indexOf(tag) > -1) {
45038                 return;
45039             }
45040             this.black.push(tag);
45041             
45042         }, this);
45043         
45044         Roo.each(b, function(tag) {
45045             if (w.indexOf(tag) > -1) {
45046                 return;
45047             }
45048             if (this.black.indexOf(tag) > -1) {
45049                 return;
45050             }
45051             this.black.push(tag);
45052             
45053         }, this);
45054         
45055         
45056         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
45057         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
45058         
45059         this.cwhite = [];
45060         this.cblack = [];
45061         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
45062             if (b.indexOf(tag) > -1) {
45063                 return;
45064             }
45065             this.cwhite.push(tag);
45066             
45067         }, this);
45068         
45069         Roo.each(w, function(tag) {
45070             if (b.indexOf(tag) > -1) {
45071                 return;
45072             }
45073             if (this.cwhite.indexOf(tag) > -1) {
45074                 return;
45075             }
45076             this.cwhite.push(tag);
45077             
45078         }, this);
45079         
45080         
45081         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
45082             if (w.indexOf(tag) > -1) {
45083                 return;
45084             }
45085             this.cblack.push(tag);
45086             
45087         }, this);
45088         
45089         Roo.each(b, function(tag) {
45090             if (w.indexOf(tag) > -1) {
45091                 return;
45092             }
45093             if (this.cblack.indexOf(tag) > -1) {
45094                 return;
45095             }
45096             this.cblack.push(tag);
45097             
45098         }, this);
45099     },
45100     
45101     setStylesheets : function(stylesheets)
45102     {
45103         if(typeof(stylesheets) == 'string'){
45104             Roo.get(this.iframe.contentDocument.head).createChild({
45105                 tag : 'link',
45106                 rel : 'stylesheet',
45107                 type : 'text/css',
45108                 href : stylesheets
45109             });
45110             
45111             return;
45112         }
45113         var _this = this;
45114      
45115         Roo.each(stylesheets, function(s) {
45116             if(!s.length){
45117                 return;
45118             }
45119             
45120             Roo.get(_this.iframe.contentDocument.head).createChild({
45121                 tag : 'link',
45122                 rel : 'stylesheet',
45123                 type : 'text/css',
45124                 href : s
45125             });
45126         });
45127
45128         
45129     },
45130     
45131     removeStylesheets : function()
45132     {
45133         var _this = this;
45134         
45135         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
45136             s.remove();
45137         });
45138     },
45139     
45140     setStyle : function(style)
45141     {
45142         Roo.get(this.iframe.contentDocument.head).createChild({
45143             tag : 'style',
45144             type : 'text/css',
45145             html : style
45146         });
45147
45148         return;
45149     }
45150     
45151     // hide stuff that is not compatible
45152     /**
45153      * @event blur
45154      * @hide
45155      */
45156     /**
45157      * @event change
45158      * @hide
45159      */
45160     /**
45161      * @event focus
45162      * @hide
45163      */
45164     /**
45165      * @event specialkey
45166      * @hide
45167      */
45168     /**
45169      * @cfg {String} fieldClass @hide
45170      */
45171     /**
45172      * @cfg {String} focusClass @hide
45173      */
45174     /**
45175      * @cfg {String} autoCreate @hide
45176      */
45177     /**
45178      * @cfg {String} inputType @hide
45179      */
45180     /**
45181      * @cfg {String} invalidClass @hide
45182      */
45183     /**
45184      * @cfg {String} invalidText @hide
45185      */
45186     /**
45187      * @cfg {String} msgFx @hide
45188      */
45189     /**
45190      * @cfg {String} validateOnBlur @hide
45191      */
45192 });
45193
45194 Roo.HtmlEditorCore.white = [
45195         'area', 'br', 'img', 'input', 'hr', 'wbr',
45196         
45197        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
45198        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
45199        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
45200        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
45201        'table',   'ul',         'xmp', 
45202        
45203        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
45204       'thead',   'tr', 
45205      
45206       'dir', 'menu', 'ol', 'ul', 'dl',
45207        
45208       'embed',  'object'
45209 ];
45210
45211
45212 Roo.HtmlEditorCore.black = [
45213     //    'embed',  'object', // enable - backend responsiblity to clean thiese
45214         'applet', // 
45215         'base',   'basefont', 'bgsound', 'blink',  'body', 
45216         'frame',  'frameset', 'head',    'html',   'ilayer', 
45217         'iframe', 'layer',  'link',     'meta',    'object',   
45218         'script', 'style' ,'title',  'xml' // clean later..
45219 ];
45220 Roo.HtmlEditorCore.clean = [
45221     'script', 'style', 'title', 'xml'
45222 ];
45223 Roo.HtmlEditorCore.remove = [
45224     'font'
45225 ];
45226 // attributes..
45227
45228 Roo.HtmlEditorCore.ablack = [
45229     'on'
45230 ];
45231     
45232 Roo.HtmlEditorCore.aclean = [ 
45233     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
45234 ];
45235
45236 // protocols..
45237 Roo.HtmlEditorCore.pwhite= [
45238         'http',  'https',  'mailto'
45239 ];
45240
45241 // white listed style attributes.
45242 Roo.HtmlEditorCore.cwhite= [
45243       //  'text-align', /// default is to allow most things..
45244       
45245          
45246 //        'font-size'//??
45247 ];
45248
45249 // black listed style attributes.
45250 Roo.HtmlEditorCore.cblack= [
45251       //  'font-size' -- this can be set by the project 
45252 ];
45253
45254
45255 Roo.HtmlEditorCore.swapCodes   =[ 
45256     [    8211, "&#8211;" ], 
45257     [    8212, "&#8212;" ], 
45258     [    8216,  "'" ],  
45259     [    8217, "'" ],  
45260     [    8220, '"' ],  
45261     [    8221, '"' ],  
45262     [    8226, "*" ],  
45263     [    8230, "..." ]
45264 ]; 
45265
45266     //<script type="text/javascript">
45267
45268 /*
45269  * Ext JS Library 1.1.1
45270  * Copyright(c) 2006-2007, Ext JS, LLC.
45271  * Licence LGPL
45272  * 
45273  */
45274  
45275  
45276 Roo.form.HtmlEditor = function(config){
45277     
45278     
45279     
45280     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
45281     
45282     if (!this.toolbars) {
45283         this.toolbars = [];
45284     }
45285     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
45286     
45287     
45288 };
45289
45290 /**
45291  * @class Roo.form.HtmlEditor
45292  * @extends Roo.form.Field
45293  * Provides a lightweight HTML Editor component.
45294  *
45295  * This has been tested on Fireforx / Chrome.. IE may not be so great..
45296  * 
45297  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
45298  * supported by this editor.</b><br/><br/>
45299  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
45300  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
45301  */
45302 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
45303     /**
45304      * @cfg {Boolean} clearUp
45305      */
45306     clearUp : true,
45307       /**
45308      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
45309      */
45310     toolbars : false,
45311    
45312      /**
45313      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
45314      *                        Roo.resizable.
45315      */
45316     resizable : false,
45317      /**
45318      * @cfg {Number} height (in pixels)
45319      */   
45320     height: 300,
45321    /**
45322      * @cfg {Number} width (in pixels)
45323      */   
45324     width: 500,
45325     
45326     /**
45327      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
45328      * 
45329      */
45330     stylesheets: false,
45331     
45332     
45333      /**
45334      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
45335      * 
45336      */
45337     cblack: false,
45338     /**
45339      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
45340      * 
45341      */
45342     cwhite: false,
45343     
45344      /**
45345      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
45346      * 
45347      */
45348     black: false,
45349     /**
45350      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
45351      * 
45352      */
45353     white: false,
45354     
45355     // id of frame..
45356     frameId: false,
45357     
45358     // private properties
45359     validationEvent : false,
45360     deferHeight: true,
45361     initialized : false,
45362     activated : false,
45363     
45364     onFocus : Roo.emptyFn,
45365     iframePad:3,
45366     hideMode:'offsets',
45367     
45368     actionMode : 'container', // defaults to hiding it...
45369     
45370     defaultAutoCreate : { // modified by initCompnoent..
45371         tag: "textarea",
45372         style:"width:500px;height:300px;",
45373         autocomplete: "new-password"
45374     },
45375
45376     // private
45377     initComponent : function(){
45378         this.addEvents({
45379             /**
45380              * @event initialize
45381              * Fires when the editor is fully initialized (including the iframe)
45382              * @param {HtmlEditor} this
45383              */
45384             initialize: true,
45385             /**
45386              * @event activate
45387              * Fires when the editor is first receives the focus. Any insertion must wait
45388              * until after this event.
45389              * @param {HtmlEditor} this
45390              */
45391             activate: true,
45392              /**
45393              * @event beforesync
45394              * Fires before the textarea is updated with content from the editor iframe. Return false
45395              * to cancel the sync.
45396              * @param {HtmlEditor} this
45397              * @param {String} html
45398              */
45399             beforesync: true,
45400              /**
45401              * @event beforepush
45402              * Fires before the iframe editor is updated with content from the textarea. Return false
45403              * to cancel the push.
45404              * @param {HtmlEditor} this
45405              * @param {String} html
45406              */
45407             beforepush: true,
45408              /**
45409              * @event sync
45410              * Fires when the textarea is updated with content from the editor iframe.
45411              * @param {HtmlEditor} this
45412              * @param {String} html
45413              */
45414             sync: true,
45415              /**
45416              * @event push
45417              * Fires when the iframe editor is updated with content from the textarea.
45418              * @param {HtmlEditor} this
45419              * @param {String} html
45420              */
45421             push: true,
45422              /**
45423              * @event editmodechange
45424              * Fires when the editor switches edit modes
45425              * @param {HtmlEditor} this
45426              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
45427              */
45428             editmodechange: true,
45429             /**
45430              * @event editorevent
45431              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
45432              * @param {HtmlEditor} this
45433              */
45434             editorevent: true,
45435             /**
45436              * @event firstfocus
45437              * Fires when on first focus - needed by toolbars..
45438              * @param {HtmlEditor} this
45439              */
45440             firstfocus: true,
45441             /**
45442              * @event autosave
45443              * Auto save the htmlEditor value as a file into Events
45444              * @param {HtmlEditor} this
45445              */
45446             autosave: true,
45447             /**
45448              * @event savedpreview
45449              * preview the saved version of htmlEditor
45450              * @param {HtmlEditor} this
45451              */
45452             savedpreview: true,
45453             
45454             /**
45455             * @event stylesheetsclick
45456             * Fires when press the Sytlesheets button
45457             * @param {Roo.HtmlEditorCore} this
45458             */
45459             stylesheetsclick: true
45460         });
45461         this.defaultAutoCreate =  {
45462             tag: "textarea",
45463             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
45464             autocomplete: "new-password"
45465         };
45466     },
45467
45468     /**
45469      * Protected method that will not generally be called directly. It
45470      * is called when the editor creates its toolbar. Override this method if you need to
45471      * add custom toolbar buttons.
45472      * @param {HtmlEditor} editor
45473      */
45474     createToolbar : function(editor){
45475         Roo.log("create toolbars");
45476         if (!editor.toolbars || !editor.toolbars.length) {
45477             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
45478         }
45479         
45480         for (var i =0 ; i < editor.toolbars.length;i++) {
45481             editor.toolbars[i] = Roo.factory(
45482                     typeof(editor.toolbars[i]) == 'string' ?
45483                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
45484                 Roo.form.HtmlEditor);
45485             editor.toolbars[i].init(editor);
45486         }
45487          
45488         
45489     },
45490
45491      
45492     // private
45493     onRender : function(ct, position)
45494     {
45495         var _t = this;
45496         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
45497         
45498         this.wrap = this.el.wrap({
45499             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
45500         });
45501         
45502         this.editorcore.onRender(ct, position);
45503          
45504         if (this.resizable) {
45505             this.resizeEl = new Roo.Resizable(this.wrap, {
45506                 pinned : true,
45507                 wrap: true,
45508                 dynamic : true,
45509                 minHeight : this.height,
45510                 height: this.height,
45511                 handles : this.resizable,
45512                 width: this.width,
45513                 listeners : {
45514                     resize : function(r, w, h) {
45515                         _t.onResize(w,h); // -something
45516                     }
45517                 }
45518             });
45519             
45520         }
45521         this.createToolbar(this);
45522        
45523         
45524         if(!this.width){
45525             this.setSize(this.wrap.getSize());
45526         }
45527         if (this.resizeEl) {
45528             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
45529             // should trigger onReize..
45530         }
45531         
45532         this.keyNav = new Roo.KeyNav(this.el, {
45533             
45534             "tab" : function(e){
45535                 e.preventDefault();
45536                 
45537                 var value = this.getValue();
45538                 
45539                 var start = this.el.dom.selectionStart;
45540                 var end = this.el.dom.selectionEnd;
45541                 
45542                 if(!e.shiftKey){
45543                     
45544                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
45545                     this.el.dom.setSelectionRange(end + 1, end + 1);
45546                     return;
45547                 }
45548                 
45549                 var f = value.substring(0, start).split("\t");
45550                 
45551                 if(f.pop().length != 0){
45552                     return;
45553                 }
45554                 
45555                 this.setValue(f.join("\t") + value.substring(end));
45556                 this.el.dom.setSelectionRange(start - 1, start - 1);
45557                 
45558             },
45559             
45560             "home" : function(e){
45561                 e.preventDefault();
45562                 
45563                 var curr = this.el.dom.selectionStart;
45564                 var lines = this.getValue().split("\n");
45565                 
45566                 if(!lines.length){
45567                     return;
45568                 }
45569                 
45570                 if(e.ctrlKey){
45571                     this.el.dom.setSelectionRange(0, 0);
45572                     return;
45573                 }
45574                 
45575                 var pos = 0;
45576                 
45577                 for (var i = 0; i < lines.length;i++) {
45578                     pos += lines[i].length;
45579                     
45580                     if(i != 0){
45581                         pos += 1;
45582                     }
45583                     
45584                     if(pos < curr){
45585                         continue;
45586                     }
45587                     
45588                     pos -= lines[i].length;
45589                     
45590                     break;
45591                 }
45592                 
45593                 if(!e.shiftKey){
45594                     this.el.dom.setSelectionRange(pos, pos);
45595                     return;
45596                 }
45597                 
45598                 this.el.dom.selectionStart = pos;
45599                 this.el.dom.selectionEnd = curr;
45600             },
45601             
45602             "end" : function(e){
45603                 e.preventDefault();
45604                 
45605                 var curr = this.el.dom.selectionStart;
45606                 var lines = this.getValue().split("\n");
45607                 
45608                 if(!lines.length){
45609                     return;
45610                 }
45611                 
45612                 if(e.ctrlKey){
45613                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
45614                     return;
45615                 }
45616                 
45617                 var pos = 0;
45618                 
45619                 for (var i = 0; i < lines.length;i++) {
45620                     
45621                     pos += lines[i].length;
45622                     
45623                     if(i != 0){
45624                         pos += 1;
45625                     }
45626                     
45627                     if(pos < curr){
45628                         continue;
45629                     }
45630                     
45631                     break;
45632                 }
45633                 
45634                 if(!e.shiftKey){
45635                     this.el.dom.setSelectionRange(pos, pos);
45636                     return;
45637                 }
45638                 
45639                 this.el.dom.selectionStart = curr;
45640                 this.el.dom.selectionEnd = pos;
45641             },
45642
45643             scope : this,
45644
45645             doRelay : function(foo, bar, hname){
45646                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
45647             },
45648
45649             forceKeyDown: true
45650         });
45651         
45652 //        if(this.autosave && this.w){
45653 //            this.autoSaveFn = setInterval(this.autosave, 1000);
45654 //        }
45655     },
45656
45657     // private
45658     onResize : function(w, h)
45659     {
45660         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
45661         var ew = false;
45662         var eh = false;
45663         
45664         if(this.el ){
45665             if(typeof w == 'number'){
45666                 var aw = w - this.wrap.getFrameWidth('lr');
45667                 this.el.setWidth(this.adjustWidth('textarea', aw));
45668                 ew = aw;
45669             }
45670             if(typeof h == 'number'){
45671                 var tbh = 0;
45672                 for (var i =0; i < this.toolbars.length;i++) {
45673                     // fixme - ask toolbars for heights?
45674                     tbh += this.toolbars[i].tb.el.getHeight();
45675                     if (this.toolbars[i].footer) {
45676                         tbh += this.toolbars[i].footer.el.getHeight();
45677                     }
45678                 }
45679                 
45680                 
45681                 
45682                 
45683                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
45684                 ah -= 5; // knock a few pixes off for look..
45685 //                Roo.log(ah);
45686                 this.el.setHeight(this.adjustWidth('textarea', ah));
45687                 var eh = ah;
45688             }
45689         }
45690         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
45691         this.editorcore.onResize(ew,eh);
45692         
45693     },
45694
45695     /**
45696      * Toggles the editor between standard and source edit mode.
45697      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
45698      */
45699     toggleSourceEdit : function(sourceEditMode)
45700     {
45701         this.editorcore.toggleSourceEdit(sourceEditMode);
45702         
45703         if(this.editorcore.sourceEditMode){
45704             Roo.log('editor - showing textarea');
45705             
45706 //            Roo.log('in');
45707 //            Roo.log(this.syncValue());
45708             this.editorcore.syncValue();
45709             this.el.removeClass('x-hidden');
45710             this.el.dom.removeAttribute('tabIndex');
45711             this.el.focus();
45712             
45713             for (var i = 0; i < this.toolbars.length; i++) {
45714                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
45715                     this.toolbars[i].tb.hide();
45716                     this.toolbars[i].footer.hide();
45717                 }
45718             }
45719             
45720         }else{
45721             Roo.log('editor - hiding textarea');
45722 //            Roo.log('out')
45723 //            Roo.log(this.pushValue()); 
45724             this.editorcore.pushValue();
45725             
45726             this.el.addClass('x-hidden');
45727             this.el.dom.setAttribute('tabIndex', -1);
45728             
45729             for (var i = 0; i < this.toolbars.length; i++) {
45730                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
45731                     this.toolbars[i].tb.show();
45732                     this.toolbars[i].footer.show();
45733                 }
45734             }
45735             
45736             //this.deferFocus();
45737         }
45738         
45739         this.setSize(this.wrap.getSize());
45740         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
45741         
45742         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
45743     },
45744  
45745     // private (for BoxComponent)
45746     adjustSize : Roo.BoxComponent.prototype.adjustSize,
45747
45748     // private (for BoxComponent)
45749     getResizeEl : function(){
45750         return this.wrap;
45751     },
45752
45753     // private (for BoxComponent)
45754     getPositionEl : function(){
45755         return this.wrap;
45756     },
45757
45758     // private
45759     initEvents : function(){
45760         this.originalValue = this.getValue();
45761     },
45762
45763     /**
45764      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
45765      * @method
45766      */
45767     markInvalid : Roo.emptyFn,
45768     /**
45769      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
45770      * @method
45771      */
45772     clearInvalid : Roo.emptyFn,
45773
45774     setValue : function(v){
45775         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
45776         this.editorcore.pushValue();
45777     },
45778
45779      
45780     // private
45781     deferFocus : function(){
45782         this.focus.defer(10, this);
45783     },
45784
45785     // doc'ed in Field
45786     focus : function(){
45787         this.editorcore.focus();
45788         
45789     },
45790       
45791
45792     // private
45793     onDestroy : function(){
45794         
45795         
45796         
45797         if(this.rendered){
45798             
45799             for (var i =0; i < this.toolbars.length;i++) {
45800                 // fixme - ask toolbars for heights?
45801                 this.toolbars[i].onDestroy();
45802             }
45803             
45804             this.wrap.dom.innerHTML = '';
45805             this.wrap.remove();
45806         }
45807     },
45808
45809     // private
45810     onFirstFocus : function(){
45811         //Roo.log("onFirstFocus");
45812         this.editorcore.onFirstFocus();
45813          for (var i =0; i < this.toolbars.length;i++) {
45814             this.toolbars[i].onFirstFocus();
45815         }
45816         
45817     },
45818     
45819     // private
45820     syncValue : function()
45821     {
45822         this.editorcore.syncValue();
45823     },
45824     
45825     pushValue : function()
45826     {
45827         this.editorcore.pushValue();
45828     },
45829     
45830     setStylesheets : function(stylesheets)
45831     {
45832         this.editorcore.setStylesheets(stylesheets);
45833     },
45834     
45835     removeStylesheets : function()
45836     {
45837         this.editorcore.removeStylesheets();
45838     }
45839      
45840     
45841     // hide stuff that is not compatible
45842     /**
45843      * @event blur
45844      * @hide
45845      */
45846     /**
45847      * @event change
45848      * @hide
45849      */
45850     /**
45851      * @event focus
45852      * @hide
45853      */
45854     /**
45855      * @event specialkey
45856      * @hide
45857      */
45858     /**
45859      * @cfg {String} fieldClass @hide
45860      */
45861     /**
45862      * @cfg {String} focusClass @hide
45863      */
45864     /**
45865      * @cfg {String} autoCreate @hide
45866      */
45867     /**
45868      * @cfg {String} inputType @hide
45869      */
45870     /**
45871      * @cfg {String} invalidClass @hide
45872      */
45873     /**
45874      * @cfg {String} invalidText @hide
45875      */
45876     /**
45877      * @cfg {String} msgFx @hide
45878      */
45879     /**
45880      * @cfg {String} validateOnBlur @hide
45881      */
45882 });
45883  
45884     // <script type="text/javascript">
45885 /*
45886  * Based on
45887  * Ext JS Library 1.1.1
45888  * Copyright(c) 2006-2007, Ext JS, LLC.
45889  *  
45890  
45891  */
45892
45893 /**
45894  * @class Roo.form.HtmlEditorToolbar1
45895  * Basic Toolbar
45896  * 
45897  * Usage:
45898  *
45899  new Roo.form.HtmlEditor({
45900     ....
45901     toolbars : [
45902         new Roo.form.HtmlEditorToolbar1({
45903             disable : { fonts: 1 , format: 1, ..., ... , ...],
45904             btns : [ .... ]
45905         })
45906     }
45907      
45908  * 
45909  * @cfg {Object} disable List of elements to disable..
45910  * @cfg {Array} btns List of additional buttons.
45911  * 
45912  * 
45913  * NEEDS Extra CSS? 
45914  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
45915  */
45916  
45917 Roo.form.HtmlEditor.ToolbarStandard = function(config)
45918 {
45919     
45920     Roo.apply(this, config);
45921     
45922     // default disabled, based on 'good practice'..
45923     this.disable = this.disable || {};
45924     Roo.applyIf(this.disable, {
45925         fontSize : true,
45926         colors : true,
45927         specialElements : true
45928     });
45929     
45930     
45931     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
45932     // dont call parent... till later.
45933 }
45934
45935 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
45936     
45937     tb: false,
45938     
45939     rendered: false,
45940     
45941     editor : false,
45942     editorcore : false,
45943     /**
45944      * @cfg {Object} disable  List of toolbar elements to disable
45945          
45946      */
45947     disable : false,
45948     
45949     
45950      /**
45951      * @cfg {String} createLinkText The default text for the create link prompt
45952      */
45953     createLinkText : 'Please enter the URL for the link:',
45954     /**
45955      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
45956      */
45957     defaultLinkValue : 'http:/'+'/',
45958    
45959     
45960       /**
45961      * @cfg {Array} fontFamilies An array of available font families
45962      */
45963     fontFamilies : [
45964         'Arial',
45965         'Courier New',
45966         'Tahoma',
45967         'Times New Roman',
45968         'Verdana'
45969     ],
45970     
45971     specialChars : [
45972            "&#169;",
45973           "&#174;",     
45974           "&#8482;",    
45975           "&#163;" ,    
45976          // "&#8212;",    
45977           "&#8230;",    
45978           "&#247;" ,    
45979         //  "&#225;" ,     ?? a acute?
45980            "&#8364;"    , //Euro
45981        //   "&#8220;"    ,
45982         //  "&#8221;"    ,
45983         //  "&#8226;"    ,
45984           "&#176;"  //   , // degrees
45985
45986          // "&#233;"     , // e ecute
45987          // "&#250;"     , // u ecute?
45988     ],
45989     
45990     specialElements : [
45991         {
45992             text: "Insert Table",
45993             xtype: 'MenuItem',
45994             xns : Roo.Menu,
45995             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
45996                 
45997         },
45998         {    
45999             text: "Insert Image",
46000             xtype: 'MenuItem',
46001             xns : Roo.Menu,
46002             ihtml : '<img src="about:blank"/>'
46003             
46004         }
46005         
46006          
46007     ],
46008     
46009     
46010     inputElements : [ 
46011             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
46012             "input:submit", "input:button", "select", "textarea", "label" ],
46013     formats : [
46014         ["p"] ,  
46015         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
46016         ["pre"],[ "code"], 
46017         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
46018         ['div'],['span'],
46019         ['sup'],['sub']
46020     ],
46021     
46022     cleanStyles : [
46023         "font-size"
46024     ],
46025      /**
46026      * @cfg {String} defaultFont default font to use.
46027      */
46028     defaultFont: 'tahoma',
46029    
46030     fontSelect : false,
46031     
46032     
46033     formatCombo : false,
46034     
46035     init : function(editor)
46036     {
46037         this.editor = editor;
46038         this.editorcore = editor.editorcore ? editor.editorcore : editor;
46039         var editorcore = this.editorcore;
46040         
46041         var _t = this;
46042         
46043         var fid = editorcore.frameId;
46044         var etb = this;
46045         function btn(id, toggle, handler){
46046             var xid = fid + '-'+ id ;
46047             return {
46048                 id : xid,
46049                 cmd : id,
46050                 cls : 'x-btn-icon x-edit-'+id,
46051                 enableToggle:toggle !== false,
46052                 scope: _t, // was editor...
46053                 handler:handler||_t.relayBtnCmd,
46054                 clickEvent:'mousedown',
46055                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
46056                 tabIndex:-1
46057             };
46058         }
46059         
46060         
46061         
46062         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
46063         this.tb = tb;
46064          // stop form submits
46065         tb.el.on('click', function(e){
46066             e.preventDefault(); // what does this do?
46067         });
46068
46069         if(!this.disable.font) { // && !Roo.isSafari){
46070             /* why no safari for fonts 
46071             editor.fontSelect = tb.el.createChild({
46072                 tag:'select',
46073                 tabIndex: -1,
46074                 cls:'x-font-select',
46075                 html: this.createFontOptions()
46076             });
46077             
46078             editor.fontSelect.on('change', function(){
46079                 var font = editor.fontSelect.dom.value;
46080                 editor.relayCmd('fontname', font);
46081                 editor.deferFocus();
46082             }, editor);
46083             
46084             tb.add(
46085                 editor.fontSelect.dom,
46086                 '-'
46087             );
46088             */
46089             
46090         };
46091         if(!this.disable.formats){
46092             this.formatCombo = new Roo.form.ComboBox({
46093                 store: new Roo.data.SimpleStore({
46094                     id : 'tag',
46095                     fields: ['tag'],
46096                     data : this.formats // from states.js
46097                 }),
46098                 blockFocus : true,
46099                 name : '',
46100                 //autoCreate : {tag: "div",  size: "20"},
46101                 displayField:'tag',
46102                 typeAhead: false,
46103                 mode: 'local',
46104                 editable : false,
46105                 triggerAction: 'all',
46106                 emptyText:'Add tag',
46107                 selectOnFocus:true,
46108                 width:135,
46109                 listeners : {
46110                     'select': function(c, r, i) {
46111                         editorcore.insertTag(r.get('tag'));
46112                         editor.focus();
46113                     }
46114                 }
46115
46116             });
46117             tb.addField(this.formatCombo);
46118             
46119         }
46120         
46121         if(!this.disable.format){
46122             tb.add(
46123                 btn('bold'),
46124                 btn('italic'),
46125                 btn('underline'),
46126                 btn('strikethrough')
46127             );
46128         };
46129         if(!this.disable.fontSize){
46130             tb.add(
46131                 '-',
46132                 
46133                 
46134                 btn('increasefontsize', false, editorcore.adjustFont),
46135                 btn('decreasefontsize', false, editorcore.adjustFont)
46136             );
46137         };
46138         
46139         
46140         if(!this.disable.colors){
46141             tb.add(
46142                 '-', {
46143                     id:editorcore.frameId +'-forecolor',
46144                     cls:'x-btn-icon x-edit-forecolor',
46145                     clickEvent:'mousedown',
46146                     tooltip: this.buttonTips['forecolor'] || undefined,
46147                     tabIndex:-1,
46148                     menu : new Roo.menu.ColorMenu({
46149                         allowReselect: true,
46150                         focus: Roo.emptyFn,
46151                         value:'000000',
46152                         plain:true,
46153                         selectHandler: function(cp, color){
46154                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
46155                             editor.deferFocus();
46156                         },
46157                         scope: editorcore,
46158                         clickEvent:'mousedown'
46159                     })
46160                 }, {
46161                     id:editorcore.frameId +'backcolor',
46162                     cls:'x-btn-icon x-edit-backcolor',
46163                     clickEvent:'mousedown',
46164                     tooltip: this.buttonTips['backcolor'] || undefined,
46165                     tabIndex:-1,
46166                     menu : new Roo.menu.ColorMenu({
46167                         focus: Roo.emptyFn,
46168                         value:'FFFFFF',
46169                         plain:true,
46170                         allowReselect: true,
46171                         selectHandler: function(cp, color){
46172                             if(Roo.isGecko){
46173                                 editorcore.execCmd('useCSS', false);
46174                                 editorcore.execCmd('hilitecolor', color);
46175                                 editorcore.execCmd('useCSS', true);
46176                                 editor.deferFocus();
46177                             }else{
46178                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
46179                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
46180                                 editor.deferFocus();
46181                             }
46182                         },
46183                         scope:editorcore,
46184                         clickEvent:'mousedown'
46185                     })
46186                 }
46187             );
46188         };
46189         // now add all the items...
46190         
46191
46192         if(!this.disable.alignments){
46193             tb.add(
46194                 '-',
46195                 btn('justifyleft'),
46196                 btn('justifycenter'),
46197                 btn('justifyright')
46198             );
46199         };
46200
46201         //if(!Roo.isSafari){
46202             if(!this.disable.links){
46203                 tb.add(
46204                     '-',
46205                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
46206                 );
46207             };
46208
46209             if(!this.disable.lists){
46210                 tb.add(
46211                     '-',
46212                     btn('insertorderedlist'),
46213                     btn('insertunorderedlist')
46214                 );
46215             }
46216             if(!this.disable.sourceEdit){
46217                 tb.add(
46218                     '-',
46219                     btn('sourceedit', true, function(btn){
46220                         this.toggleSourceEdit(btn.pressed);
46221                     })
46222                 );
46223             }
46224         //}
46225         
46226         var smenu = { };
46227         // special menu.. - needs to be tidied up..
46228         if (!this.disable.special) {
46229             smenu = {
46230                 text: "&#169;",
46231                 cls: 'x-edit-none',
46232                 
46233                 menu : {
46234                     items : []
46235                 }
46236             };
46237             for (var i =0; i < this.specialChars.length; i++) {
46238                 smenu.menu.items.push({
46239                     
46240                     html: this.specialChars[i],
46241                     handler: function(a,b) {
46242                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
46243                         //editor.insertAtCursor(a.html);
46244                         
46245                     },
46246                     tabIndex:-1
46247                 });
46248             }
46249             
46250             
46251             tb.add(smenu);
46252             
46253             
46254         }
46255         
46256         var cmenu = { };
46257         if (!this.disable.cleanStyles) {
46258             cmenu = {
46259                 cls: 'x-btn-icon x-btn-clear',
46260                 
46261                 menu : {
46262                     items : []
46263                 }
46264             };
46265             for (var i =0; i < this.cleanStyles.length; i++) {
46266                 cmenu.menu.items.push({
46267                     actiontype : this.cleanStyles[i],
46268                     html: 'Remove ' + this.cleanStyles[i],
46269                     handler: function(a,b) {
46270 //                        Roo.log(a);
46271 //                        Roo.log(b);
46272                         var c = Roo.get(editorcore.doc.body);
46273                         c.select('[style]').each(function(s) {
46274                             s.dom.style.removeProperty(a.actiontype);
46275                         });
46276                         editorcore.syncValue();
46277                     },
46278                     tabIndex:-1
46279                 });
46280             }
46281              cmenu.menu.items.push({
46282                 actiontype : 'tablewidths',
46283                 html: 'Remove Table Widths',
46284                 handler: function(a,b) {
46285                     editorcore.cleanTableWidths();
46286                     editorcore.syncValue();
46287                 },
46288                 tabIndex:-1
46289             });
46290             cmenu.menu.items.push({
46291                 actiontype : 'word',
46292                 html: 'Remove MS Word Formating',
46293                 handler: function(a,b) {
46294                     editorcore.cleanWord();
46295                     editorcore.syncValue();
46296                 },
46297                 tabIndex:-1
46298             });
46299             
46300             cmenu.menu.items.push({
46301                 actiontype : 'all',
46302                 html: 'Remove All Styles',
46303                 handler: function(a,b) {
46304                     
46305                     var c = Roo.get(editorcore.doc.body);
46306                     c.select('[style]').each(function(s) {
46307                         s.dom.removeAttribute('style');
46308                     });
46309                     editorcore.syncValue();
46310                 },
46311                 tabIndex:-1
46312             });
46313             
46314             cmenu.menu.items.push({
46315                 actiontype : 'all',
46316                 html: 'Remove All CSS Classes',
46317                 handler: function(a,b) {
46318                     
46319                     var c = Roo.get(editorcore.doc.body);
46320                     c.select('[class]').each(function(s) {
46321                         s.dom.removeAttribute('class');
46322                     });
46323                     editorcore.cleanWord();
46324                     editorcore.syncValue();
46325                 },
46326                 tabIndex:-1
46327             });
46328             
46329              cmenu.menu.items.push({
46330                 actiontype : 'tidy',
46331                 html: 'Tidy HTML Source',
46332                 handler: function(a,b) {
46333                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
46334                     editorcore.syncValue();
46335                 },
46336                 tabIndex:-1
46337             });
46338             
46339             
46340             tb.add(cmenu);
46341         }
46342          
46343         if (!this.disable.specialElements) {
46344             var semenu = {
46345                 text: "Other;",
46346                 cls: 'x-edit-none',
46347                 menu : {
46348                     items : []
46349                 }
46350             };
46351             for (var i =0; i < this.specialElements.length; i++) {
46352                 semenu.menu.items.push(
46353                     Roo.apply({ 
46354                         handler: function(a,b) {
46355                             editor.insertAtCursor(this.ihtml);
46356                         }
46357                     }, this.specialElements[i])
46358                 );
46359                     
46360             }
46361             
46362             tb.add(semenu);
46363             
46364             
46365         }
46366          
46367         
46368         if (this.btns) {
46369             for(var i =0; i< this.btns.length;i++) {
46370                 var b = Roo.factory(this.btns[i],Roo.form);
46371                 b.cls =  'x-edit-none';
46372                 
46373                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
46374                     b.cls += ' x-init-enable';
46375                 }
46376                 
46377                 b.scope = editorcore;
46378                 tb.add(b);
46379             }
46380         
46381         }
46382         
46383         
46384         
46385         // disable everything...
46386         
46387         this.tb.items.each(function(item){
46388             
46389            if(
46390                 item.id != editorcore.frameId+ '-sourceedit' && 
46391                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
46392             ){
46393                 
46394                 item.disable();
46395             }
46396         });
46397         this.rendered = true;
46398         
46399         // the all the btns;
46400         editor.on('editorevent', this.updateToolbar, this);
46401         // other toolbars need to implement this..
46402         //editor.on('editmodechange', this.updateToolbar, this);
46403     },
46404     
46405     
46406     relayBtnCmd : function(btn) {
46407         this.editorcore.relayCmd(btn.cmd);
46408     },
46409     // private used internally
46410     createLink : function(){
46411         Roo.log("create link?");
46412         var url = prompt(this.createLinkText, this.defaultLinkValue);
46413         if(url && url != 'http:/'+'/'){
46414             this.editorcore.relayCmd('createlink', url);
46415         }
46416     },
46417
46418     
46419     /**
46420      * Protected method that will not generally be called directly. It triggers
46421      * a toolbar update by reading the markup state of the current selection in the editor.
46422      */
46423     updateToolbar: function(){
46424
46425         if(!this.editorcore.activated){
46426             this.editor.onFirstFocus();
46427             return;
46428         }
46429
46430         var btns = this.tb.items.map, 
46431             doc = this.editorcore.doc,
46432             frameId = this.editorcore.frameId;
46433
46434         if(!this.disable.font && !Roo.isSafari){
46435             /*
46436             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
46437             if(name != this.fontSelect.dom.value){
46438                 this.fontSelect.dom.value = name;
46439             }
46440             */
46441         }
46442         if(!this.disable.format){
46443             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
46444             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
46445             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
46446             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
46447         }
46448         if(!this.disable.alignments){
46449             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
46450             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
46451             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
46452         }
46453         if(!Roo.isSafari && !this.disable.lists){
46454             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
46455             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
46456         }
46457         
46458         var ans = this.editorcore.getAllAncestors();
46459         if (this.formatCombo) {
46460             
46461             
46462             var store = this.formatCombo.store;
46463             this.formatCombo.setValue("");
46464             for (var i =0; i < ans.length;i++) {
46465                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
46466                     // select it..
46467                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
46468                     break;
46469                 }
46470             }
46471         }
46472         
46473         
46474         
46475         // hides menus... - so this cant be on a menu...
46476         Roo.menu.MenuMgr.hideAll();
46477
46478         //this.editorsyncValue();
46479     },
46480    
46481     
46482     createFontOptions : function(){
46483         var buf = [], fs = this.fontFamilies, ff, lc;
46484         
46485         
46486         
46487         for(var i = 0, len = fs.length; i< len; i++){
46488             ff = fs[i];
46489             lc = ff.toLowerCase();
46490             buf.push(
46491                 '<option value="',lc,'" style="font-family:',ff,';"',
46492                     (this.defaultFont == lc ? ' selected="true">' : '>'),
46493                     ff,
46494                 '</option>'
46495             );
46496         }
46497         return buf.join('');
46498     },
46499     
46500     toggleSourceEdit : function(sourceEditMode){
46501         
46502         Roo.log("toolbar toogle");
46503         if(sourceEditMode === undefined){
46504             sourceEditMode = !this.sourceEditMode;
46505         }
46506         this.sourceEditMode = sourceEditMode === true;
46507         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
46508         // just toggle the button?
46509         if(btn.pressed !== this.sourceEditMode){
46510             btn.toggle(this.sourceEditMode);
46511             return;
46512         }
46513         
46514         if(sourceEditMode){
46515             Roo.log("disabling buttons");
46516             this.tb.items.each(function(item){
46517                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
46518                     item.disable();
46519                 }
46520             });
46521           
46522         }else{
46523             Roo.log("enabling buttons");
46524             if(this.editorcore.initialized){
46525                 this.tb.items.each(function(item){
46526                     item.enable();
46527                 });
46528             }
46529             
46530         }
46531         Roo.log("calling toggole on editor");
46532         // tell the editor that it's been pressed..
46533         this.editor.toggleSourceEdit(sourceEditMode);
46534        
46535     },
46536      /**
46537      * Object collection of toolbar tooltips for the buttons in the editor. The key
46538      * is the command id associated with that button and the value is a valid QuickTips object.
46539      * For example:
46540 <pre><code>
46541 {
46542     bold : {
46543         title: 'Bold (Ctrl+B)',
46544         text: 'Make the selected text bold.',
46545         cls: 'x-html-editor-tip'
46546     },
46547     italic : {
46548         title: 'Italic (Ctrl+I)',
46549         text: 'Make the selected text italic.',
46550         cls: 'x-html-editor-tip'
46551     },
46552     ...
46553 </code></pre>
46554     * @type Object
46555      */
46556     buttonTips : {
46557         bold : {
46558             title: 'Bold (Ctrl+B)',
46559             text: 'Make the selected text bold.',
46560             cls: 'x-html-editor-tip'
46561         },
46562         italic : {
46563             title: 'Italic (Ctrl+I)',
46564             text: 'Make the selected text italic.',
46565             cls: 'x-html-editor-tip'
46566         },
46567         underline : {
46568             title: 'Underline (Ctrl+U)',
46569             text: 'Underline the selected text.',
46570             cls: 'x-html-editor-tip'
46571         },
46572         strikethrough : {
46573             title: 'Strikethrough',
46574             text: 'Strikethrough the selected text.',
46575             cls: 'x-html-editor-tip'
46576         },
46577         increasefontsize : {
46578             title: 'Grow Text',
46579             text: 'Increase the font size.',
46580             cls: 'x-html-editor-tip'
46581         },
46582         decreasefontsize : {
46583             title: 'Shrink Text',
46584             text: 'Decrease the font size.',
46585             cls: 'x-html-editor-tip'
46586         },
46587         backcolor : {
46588             title: 'Text Highlight Color',
46589             text: 'Change the background color of the selected text.',
46590             cls: 'x-html-editor-tip'
46591         },
46592         forecolor : {
46593             title: 'Font Color',
46594             text: 'Change the color of the selected text.',
46595             cls: 'x-html-editor-tip'
46596         },
46597         justifyleft : {
46598             title: 'Align Text Left',
46599             text: 'Align text to the left.',
46600             cls: 'x-html-editor-tip'
46601         },
46602         justifycenter : {
46603             title: 'Center Text',
46604             text: 'Center text in the editor.',
46605             cls: 'x-html-editor-tip'
46606         },
46607         justifyright : {
46608             title: 'Align Text Right',
46609             text: 'Align text to the right.',
46610             cls: 'x-html-editor-tip'
46611         },
46612         insertunorderedlist : {
46613             title: 'Bullet List',
46614             text: 'Start a bulleted list.',
46615             cls: 'x-html-editor-tip'
46616         },
46617         insertorderedlist : {
46618             title: 'Numbered List',
46619             text: 'Start a numbered list.',
46620             cls: 'x-html-editor-tip'
46621         },
46622         createlink : {
46623             title: 'Hyperlink',
46624             text: 'Make the selected text a hyperlink.',
46625             cls: 'x-html-editor-tip'
46626         },
46627         sourceedit : {
46628             title: 'Source Edit',
46629             text: 'Switch to source editing mode.',
46630             cls: 'x-html-editor-tip'
46631         }
46632     },
46633     // private
46634     onDestroy : function(){
46635         if(this.rendered){
46636             
46637             this.tb.items.each(function(item){
46638                 if(item.menu){
46639                     item.menu.removeAll();
46640                     if(item.menu.el){
46641                         item.menu.el.destroy();
46642                     }
46643                 }
46644                 item.destroy();
46645             });
46646              
46647         }
46648     },
46649     onFirstFocus: function() {
46650         this.tb.items.each(function(item){
46651            item.enable();
46652         });
46653     }
46654 });
46655
46656
46657
46658
46659 // <script type="text/javascript">
46660 /*
46661  * Based on
46662  * Ext JS Library 1.1.1
46663  * Copyright(c) 2006-2007, Ext JS, LLC.
46664  *  
46665  
46666  */
46667
46668  
46669 /**
46670  * @class Roo.form.HtmlEditor.ToolbarContext
46671  * Context Toolbar
46672  * 
46673  * Usage:
46674  *
46675  new Roo.form.HtmlEditor({
46676     ....
46677     toolbars : [
46678         { xtype: 'ToolbarStandard', styles : {} }
46679         { xtype: 'ToolbarContext', disable : {} }
46680     ]
46681 })
46682
46683      
46684  * 
46685  * @config : {Object} disable List of elements to disable.. (not done yet.)
46686  * @config : {Object} styles  Map of styles available.
46687  * 
46688  */
46689
46690 Roo.form.HtmlEditor.ToolbarContext = function(config)
46691 {
46692     
46693     Roo.apply(this, config);
46694     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
46695     // dont call parent... till later.
46696     this.styles = this.styles || {};
46697 }
46698
46699  
46700
46701 Roo.form.HtmlEditor.ToolbarContext.types = {
46702     'IMG' : {
46703         width : {
46704             title: "Width",
46705             width: 40
46706         },
46707         height:  {
46708             title: "Height",
46709             width: 40
46710         },
46711         align: {
46712             title: "Align",
46713             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
46714             width : 80
46715             
46716         },
46717         border: {
46718             title: "Border",
46719             width: 40
46720         },
46721         alt: {
46722             title: "Alt",
46723             width: 120
46724         },
46725         src : {
46726             title: "Src",
46727             width: 220
46728         }
46729         
46730     },
46731     'A' : {
46732         name : {
46733             title: "Name",
46734             width: 50
46735         },
46736         target:  {
46737             title: "Target",
46738             width: 120
46739         },
46740         href:  {
46741             title: "Href",
46742             width: 220
46743         } // border?
46744         
46745     },
46746     'TABLE' : {
46747         rows : {
46748             title: "Rows",
46749             width: 20
46750         },
46751         cols : {
46752             title: "Cols",
46753             width: 20
46754         },
46755         width : {
46756             title: "Width",
46757             width: 40
46758         },
46759         height : {
46760             title: "Height",
46761             width: 40
46762         },
46763         border : {
46764             title: "Border",
46765             width: 20
46766         }
46767     },
46768     'TD' : {
46769         width : {
46770             title: "Width",
46771             width: 40
46772         },
46773         height : {
46774             title: "Height",
46775             width: 40
46776         },   
46777         align: {
46778             title: "Align",
46779             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
46780             width: 80
46781         },
46782         valign: {
46783             title: "Valign",
46784             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
46785             width: 80
46786         },
46787         colspan: {
46788             title: "Colspan",
46789             width: 20
46790             
46791         },
46792          'font-family'  : {
46793             title : "Font",
46794             style : 'fontFamily',
46795             displayField: 'display',
46796             optname : 'font-family',
46797             width: 140
46798         }
46799     },
46800     'INPUT' : {
46801         name : {
46802             title: "name",
46803             width: 120
46804         },
46805         value : {
46806             title: "Value",
46807             width: 120
46808         },
46809         width : {
46810             title: "Width",
46811             width: 40
46812         }
46813     },
46814     'LABEL' : {
46815         'for' : {
46816             title: "For",
46817             width: 120
46818         }
46819     },
46820     'TEXTAREA' : {
46821           name : {
46822             title: "name",
46823             width: 120
46824         },
46825         rows : {
46826             title: "Rows",
46827             width: 20
46828         },
46829         cols : {
46830             title: "Cols",
46831             width: 20
46832         }
46833     },
46834     'SELECT' : {
46835         name : {
46836             title: "name",
46837             width: 120
46838         },
46839         selectoptions : {
46840             title: "Options",
46841             width: 200
46842         }
46843     },
46844     
46845     // should we really allow this??
46846     // should this just be 
46847     'BODY' : {
46848         title : {
46849             title: "Title",
46850             width: 200,
46851             disabled : true
46852         }
46853     },
46854     'SPAN' : {
46855         'font-family'  : {
46856             title : "Font",
46857             style : 'fontFamily',
46858             displayField: 'display',
46859             optname : 'font-family',
46860             width: 140
46861         }
46862     },
46863     'DIV' : {
46864         'font-family'  : {
46865             title : "Font",
46866             style : 'fontFamily',
46867             displayField: 'display',
46868             optname : 'font-family',
46869             width: 140
46870         }
46871     },
46872      'P' : {
46873         'font-family'  : {
46874             title : "Font",
46875             style : 'fontFamily',
46876             displayField: 'display',
46877             optname : 'font-family',
46878             width: 140
46879         }
46880     },
46881     
46882     '*' : {
46883         // empty..
46884     }
46885
46886 };
46887
46888 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
46889 Roo.form.HtmlEditor.ToolbarContext.stores = false;
46890
46891 Roo.form.HtmlEditor.ToolbarContext.options = {
46892         'font-family'  : [ 
46893                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
46894                 [ 'Courier New', 'Courier New'],
46895                 [ 'Tahoma', 'Tahoma'],
46896                 [ 'Times New Roman,serif', 'Times'],
46897                 [ 'Verdana','Verdana' ]
46898         ]
46899 };
46900
46901 // fixme - these need to be configurable..
46902  
46903
46904 //Roo.form.HtmlEditor.ToolbarContext.types
46905
46906
46907 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
46908     
46909     tb: false,
46910     
46911     rendered: false,
46912     
46913     editor : false,
46914     editorcore : false,
46915     /**
46916      * @cfg {Object} disable  List of toolbar elements to disable
46917          
46918      */
46919     disable : false,
46920     /**
46921      * @cfg {Object} styles List of styles 
46922      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
46923      *
46924      * These must be defined in the page, so they get rendered correctly..
46925      * .headline { }
46926      * TD.underline { }
46927      * 
46928      */
46929     styles : false,
46930     
46931     options: false,
46932     
46933     toolbars : false,
46934     
46935     init : function(editor)
46936     {
46937         this.editor = editor;
46938         this.editorcore = editor.editorcore ? editor.editorcore : editor;
46939         var editorcore = this.editorcore;
46940         
46941         var fid = editorcore.frameId;
46942         var etb = this;
46943         function btn(id, toggle, handler){
46944             var xid = fid + '-'+ id ;
46945             return {
46946                 id : xid,
46947                 cmd : id,
46948                 cls : 'x-btn-icon x-edit-'+id,
46949                 enableToggle:toggle !== false,
46950                 scope: editorcore, // was editor...
46951                 handler:handler||editorcore.relayBtnCmd,
46952                 clickEvent:'mousedown',
46953                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
46954                 tabIndex:-1
46955             };
46956         }
46957         // create a new element.
46958         var wdiv = editor.wrap.createChild({
46959                 tag: 'div'
46960             }, editor.wrap.dom.firstChild.nextSibling, true);
46961         
46962         // can we do this more than once??
46963         
46964          // stop form submits
46965       
46966  
46967         // disable everything...
46968         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
46969         this.toolbars = {};
46970            
46971         for (var i in  ty) {
46972           
46973             this.toolbars[i] = this.buildToolbar(ty[i],i);
46974         }
46975         this.tb = this.toolbars.BODY;
46976         this.tb.el.show();
46977         this.buildFooter();
46978         this.footer.show();
46979         editor.on('hide', function( ) { this.footer.hide() }, this);
46980         editor.on('show', function( ) { this.footer.show() }, this);
46981         
46982          
46983         this.rendered = true;
46984         
46985         // the all the btns;
46986         editor.on('editorevent', this.updateToolbar, this);
46987         // other toolbars need to implement this..
46988         //editor.on('editmodechange', this.updateToolbar, this);
46989     },
46990     
46991     
46992     
46993     /**
46994      * Protected method that will not generally be called directly. It triggers
46995      * a toolbar update by reading the markup state of the current selection in the editor.
46996      *
46997      * Note you can force an update by calling on('editorevent', scope, false)
46998      */
46999     updateToolbar: function(editor,ev,sel){
47000
47001         //Roo.log(ev);
47002         // capture mouse up - this is handy for selecting images..
47003         // perhaps should go somewhere else...
47004         if(!this.editorcore.activated){
47005              this.editor.onFirstFocus();
47006             return;
47007         }
47008         
47009         
47010         
47011         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
47012         // selectNode - might want to handle IE?
47013         if (ev &&
47014             (ev.type == 'mouseup' || ev.type == 'click' ) &&
47015             ev.target && ev.target.tagName == 'IMG') {
47016             // they have click on an image...
47017             // let's see if we can change the selection...
47018             sel = ev.target;
47019          
47020               var nodeRange = sel.ownerDocument.createRange();
47021             try {
47022                 nodeRange.selectNode(sel);
47023             } catch (e) {
47024                 nodeRange.selectNodeContents(sel);
47025             }
47026             //nodeRange.collapse(true);
47027             var s = this.editorcore.win.getSelection();
47028             s.removeAllRanges();
47029             s.addRange(nodeRange);
47030         }  
47031         
47032       
47033         var updateFooter = sel ? false : true;
47034         
47035         
47036         var ans = this.editorcore.getAllAncestors();
47037         
47038         // pick
47039         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
47040         
47041         if (!sel) { 
47042             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
47043             sel = sel ? sel : this.editorcore.doc.body;
47044             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
47045             
47046         }
47047         // pick a menu that exists..
47048         var tn = sel.tagName.toUpperCase();
47049         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
47050         
47051         tn = sel.tagName.toUpperCase();
47052         
47053         var lastSel = this.tb.selectedNode;
47054         
47055         this.tb.selectedNode = sel;
47056         
47057         // if current menu does not match..
47058         
47059         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode) || ev === false) {
47060                 
47061             this.tb.el.hide();
47062             ///console.log("show: " + tn);
47063             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
47064             this.tb.el.show();
47065             // update name
47066             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
47067             
47068             
47069             // update attributes
47070             if (this.tb.fields) {
47071                 this.tb.fields.each(function(e) {
47072                     if (e.stylename) {
47073                         e.setValue(sel.style[e.stylename]);
47074                         return;
47075                     } 
47076                    e.setValue(sel.getAttribute(e.attrname));
47077                 });
47078             }
47079             
47080             var hasStyles = false;
47081             for(var i in this.styles) {
47082                 hasStyles = true;
47083                 break;
47084             }
47085             
47086             // update styles
47087             if (hasStyles) { 
47088                 var st = this.tb.fields.item(0);
47089                 
47090                 st.store.removeAll();
47091                
47092                 
47093                 var cn = sel.className.split(/\s+/);
47094                 
47095                 var avs = [];
47096                 if (this.styles['*']) {
47097                     
47098                     Roo.each(this.styles['*'], function(v) {
47099                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
47100                     });
47101                 }
47102                 if (this.styles[tn]) { 
47103                     Roo.each(this.styles[tn], function(v) {
47104                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
47105                     });
47106                 }
47107                 
47108                 st.store.loadData(avs);
47109                 st.collapse();
47110                 st.setValue(cn);
47111             }
47112             // flag our selected Node.
47113             this.tb.selectedNode = sel;
47114            
47115            
47116             Roo.menu.MenuMgr.hideAll();
47117
47118         }
47119         
47120         if (!updateFooter) {
47121             //this.footDisp.dom.innerHTML = ''; 
47122             return;
47123         }
47124         // update the footer
47125         //
47126         var html = '';
47127         
47128         this.footerEls = ans.reverse();
47129         Roo.each(this.footerEls, function(a,i) {
47130             if (!a) { return; }
47131             html += html.length ? ' &gt; '  :  '';
47132             
47133             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
47134             
47135         });
47136        
47137         // 
47138         var sz = this.footDisp.up('td').getSize();
47139         this.footDisp.dom.style.width = (sz.width -10) + 'px';
47140         this.footDisp.dom.style.marginLeft = '5px';
47141         
47142         this.footDisp.dom.style.overflow = 'hidden';
47143         
47144         this.footDisp.dom.innerHTML = html;
47145             
47146         //this.editorsyncValue();
47147     },
47148      
47149     
47150    
47151        
47152     // private
47153     onDestroy : function(){
47154         if(this.rendered){
47155             
47156             this.tb.items.each(function(item){
47157                 if(item.menu){
47158                     item.menu.removeAll();
47159                     if(item.menu.el){
47160                         item.menu.el.destroy();
47161                     }
47162                 }
47163                 item.destroy();
47164             });
47165              
47166         }
47167     },
47168     onFirstFocus: function() {
47169         // need to do this for all the toolbars..
47170         this.tb.items.each(function(item){
47171            item.enable();
47172         });
47173     },
47174     buildToolbar: function(tlist, nm)
47175     {
47176         var editor = this.editor;
47177         var editorcore = this.editorcore;
47178          // create a new element.
47179         var wdiv = editor.wrap.createChild({
47180                 tag: 'div'
47181             }, editor.wrap.dom.firstChild.nextSibling, true);
47182         
47183        
47184         var tb = new Roo.Toolbar(wdiv);
47185         // add the name..
47186         
47187         tb.add(nm+ ":&nbsp;");
47188         
47189         var styles = [];
47190         for(var i in this.styles) {
47191             styles.push(i);
47192         }
47193         
47194         // styles...
47195         if (styles && styles.length) {
47196             
47197             // this needs a multi-select checkbox...
47198             tb.addField( new Roo.form.ComboBox({
47199                 store: new Roo.data.SimpleStore({
47200                     id : 'val',
47201                     fields: ['val', 'selected'],
47202                     data : [] 
47203                 }),
47204                 name : '-roo-edit-className',
47205                 attrname : 'className',
47206                 displayField: 'val',
47207                 typeAhead: false,
47208                 mode: 'local',
47209                 editable : false,
47210                 triggerAction: 'all',
47211                 emptyText:'Select Style',
47212                 selectOnFocus:true,
47213                 width: 130,
47214                 listeners : {
47215                     'select': function(c, r, i) {
47216                         // initial support only for on class per el..
47217                         tb.selectedNode.className =  r ? r.get('val') : '';
47218                         editorcore.syncValue();
47219                     }
47220                 }
47221     
47222             }));
47223         }
47224         
47225         var tbc = Roo.form.HtmlEditor.ToolbarContext;
47226         var tbops = tbc.options;
47227         
47228         for (var i in tlist) {
47229             
47230             var item = tlist[i];
47231             tb.add(item.title + ":&nbsp;");
47232             
47233             
47234             //optname == used so you can configure the options available..
47235             var opts = item.opts ? item.opts : false;
47236             if (item.optname) {
47237                 opts = tbops[item.optname];
47238            
47239             }
47240             
47241             if (opts) {
47242                 // opts == pulldown..
47243                 tb.addField( new Roo.form.ComboBox({
47244                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
47245                         id : 'val',
47246                         fields: ['val', 'display'],
47247                         data : opts  
47248                     }),
47249                     name : '-roo-edit-' + i,
47250                     attrname : i,
47251                     stylename : item.style ? item.style : false,
47252                     displayField: item.displayField ? item.displayField : 'val',
47253                     valueField :  'val',
47254                     typeAhead: false,
47255                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
47256                     editable : false,
47257                     triggerAction: 'all',
47258                     emptyText:'Select',
47259                     selectOnFocus:true,
47260                     width: item.width ? item.width  : 130,
47261                     listeners : {
47262                         'select': function(c, r, i) {
47263                             if (c.stylename) {
47264                                 tb.selectedNode.style[c.stylename] =  r.get('val');
47265                                 return;
47266                             }
47267                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
47268                         }
47269                     }
47270
47271                 }));
47272                 continue;
47273                     
47274                  
47275                 
47276                 tb.addField( new Roo.form.TextField({
47277                     name: i,
47278                     width: 100,
47279                     //allowBlank:false,
47280                     value: ''
47281                 }));
47282                 continue;
47283             }
47284             tb.addField( new Roo.form.TextField({
47285                 name: '-roo-edit-' + i,
47286                 attrname : i,
47287                 
47288                 width: item.width,
47289                 //allowBlank:true,
47290                 value: '',
47291                 listeners: {
47292                     'change' : function(f, nv, ov) {
47293                         tb.selectedNode.setAttribute(f.attrname, nv);
47294                         editorcore.syncValue();
47295                     }
47296                 }
47297             }));
47298              
47299         }
47300         
47301         var _this = this;
47302         
47303         if(nm == 'BODY'){
47304             tb.addSeparator();
47305         
47306             tb.addButton( {
47307                 text: 'Stylesheets',
47308
47309                 listeners : {
47310                     click : function ()
47311                     {
47312                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
47313                     }
47314                 }
47315             });
47316         }
47317         
47318         tb.addFill();
47319         tb.addButton( {
47320             text: 'Remove Tag',
47321     
47322             listeners : {
47323                 click : function ()
47324                 {
47325                     // remove
47326                     // undo does not work.
47327                      
47328                     var sn = tb.selectedNode;
47329                     
47330                     var pn = sn.parentNode;
47331                     
47332                     var stn =  sn.childNodes[0];
47333                     var en = sn.childNodes[sn.childNodes.length - 1 ];
47334                     while (sn.childNodes.length) {
47335                         var node = sn.childNodes[0];
47336                         sn.removeChild(node);
47337                         //Roo.log(node);
47338                         pn.insertBefore(node, sn);
47339                         
47340                     }
47341                     pn.removeChild(sn);
47342                     var range = editorcore.createRange();
47343         
47344                     range.setStart(stn,0);
47345                     range.setEnd(en,0); //????
47346                     //range.selectNode(sel);
47347                     
47348                     
47349                     var selection = editorcore.getSelection();
47350                     selection.removeAllRanges();
47351                     selection.addRange(range);
47352                     
47353                     
47354                     
47355                     //_this.updateToolbar(null, null, pn);
47356                     _this.updateToolbar(null, null, null);
47357                     _this.footDisp.dom.innerHTML = ''; 
47358                 }
47359             }
47360             
47361                     
47362                 
47363             
47364         });
47365         
47366         
47367         tb.el.on('click', function(e){
47368             e.preventDefault(); // what does this do?
47369         });
47370         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
47371         tb.el.hide();
47372         tb.name = nm;
47373         // dont need to disable them... as they will get hidden
47374         return tb;
47375          
47376         
47377     },
47378     buildFooter : function()
47379     {
47380         
47381         var fel = this.editor.wrap.createChild();
47382         this.footer = new Roo.Toolbar(fel);
47383         // toolbar has scrolly on left / right?
47384         var footDisp= new Roo.Toolbar.Fill();
47385         var _t = this;
47386         this.footer.add(
47387             {
47388                 text : '&lt;',
47389                 xtype: 'Button',
47390                 handler : function() {
47391                     _t.footDisp.scrollTo('left',0,true)
47392                 }
47393             }
47394         );
47395         this.footer.add( footDisp );
47396         this.footer.add( 
47397             {
47398                 text : '&gt;',
47399                 xtype: 'Button',
47400                 handler : function() {
47401                     // no animation..
47402                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
47403                 }
47404             }
47405         );
47406         var fel = Roo.get(footDisp.el);
47407         fel.addClass('x-editor-context');
47408         this.footDispWrap = fel; 
47409         this.footDispWrap.overflow  = 'hidden';
47410         
47411         this.footDisp = fel.createChild();
47412         this.footDispWrap.on('click', this.onContextClick, this)
47413         
47414         
47415     },
47416     onContextClick : function (ev,dom)
47417     {
47418         ev.preventDefault();
47419         var  cn = dom.className;
47420         //Roo.log(cn);
47421         if (!cn.match(/x-ed-loc-/)) {
47422             return;
47423         }
47424         var n = cn.split('-').pop();
47425         var ans = this.footerEls;
47426         var sel = ans[n];
47427         
47428          // pick
47429         var range = this.editorcore.createRange();
47430         
47431         range.selectNodeContents(sel);
47432         //range.selectNode(sel);
47433         
47434         
47435         var selection = this.editorcore.getSelection();
47436         selection.removeAllRanges();
47437         selection.addRange(range);
47438         
47439         
47440         
47441         this.updateToolbar(null, null, sel);
47442         
47443         
47444     }
47445     
47446     
47447     
47448     
47449     
47450 });
47451
47452
47453
47454
47455
47456 /*
47457  * Based on:
47458  * Ext JS Library 1.1.1
47459  * Copyright(c) 2006-2007, Ext JS, LLC.
47460  *
47461  * Originally Released Under LGPL - original licence link has changed is not relivant.
47462  *
47463  * Fork - LGPL
47464  * <script type="text/javascript">
47465  */
47466  
47467 /**
47468  * @class Roo.form.BasicForm
47469  * @extends Roo.util.Observable
47470  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
47471  * @constructor
47472  * @param {String/HTMLElement/Roo.Element} el The form element or its id
47473  * @param {Object} config Configuration options
47474  */
47475 Roo.form.BasicForm = function(el, config){
47476     this.allItems = [];
47477     this.childForms = [];
47478     Roo.apply(this, config);
47479     /*
47480      * The Roo.form.Field items in this form.
47481      * @type MixedCollection
47482      */
47483      
47484      
47485     this.items = new Roo.util.MixedCollection(false, function(o){
47486         return o.id || (o.id = Roo.id());
47487     });
47488     this.addEvents({
47489         /**
47490          * @event beforeaction
47491          * Fires before any action is performed. Return false to cancel the action.
47492          * @param {Form} this
47493          * @param {Action} action The action to be performed
47494          */
47495         beforeaction: true,
47496         /**
47497          * @event actionfailed
47498          * Fires when an action fails.
47499          * @param {Form} this
47500          * @param {Action} action The action that failed
47501          */
47502         actionfailed : true,
47503         /**
47504          * @event actioncomplete
47505          * Fires when an action is completed.
47506          * @param {Form} this
47507          * @param {Action} action The action that completed
47508          */
47509         actioncomplete : true
47510     });
47511     if(el){
47512         this.initEl(el);
47513     }
47514     Roo.form.BasicForm.superclass.constructor.call(this);
47515     
47516     Roo.form.BasicForm.popover.apply();
47517 };
47518
47519 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
47520     /**
47521      * @cfg {String} method
47522      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
47523      */
47524     /**
47525      * @cfg {DataReader} reader
47526      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
47527      * This is optional as there is built-in support for processing JSON.
47528      */
47529     /**
47530      * @cfg {DataReader} errorReader
47531      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
47532      * This is completely optional as there is built-in support for processing JSON.
47533      */
47534     /**
47535      * @cfg {String} url
47536      * The URL to use for form actions if one isn't supplied in the action options.
47537      */
47538     /**
47539      * @cfg {Boolean} fileUpload
47540      * Set to true if this form is a file upload.
47541      */
47542      
47543     /**
47544      * @cfg {Object} baseParams
47545      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
47546      */
47547      /**
47548      
47549     /**
47550      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
47551      */
47552     timeout: 30,
47553
47554     // private
47555     activeAction : null,
47556
47557     /**
47558      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
47559      * or setValues() data instead of when the form was first created.
47560      */
47561     trackResetOnLoad : false,
47562     
47563     
47564     /**
47565      * childForms - used for multi-tab forms
47566      * @type {Array}
47567      */
47568     childForms : false,
47569     
47570     /**
47571      * allItems - full list of fields.
47572      * @type {Array}
47573      */
47574     allItems : false,
47575     
47576     /**
47577      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
47578      * element by passing it or its id or mask the form itself by passing in true.
47579      * @type Mixed
47580      */
47581     waitMsgTarget : false,
47582     
47583     /**
47584      * @type Boolean
47585      */
47586     disableMask : false,
47587     
47588     /**
47589      * @cfg {Boolean} errorMask (true|false) default false
47590      */
47591     errorMask : false,
47592     
47593     /**
47594      * @cfg {Number} maskOffset Default 100
47595      */
47596     maskOffset : 100,
47597
47598     // private
47599     initEl : function(el){
47600         this.el = Roo.get(el);
47601         this.id = this.el.id || Roo.id();
47602         this.el.on('submit', this.onSubmit, this);
47603         this.el.addClass('x-form');
47604     },
47605
47606     // private
47607     onSubmit : function(e){
47608         e.stopEvent();
47609     },
47610
47611     /**
47612      * Returns true if client-side validation on the form is successful.
47613      * @return Boolean
47614      */
47615     isValid : function(){
47616         var valid = true;
47617         var target = false;
47618         this.items.each(function(f){
47619             if(f.validate()){
47620                 return;
47621             }
47622             
47623             valid = false;
47624                 
47625             if(!target && f.el.isVisible(true)){
47626                 target = f;
47627             }
47628         });
47629         
47630         if(this.errorMask && !valid){
47631             Roo.form.BasicForm.popover.mask(this, target);
47632         }
47633         
47634         return valid;
47635     },
47636     /**
47637      * Returns array of invalid form fields.
47638      * @return Array
47639      */
47640     
47641     invalidFields : function()
47642     {
47643         var ret = [];
47644         this.items.each(function(f){
47645             if(f.validate()){
47646                 return;
47647             }
47648             ret.push(f);
47649             
47650         });
47651         
47652         return ret;
47653     },
47654     
47655     
47656     /**
47657      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
47658      * @return Boolean
47659      */
47660     isDirty : function(){
47661         var dirty = false;
47662         this.items.each(function(f){
47663            if(f.isDirty()){
47664                dirty = true;
47665                return false;
47666            }
47667         });
47668         return dirty;
47669     },
47670     
47671     /**
47672      * Returns true if any fields in this form have changed since their original load. (New version)
47673      * @return Boolean
47674      */
47675     
47676     hasChanged : function()
47677     {
47678         var dirty = false;
47679         this.items.each(function(f){
47680            if(f.hasChanged()){
47681                dirty = true;
47682                return false;
47683            }
47684         });
47685         return dirty;
47686         
47687     },
47688     /**
47689      * Resets all hasChanged to 'false' -
47690      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
47691      * So hasChanged storage is only to be used for this purpose
47692      * @return Boolean
47693      */
47694     resetHasChanged : function()
47695     {
47696         this.items.each(function(f){
47697            f.resetHasChanged();
47698         });
47699         
47700     },
47701     
47702     
47703     /**
47704      * Performs a predefined action (submit or load) or custom actions you define on this form.
47705      * @param {String} actionName The name of the action type
47706      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
47707      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
47708      * accept other config options):
47709      * <pre>
47710 Property          Type             Description
47711 ----------------  ---------------  ----------------------------------------------------------------------------------
47712 url               String           The url for the action (defaults to the form's url)
47713 method            String           The form method to use (defaults to the form's method, or POST if not defined)
47714 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
47715 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
47716                                    validate the form on the client (defaults to false)
47717      * </pre>
47718      * @return {BasicForm} this
47719      */
47720     doAction : function(action, options){
47721         if(typeof action == 'string'){
47722             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
47723         }
47724         if(this.fireEvent('beforeaction', this, action) !== false){
47725             this.beforeAction(action);
47726             action.run.defer(100, action);
47727         }
47728         return this;
47729     },
47730
47731     /**
47732      * Shortcut to do a submit action.
47733      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
47734      * @return {BasicForm} this
47735      */
47736     submit : function(options){
47737         this.doAction('submit', options);
47738         return this;
47739     },
47740
47741     /**
47742      * Shortcut to do a load action.
47743      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
47744      * @return {BasicForm} this
47745      */
47746     load : function(options){
47747         this.doAction('load', options);
47748         return this;
47749     },
47750
47751     /**
47752      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
47753      * @param {Record} record The record to edit
47754      * @return {BasicForm} this
47755      */
47756     updateRecord : function(record){
47757         record.beginEdit();
47758         var fs = record.fields;
47759         fs.each(function(f){
47760             var field = this.findField(f.name);
47761             if(field){
47762                 record.set(f.name, field.getValue());
47763             }
47764         }, this);
47765         record.endEdit();
47766         return this;
47767     },
47768
47769     /**
47770      * Loads an Roo.data.Record into this form.
47771      * @param {Record} record The record to load
47772      * @return {BasicForm} this
47773      */
47774     loadRecord : function(record){
47775         this.setValues(record.data);
47776         return this;
47777     },
47778
47779     // private
47780     beforeAction : function(action){
47781         var o = action.options;
47782         
47783         if(!this.disableMask) {
47784             if(this.waitMsgTarget === true){
47785                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
47786             }else if(this.waitMsgTarget){
47787                 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
47788                 this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
47789             }else {
47790                 Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
47791             }
47792         }
47793         
47794          
47795     },
47796
47797     // private
47798     afterAction : function(action, success){
47799         this.activeAction = null;
47800         var o = action.options;
47801         
47802         if(!this.disableMask) {
47803             if(this.waitMsgTarget === true){
47804                 this.el.unmask();
47805             }else if(this.waitMsgTarget){
47806                 this.waitMsgTarget.unmask();
47807             }else{
47808                 Roo.MessageBox.updateProgress(1);
47809                 Roo.MessageBox.hide();
47810             }
47811         }
47812         
47813         if(success){
47814             if(o.reset){
47815                 this.reset();
47816             }
47817             Roo.callback(o.success, o.scope, [this, action]);
47818             this.fireEvent('actioncomplete', this, action);
47819             
47820         }else{
47821             
47822             // failure condition..
47823             // we have a scenario where updates need confirming.
47824             // eg. if a locking scenario exists..
47825             // we look for { errors : { needs_confirm : true }} in the response.
47826             if (
47827                 (typeof(action.result) != 'undefined')  &&
47828                 (typeof(action.result.errors) != 'undefined')  &&
47829                 (typeof(action.result.errors.needs_confirm) != 'undefined')
47830            ){
47831                 var _t = this;
47832                 Roo.MessageBox.confirm(
47833                     "Change requires confirmation",
47834                     action.result.errorMsg,
47835                     function(r) {
47836                         if (r != 'yes') {
47837                             return;
47838                         }
47839                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
47840                     }
47841                     
47842                 );
47843                 
47844                 
47845                 
47846                 return;
47847             }
47848             
47849             Roo.callback(o.failure, o.scope, [this, action]);
47850             // show an error message if no failed handler is set..
47851             if (!this.hasListener('actionfailed')) {
47852                 Roo.MessageBox.alert("Error",
47853                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
47854                         action.result.errorMsg :
47855                         "Saving Failed, please check your entries or try again"
47856                 );
47857             }
47858             
47859             this.fireEvent('actionfailed', this, action);
47860         }
47861         
47862     },
47863
47864     /**
47865      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
47866      * @param {String} id The value to search for
47867      * @return Field
47868      */
47869     findField : function(id){
47870         var field = this.items.get(id);
47871         if(!field){
47872             this.items.each(function(f){
47873                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
47874                     field = f;
47875                     return false;
47876                 }
47877             });
47878         }
47879         return field || null;
47880     },
47881
47882     /**
47883      * Add a secondary form to this one, 
47884      * Used to provide tabbed forms. One form is primary, with hidden values 
47885      * which mirror the elements from the other forms.
47886      * 
47887      * @param {Roo.form.Form} form to add.
47888      * 
47889      */
47890     addForm : function(form)
47891     {
47892        
47893         if (this.childForms.indexOf(form) > -1) {
47894             // already added..
47895             return;
47896         }
47897         this.childForms.push(form);
47898         var n = '';
47899         Roo.each(form.allItems, function (fe) {
47900             
47901             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
47902             if (this.findField(n)) { // already added..
47903                 return;
47904             }
47905             var add = new Roo.form.Hidden({
47906                 name : n
47907             });
47908             add.render(this.el);
47909             
47910             this.add( add );
47911         }, this);
47912         
47913     },
47914     /**
47915      * Mark fields in this form invalid in bulk.
47916      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
47917      * @return {BasicForm} this
47918      */
47919     markInvalid : function(errors){
47920         if(errors instanceof Array){
47921             for(var i = 0, len = errors.length; i < len; i++){
47922                 var fieldError = errors[i];
47923                 var f = this.findField(fieldError.id);
47924                 if(f){
47925                     f.markInvalid(fieldError.msg);
47926                 }
47927             }
47928         }else{
47929             var field, id;
47930             for(id in errors){
47931                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
47932                     field.markInvalid(errors[id]);
47933                 }
47934             }
47935         }
47936         Roo.each(this.childForms || [], function (f) {
47937             f.markInvalid(errors);
47938         });
47939         
47940         return this;
47941     },
47942
47943     /**
47944      * Set values for fields in this form in bulk.
47945      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
47946      * @return {BasicForm} this
47947      */
47948     setValues : function(values){
47949         if(values instanceof Array){ // array of objects
47950             for(var i = 0, len = values.length; i < len; i++){
47951                 var v = values[i];
47952                 var f = this.findField(v.id);
47953                 if(f){
47954                     f.setValue(v.value);
47955                     if(this.trackResetOnLoad){
47956                         f.originalValue = f.getValue();
47957                     }
47958                 }
47959             }
47960         }else{ // object hash
47961             var field, id;
47962             for(id in values){
47963                 if(typeof values[id] != 'function' && (field = this.findField(id))){
47964                     
47965                     if (field.setFromData && 
47966                         field.valueField && 
47967                         field.displayField &&
47968                         // combos' with local stores can 
47969                         // be queried via setValue()
47970                         // to set their value..
47971                         (field.store && !field.store.isLocal)
47972                         ) {
47973                         // it's a combo
47974                         var sd = { };
47975                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
47976                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
47977                         field.setFromData(sd);
47978                         
47979                     } else {
47980                         field.setValue(values[id]);
47981                     }
47982                     
47983                     
47984                     if(this.trackResetOnLoad){
47985                         field.originalValue = field.getValue();
47986                     }
47987                 }
47988             }
47989         }
47990         this.resetHasChanged();
47991         
47992         
47993         Roo.each(this.childForms || [], function (f) {
47994             f.setValues(values);
47995             f.resetHasChanged();
47996         });
47997                 
47998         return this;
47999     },
48000  
48001     /**
48002      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
48003      * they are returned as an array.
48004      * @param {Boolean} asString
48005      * @return {Object}
48006      */
48007     getValues : function(asString){
48008         if (this.childForms) {
48009             // copy values from the child forms
48010             Roo.each(this.childForms, function (f) {
48011                 this.setValues(f.getValues());
48012             }, this);
48013         }
48014         
48015         // use formdata
48016         if (typeof(FormData) != 'undefined' && asString !== true) {
48017             // this relies on a 'recent' version of chrome apparently...
48018             try {
48019                 var fd = (new FormData(this.el.dom)).entries();
48020                 var ret = {};
48021                 var ent = fd.next();
48022                 while (!ent.done) {
48023                     ret[ent.value[0]] = ent.value[1]; // not sure how this will handle duplicates..
48024                     ent = fd.next();
48025                 };
48026                 return ret;
48027             } catch(e) {
48028                 
48029             }
48030             
48031         }
48032         
48033         
48034         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
48035         if(asString === true){
48036             return fs;
48037         }
48038         return Roo.urlDecode(fs);
48039     },
48040     
48041     /**
48042      * Returns the fields in this form as an object with key/value pairs. 
48043      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
48044      * @return {Object}
48045      */
48046     getFieldValues : function(with_hidden)
48047     {
48048         if (this.childForms) {
48049             // copy values from the child forms
48050             // should this call getFieldValues - probably not as we do not currently copy
48051             // hidden fields when we generate..
48052             Roo.each(this.childForms, function (f) {
48053                 this.setValues(f.getValues());
48054             }, this);
48055         }
48056         
48057         var ret = {};
48058         this.items.each(function(f){
48059             if (!f.getName()) {
48060                 return;
48061             }
48062             var v = f.getValue();
48063             if (f.inputType =='radio') {
48064                 if (typeof(ret[f.getName()]) == 'undefined') {
48065                     ret[f.getName()] = ''; // empty..
48066                 }
48067                 
48068                 if (!f.el.dom.checked) {
48069                     return;
48070                     
48071                 }
48072                 v = f.el.dom.value;
48073                 
48074             }
48075             
48076             // not sure if this supported any more..
48077             if ((typeof(v) == 'object') && f.getRawValue) {
48078                 v = f.getRawValue() ; // dates..
48079             }
48080             // combo boxes where name != hiddenName...
48081             if (f.name != f.getName()) {
48082                 ret[f.name] = f.getRawValue();
48083             }
48084             ret[f.getName()] = v;
48085         });
48086         
48087         return ret;
48088     },
48089
48090     /**
48091      * Clears all invalid messages in this form.
48092      * @return {BasicForm} this
48093      */
48094     clearInvalid : function(){
48095         this.items.each(function(f){
48096            f.clearInvalid();
48097         });
48098         
48099         Roo.each(this.childForms || [], function (f) {
48100             f.clearInvalid();
48101         });
48102         
48103         
48104         return this;
48105     },
48106
48107     /**
48108      * Resets this form.
48109      * @return {BasicForm} this
48110      */
48111     reset : function(){
48112         this.items.each(function(f){
48113             f.reset();
48114         });
48115         
48116         Roo.each(this.childForms || [], function (f) {
48117             f.reset();
48118         });
48119         this.resetHasChanged();
48120         
48121         return this;
48122     },
48123
48124     /**
48125      * Add Roo.form components to this form.
48126      * @param {Field} field1
48127      * @param {Field} field2 (optional)
48128      * @param {Field} etc (optional)
48129      * @return {BasicForm} this
48130      */
48131     add : function(){
48132         this.items.addAll(Array.prototype.slice.call(arguments, 0));
48133         return this;
48134     },
48135
48136
48137     /**
48138      * Removes a field from the items collection (does NOT remove its markup).
48139      * @param {Field} field
48140      * @return {BasicForm} this
48141      */
48142     remove : function(field){
48143         this.items.remove(field);
48144         return this;
48145     },
48146
48147     /**
48148      * Looks at the fields in this form, checks them for an id attribute,
48149      * and calls applyTo on the existing dom element with that id.
48150      * @return {BasicForm} this
48151      */
48152     render : function(){
48153         this.items.each(function(f){
48154             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
48155                 f.applyTo(f.id);
48156             }
48157         });
48158         return this;
48159     },
48160
48161     /**
48162      * Calls {@link Ext#apply} for all fields in this form with the passed object.
48163      * @param {Object} values
48164      * @return {BasicForm} this
48165      */
48166     applyToFields : function(o){
48167         this.items.each(function(f){
48168            Roo.apply(f, o);
48169         });
48170         return this;
48171     },
48172
48173     /**
48174      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
48175      * @param {Object} values
48176      * @return {BasicForm} this
48177      */
48178     applyIfToFields : function(o){
48179         this.items.each(function(f){
48180            Roo.applyIf(f, o);
48181         });
48182         return this;
48183     }
48184 });
48185
48186 // back compat
48187 Roo.BasicForm = Roo.form.BasicForm;
48188
48189 Roo.apply(Roo.form.BasicForm, {
48190     
48191     popover : {
48192         
48193         padding : 5,
48194         
48195         isApplied : false,
48196         
48197         isMasked : false,
48198         
48199         form : false,
48200         
48201         target : false,
48202         
48203         intervalID : false,
48204         
48205         maskEl : false,
48206         
48207         apply : function()
48208         {
48209             if(this.isApplied){
48210                 return;
48211             }
48212             
48213             this.maskEl = {
48214                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
48215                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
48216                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
48217                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
48218             };
48219             
48220             this.maskEl.top.enableDisplayMode("block");
48221             this.maskEl.left.enableDisplayMode("block");
48222             this.maskEl.bottom.enableDisplayMode("block");
48223             this.maskEl.right.enableDisplayMode("block");
48224             
48225             Roo.get(document.body).on('click', function(){
48226                 this.unmask();
48227             }, this);
48228             
48229             Roo.get(document.body).on('touchstart', function(){
48230                 this.unmask();
48231             }, this);
48232             
48233             this.isApplied = true
48234         },
48235         
48236         mask : function(form, target)
48237         {
48238             this.form = form;
48239             
48240             this.target = target;
48241             
48242             if(!this.form.errorMask || !target.el){
48243                 return;
48244             }
48245             
48246             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.x-layout-active-content', 100, true) || Roo.get(document.body);
48247             
48248             var ot = this.target.el.calcOffsetsTo(scrollable);
48249             
48250             var scrollTo = ot[1] - this.form.maskOffset;
48251             
48252             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
48253             
48254             scrollable.scrollTo('top', scrollTo);
48255             
48256             var el = this.target.wrap || this.target.el;
48257             
48258             var box = el.getBox();
48259             
48260             this.maskEl.top.setStyle('position', 'absolute');
48261             this.maskEl.top.setStyle('z-index', 10000);
48262             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
48263             this.maskEl.top.setLeft(0);
48264             this.maskEl.top.setTop(0);
48265             this.maskEl.top.show();
48266             
48267             this.maskEl.left.setStyle('position', 'absolute');
48268             this.maskEl.left.setStyle('z-index', 10000);
48269             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
48270             this.maskEl.left.setLeft(0);
48271             this.maskEl.left.setTop(box.y - this.padding);
48272             this.maskEl.left.show();
48273
48274             this.maskEl.bottom.setStyle('position', 'absolute');
48275             this.maskEl.bottom.setStyle('z-index', 10000);
48276             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
48277             this.maskEl.bottom.setLeft(0);
48278             this.maskEl.bottom.setTop(box.bottom + this.padding);
48279             this.maskEl.bottom.show();
48280
48281             this.maskEl.right.setStyle('position', 'absolute');
48282             this.maskEl.right.setStyle('z-index', 10000);
48283             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
48284             this.maskEl.right.setLeft(box.right + this.padding);
48285             this.maskEl.right.setTop(box.y - this.padding);
48286             this.maskEl.right.show();
48287
48288             this.intervalID = window.setInterval(function() {
48289                 Roo.form.BasicForm.popover.unmask();
48290             }, 10000);
48291
48292             window.onwheel = function(){ return false;};
48293             
48294             (function(){ this.isMasked = true; }).defer(500, this);
48295             
48296         },
48297         
48298         unmask : function()
48299         {
48300             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
48301                 return;
48302             }
48303             
48304             this.maskEl.top.setStyle('position', 'absolute');
48305             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
48306             this.maskEl.top.hide();
48307
48308             this.maskEl.left.setStyle('position', 'absolute');
48309             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
48310             this.maskEl.left.hide();
48311
48312             this.maskEl.bottom.setStyle('position', 'absolute');
48313             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
48314             this.maskEl.bottom.hide();
48315
48316             this.maskEl.right.setStyle('position', 'absolute');
48317             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
48318             this.maskEl.right.hide();
48319             
48320             window.onwheel = function(){ return true;};
48321             
48322             if(this.intervalID){
48323                 window.clearInterval(this.intervalID);
48324                 this.intervalID = false;
48325             }
48326             
48327             this.isMasked = false;
48328             
48329         }
48330         
48331     }
48332     
48333 });/*
48334  * Based on:
48335  * Ext JS Library 1.1.1
48336  * Copyright(c) 2006-2007, Ext JS, LLC.
48337  *
48338  * Originally Released Under LGPL - original licence link has changed is not relivant.
48339  *
48340  * Fork - LGPL
48341  * <script type="text/javascript">
48342  */
48343
48344 /**
48345  * @class Roo.form.Form
48346  * @extends Roo.form.BasicForm
48347  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
48348  * @constructor
48349  * @param {Object} config Configuration options
48350  */
48351 Roo.form.Form = function(config){
48352     var xitems =  [];
48353     if (config.items) {
48354         xitems = config.items;
48355         delete config.items;
48356     }
48357    
48358     
48359     Roo.form.Form.superclass.constructor.call(this, null, config);
48360     this.url = this.url || this.action;
48361     if(!this.root){
48362         this.root = new Roo.form.Layout(Roo.applyIf({
48363             id: Roo.id()
48364         }, config));
48365     }
48366     this.active = this.root;
48367     /**
48368      * Array of all the buttons that have been added to this form via {@link addButton}
48369      * @type Array
48370      */
48371     this.buttons = [];
48372     this.allItems = [];
48373     this.addEvents({
48374         /**
48375          * @event clientvalidation
48376          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
48377          * @param {Form} this
48378          * @param {Boolean} valid true if the form has passed client-side validation
48379          */
48380         clientvalidation: true,
48381         /**
48382          * @event rendered
48383          * Fires when the form is rendered
48384          * @param {Roo.form.Form} form
48385          */
48386         rendered : true
48387     });
48388     
48389     if (this.progressUrl) {
48390             // push a hidden field onto the list of fields..
48391             this.addxtype( {
48392                     xns: Roo.form, 
48393                     xtype : 'Hidden', 
48394                     name : 'UPLOAD_IDENTIFIER' 
48395             });
48396         }
48397         
48398     
48399     Roo.each(xitems, this.addxtype, this);
48400     
48401 };
48402
48403 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
48404     /**
48405      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
48406      */
48407     /**
48408      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
48409      */
48410     /**
48411      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
48412      */
48413     buttonAlign:'center',
48414
48415     /**
48416      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
48417      */
48418     minButtonWidth:75,
48419
48420     /**
48421      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
48422      * This property cascades to child containers if not set.
48423      */
48424     labelAlign:'left',
48425
48426     /**
48427      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
48428      * fires a looping event with that state. This is required to bind buttons to the valid
48429      * state using the config value formBind:true on the button.
48430      */
48431     monitorValid : false,
48432
48433     /**
48434      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
48435      */
48436     monitorPoll : 200,
48437     
48438     /**
48439      * @cfg {String} progressUrl - Url to return progress data 
48440      */
48441     
48442     progressUrl : false,
48443     /**
48444      * @cfg {boolean|FormData} formData - true to use new 'FormData' post, or set to a new FormData({dom form}) Object, if
48445      * sending a formdata with extra parameters - eg uploaded elements.
48446      */
48447     
48448     formData : false,
48449     
48450     /**
48451      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
48452      * fields are added and the column is closed. If no fields are passed the column remains open
48453      * until end() is called.
48454      * @param {Object} config The config to pass to the column
48455      * @param {Field} field1 (optional)
48456      * @param {Field} field2 (optional)
48457      * @param {Field} etc (optional)
48458      * @return Column The column container object
48459      */
48460     column : function(c){
48461         var col = new Roo.form.Column(c);
48462         this.start(col);
48463         if(arguments.length > 1){ // duplicate code required because of Opera
48464             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
48465             this.end();
48466         }
48467         return col;
48468     },
48469
48470     /**
48471      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
48472      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
48473      * until end() is called.
48474      * @param {Object} config The config to pass to the fieldset
48475      * @param {Field} field1 (optional)
48476      * @param {Field} field2 (optional)
48477      * @param {Field} etc (optional)
48478      * @return FieldSet The fieldset container object
48479      */
48480     fieldset : function(c){
48481         var fs = new Roo.form.FieldSet(c);
48482         this.start(fs);
48483         if(arguments.length > 1){ // duplicate code required because of Opera
48484             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
48485             this.end();
48486         }
48487         return fs;
48488     },
48489
48490     /**
48491      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
48492      * fields are added and the container is closed. If no fields are passed the container remains open
48493      * until end() is called.
48494      * @param {Object} config The config to pass to the Layout
48495      * @param {Field} field1 (optional)
48496      * @param {Field} field2 (optional)
48497      * @param {Field} etc (optional)
48498      * @return Layout The container object
48499      */
48500     container : function(c){
48501         var l = new Roo.form.Layout(c);
48502         this.start(l);
48503         if(arguments.length > 1){ // duplicate code required because of Opera
48504             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
48505             this.end();
48506         }
48507         return l;
48508     },
48509
48510     /**
48511      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
48512      * @param {Object} container A Roo.form.Layout or subclass of Layout
48513      * @return {Form} this
48514      */
48515     start : function(c){
48516         // cascade label info
48517         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
48518         this.active.stack.push(c);
48519         c.ownerCt = this.active;
48520         this.active = c;
48521         return this;
48522     },
48523
48524     /**
48525      * Closes the current open container
48526      * @return {Form} this
48527      */
48528     end : function(){
48529         if(this.active == this.root){
48530             return this;
48531         }
48532         this.active = this.active.ownerCt;
48533         return this;
48534     },
48535
48536     /**
48537      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
48538      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
48539      * as the label of the field.
48540      * @param {Field} field1
48541      * @param {Field} field2 (optional)
48542      * @param {Field} etc. (optional)
48543      * @return {Form} this
48544      */
48545     add : function(){
48546         this.active.stack.push.apply(this.active.stack, arguments);
48547         this.allItems.push.apply(this.allItems,arguments);
48548         var r = [];
48549         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
48550             if(a[i].isFormField){
48551                 r.push(a[i]);
48552             }
48553         }
48554         if(r.length > 0){
48555             Roo.form.Form.superclass.add.apply(this, r);
48556         }
48557         return this;
48558     },
48559     
48560
48561     
48562     
48563     
48564      /**
48565      * Find any element that has been added to a form, using it's ID or name
48566      * This can include framesets, columns etc. along with regular fields..
48567      * @param {String} id - id or name to find.
48568      
48569      * @return {Element} e - or false if nothing found.
48570      */
48571     findbyId : function(id)
48572     {
48573         var ret = false;
48574         if (!id) {
48575             return ret;
48576         }
48577         Roo.each(this.allItems, function(f){
48578             if (f.id == id || f.name == id ){
48579                 ret = f;
48580                 return false;
48581             }
48582         });
48583         return ret;
48584     },
48585
48586     
48587     
48588     /**
48589      * Render this form into the passed container. This should only be called once!
48590      * @param {String/HTMLElement/Element} container The element this component should be rendered into
48591      * @return {Form} this
48592      */
48593     render : function(ct)
48594     {
48595         
48596         
48597         
48598         ct = Roo.get(ct);
48599         var o = this.autoCreate || {
48600             tag: 'form',
48601             method : this.method || 'POST',
48602             id : this.id || Roo.id()
48603         };
48604         this.initEl(ct.createChild(o));
48605
48606         this.root.render(this.el);
48607         
48608        
48609              
48610         this.items.each(function(f){
48611             f.render('x-form-el-'+f.id);
48612         });
48613
48614         if(this.buttons.length > 0){
48615             // tables are required to maintain order and for correct IE layout
48616             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
48617                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
48618                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
48619             }}, null, true);
48620             var tr = tb.getElementsByTagName('tr')[0];
48621             for(var i = 0, len = this.buttons.length; i < len; i++) {
48622                 var b = this.buttons[i];
48623                 var td = document.createElement('td');
48624                 td.className = 'x-form-btn-td';
48625                 b.render(tr.appendChild(td));
48626             }
48627         }
48628         if(this.monitorValid){ // initialize after render
48629             this.startMonitoring();
48630         }
48631         this.fireEvent('rendered', this);
48632         return this;
48633     },
48634
48635     /**
48636      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
48637      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
48638      * object or a valid Roo.DomHelper element config
48639      * @param {Function} handler The function called when the button is clicked
48640      * @param {Object} scope (optional) The scope of the handler function
48641      * @return {Roo.Button}
48642      */
48643     addButton : function(config, handler, scope){
48644         var bc = {
48645             handler: handler,
48646             scope: scope,
48647             minWidth: this.minButtonWidth,
48648             hideParent:true
48649         };
48650         if(typeof config == "string"){
48651             bc.text = config;
48652         }else{
48653             Roo.apply(bc, config);
48654         }
48655         var btn = new Roo.Button(null, bc);
48656         this.buttons.push(btn);
48657         return btn;
48658     },
48659
48660      /**
48661      * Adds a series of form elements (using the xtype property as the factory method.
48662      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
48663      * @param {Object} config 
48664      */
48665     
48666     addxtype : function()
48667     {
48668         var ar = Array.prototype.slice.call(arguments, 0);
48669         var ret = false;
48670         for(var i = 0; i < ar.length; i++) {
48671             if (!ar[i]) {
48672                 continue; // skip -- if this happends something invalid got sent, we 
48673                 // should ignore it, as basically that interface element will not show up
48674                 // and that should be pretty obvious!!
48675             }
48676             
48677             if (Roo.form[ar[i].xtype]) {
48678                 ar[i].form = this;
48679                 var fe = Roo.factory(ar[i], Roo.form);
48680                 if (!ret) {
48681                     ret = fe;
48682                 }
48683                 fe.form = this;
48684                 if (fe.store) {
48685                     fe.store.form = this;
48686                 }
48687                 if (fe.isLayout) {  
48688                          
48689                     this.start(fe);
48690                     this.allItems.push(fe);
48691                     if (fe.items && fe.addxtype) {
48692                         fe.addxtype.apply(fe, fe.items);
48693                         delete fe.items;
48694                     }
48695                      this.end();
48696                     continue;
48697                 }
48698                 
48699                 
48700                  
48701                 this.add(fe);
48702               //  console.log('adding ' + ar[i].xtype);
48703             }
48704             if (ar[i].xtype == 'Button') {  
48705                 //console.log('adding button');
48706                 //console.log(ar[i]);
48707                 this.addButton(ar[i]);
48708                 this.allItems.push(fe);
48709                 continue;
48710             }
48711             
48712             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
48713                 alert('end is not supported on xtype any more, use items');
48714             //    this.end();
48715             //    //console.log('adding end');
48716             }
48717             
48718         }
48719         return ret;
48720     },
48721     
48722     /**
48723      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
48724      * option "monitorValid"
48725      */
48726     startMonitoring : function(){
48727         if(!this.bound){
48728             this.bound = true;
48729             Roo.TaskMgr.start({
48730                 run : this.bindHandler,
48731                 interval : this.monitorPoll || 200,
48732                 scope: this
48733             });
48734         }
48735     },
48736
48737     /**
48738      * Stops monitoring of the valid state of this form
48739      */
48740     stopMonitoring : function(){
48741         this.bound = false;
48742     },
48743
48744     // private
48745     bindHandler : function(){
48746         if(!this.bound){
48747             return false; // stops binding
48748         }
48749         var valid = true;
48750         this.items.each(function(f){
48751             if(!f.isValid(true)){
48752                 valid = false;
48753                 return false;
48754             }
48755         });
48756         for(var i = 0, len = this.buttons.length; i < len; i++){
48757             var btn = this.buttons[i];
48758             if(btn.formBind === true && btn.disabled === valid){
48759                 btn.setDisabled(!valid);
48760             }
48761         }
48762         this.fireEvent('clientvalidation', this, valid);
48763     }
48764     
48765     
48766     
48767     
48768     
48769     
48770     
48771     
48772 });
48773
48774
48775 // back compat
48776 Roo.Form = Roo.form.Form;
48777 /*
48778  * Based on:
48779  * Ext JS Library 1.1.1
48780  * Copyright(c) 2006-2007, Ext JS, LLC.
48781  *
48782  * Originally Released Under LGPL - original licence link has changed is not relivant.
48783  *
48784  * Fork - LGPL
48785  * <script type="text/javascript">
48786  */
48787
48788 // as we use this in bootstrap.
48789 Roo.namespace('Roo.form');
48790  /**
48791  * @class Roo.form.Action
48792  * Internal Class used to handle form actions
48793  * @constructor
48794  * @param {Roo.form.BasicForm} el The form element or its id
48795  * @param {Object} config Configuration options
48796  */
48797
48798  
48799  
48800 // define the action interface
48801 Roo.form.Action = function(form, options){
48802     this.form = form;
48803     this.options = options || {};
48804 };
48805 /**
48806  * Client Validation Failed
48807  * @const 
48808  */
48809 Roo.form.Action.CLIENT_INVALID = 'client';
48810 /**
48811  * Server Validation Failed
48812  * @const 
48813  */
48814 Roo.form.Action.SERVER_INVALID = 'server';
48815  /**
48816  * Connect to Server Failed
48817  * @const 
48818  */
48819 Roo.form.Action.CONNECT_FAILURE = 'connect';
48820 /**
48821  * Reading Data from Server Failed
48822  * @const 
48823  */
48824 Roo.form.Action.LOAD_FAILURE = 'load';
48825
48826 Roo.form.Action.prototype = {
48827     type : 'default',
48828     failureType : undefined,
48829     response : undefined,
48830     result : undefined,
48831
48832     // interface method
48833     run : function(options){
48834
48835     },
48836
48837     // interface method
48838     success : function(response){
48839
48840     },
48841
48842     // interface method
48843     handleResponse : function(response){
48844
48845     },
48846
48847     // default connection failure
48848     failure : function(response){
48849         
48850         this.response = response;
48851         this.failureType = Roo.form.Action.CONNECT_FAILURE;
48852         this.form.afterAction(this, false);
48853     },
48854
48855     processResponse : function(response){
48856         this.response = response;
48857         if(!response.responseText){
48858             return true;
48859         }
48860         this.result = this.handleResponse(response);
48861         return this.result;
48862     },
48863
48864     // utility functions used internally
48865     getUrl : function(appendParams){
48866         var url = this.options.url || this.form.url || this.form.el.dom.action;
48867         if(appendParams){
48868             var p = this.getParams();
48869             if(p){
48870                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
48871             }
48872         }
48873         return url;
48874     },
48875
48876     getMethod : function(){
48877         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
48878     },
48879
48880     getParams : function(){
48881         var bp = this.form.baseParams;
48882         var p = this.options.params;
48883         if(p){
48884             if(typeof p == "object"){
48885                 p = Roo.urlEncode(Roo.applyIf(p, bp));
48886             }else if(typeof p == 'string' && bp){
48887                 p += '&' + Roo.urlEncode(bp);
48888             }
48889         }else if(bp){
48890             p = Roo.urlEncode(bp);
48891         }
48892         return p;
48893     },
48894
48895     createCallback : function(){
48896         return {
48897             success: this.success,
48898             failure: this.failure,
48899             scope: this,
48900             timeout: (this.form.timeout*1000),
48901             upload: this.form.fileUpload ? this.success : undefined
48902         };
48903     }
48904 };
48905
48906 Roo.form.Action.Submit = function(form, options){
48907     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
48908 };
48909
48910 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
48911     type : 'submit',
48912
48913     haveProgress : false,
48914     uploadComplete : false,
48915     
48916     // uploadProgress indicator.
48917     uploadProgress : function()
48918     {
48919         if (!this.form.progressUrl) {
48920             return;
48921         }
48922         
48923         if (!this.haveProgress) {
48924             Roo.MessageBox.progress("Uploading", "Uploading");
48925         }
48926         if (this.uploadComplete) {
48927            Roo.MessageBox.hide();
48928            return;
48929         }
48930         
48931         this.haveProgress = true;
48932    
48933         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
48934         
48935         var c = new Roo.data.Connection();
48936         c.request({
48937             url : this.form.progressUrl,
48938             params: {
48939                 id : uid
48940             },
48941             method: 'GET',
48942             success : function(req){
48943                //console.log(data);
48944                 var rdata = false;
48945                 var edata;
48946                 try  {
48947                    rdata = Roo.decode(req.responseText)
48948                 } catch (e) {
48949                     Roo.log("Invalid data from server..");
48950                     Roo.log(edata);
48951                     return;
48952                 }
48953                 if (!rdata || !rdata.success) {
48954                     Roo.log(rdata);
48955                     Roo.MessageBox.alert(Roo.encode(rdata));
48956                     return;
48957                 }
48958                 var data = rdata.data;
48959                 
48960                 if (this.uploadComplete) {
48961                    Roo.MessageBox.hide();
48962                    return;
48963                 }
48964                    
48965                 if (data){
48966                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
48967                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
48968                     );
48969                 }
48970                 this.uploadProgress.defer(2000,this);
48971             },
48972        
48973             failure: function(data) {
48974                 Roo.log('progress url failed ');
48975                 Roo.log(data);
48976             },
48977             scope : this
48978         });
48979            
48980     },
48981     
48982     
48983     run : function()
48984     {
48985         // run get Values on the form, so it syncs any secondary forms.
48986         this.form.getValues();
48987         
48988         var o = this.options;
48989         var method = this.getMethod();
48990         var isPost = method == 'POST';
48991         if(o.clientValidation === false || this.form.isValid()){
48992             
48993             if (this.form.progressUrl) {
48994                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
48995                     (new Date() * 1) + '' + Math.random());
48996                     
48997             } 
48998             
48999             
49000             Roo.Ajax.request(Roo.apply(this.createCallback(), {
49001                 form:this.form.el.dom,
49002                 url:this.getUrl(!isPost),
49003                 method: method,
49004                 params:isPost ? this.getParams() : null,
49005                 isUpload: this.form.fileUpload,
49006                 formData : this.form.formData
49007             }));
49008             
49009             this.uploadProgress();
49010
49011         }else if (o.clientValidation !== false){ // client validation failed
49012             this.failureType = Roo.form.Action.CLIENT_INVALID;
49013             this.form.afterAction(this, false);
49014         }
49015     },
49016
49017     success : function(response)
49018     {
49019         this.uploadComplete= true;
49020         if (this.haveProgress) {
49021             Roo.MessageBox.hide();
49022         }
49023         
49024         
49025         var result = this.processResponse(response);
49026         if(result === true || result.success){
49027             this.form.afterAction(this, true);
49028             return;
49029         }
49030         if(result.errors){
49031             this.form.markInvalid(result.errors);
49032             this.failureType = Roo.form.Action.SERVER_INVALID;
49033         }
49034         this.form.afterAction(this, false);
49035     },
49036     failure : function(response)
49037     {
49038         this.uploadComplete= true;
49039         if (this.haveProgress) {
49040             Roo.MessageBox.hide();
49041         }
49042         
49043         this.response = response;
49044         this.failureType = Roo.form.Action.CONNECT_FAILURE;
49045         this.form.afterAction(this, false);
49046     },
49047     
49048     handleResponse : function(response){
49049         if(this.form.errorReader){
49050             var rs = this.form.errorReader.read(response);
49051             var errors = [];
49052             if(rs.records){
49053                 for(var i = 0, len = rs.records.length; i < len; i++) {
49054                     var r = rs.records[i];
49055                     errors[i] = r.data;
49056                 }
49057             }
49058             if(errors.length < 1){
49059                 errors = null;
49060             }
49061             return {
49062                 success : rs.success,
49063                 errors : errors
49064             };
49065         }
49066         var ret = false;
49067         try {
49068             ret = Roo.decode(response.responseText);
49069         } catch (e) {
49070             ret = {
49071                 success: false,
49072                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
49073                 errors : []
49074             };
49075         }
49076         return ret;
49077         
49078     }
49079 });
49080
49081
49082 Roo.form.Action.Load = function(form, options){
49083     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
49084     this.reader = this.form.reader;
49085 };
49086
49087 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
49088     type : 'load',
49089
49090     run : function(){
49091         
49092         Roo.Ajax.request(Roo.apply(
49093                 this.createCallback(), {
49094                     method:this.getMethod(),
49095                     url:this.getUrl(false),
49096                     params:this.getParams()
49097         }));
49098     },
49099
49100     success : function(response){
49101         
49102         var result = this.processResponse(response);
49103         if(result === true || !result.success || !result.data){
49104             this.failureType = Roo.form.Action.LOAD_FAILURE;
49105             this.form.afterAction(this, false);
49106             return;
49107         }
49108         this.form.clearInvalid();
49109         this.form.setValues(result.data);
49110         this.form.afterAction(this, true);
49111     },
49112
49113     handleResponse : function(response){
49114         if(this.form.reader){
49115             var rs = this.form.reader.read(response);
49116             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
49117             return {
49118                 success : rs.success,
49119                 data : data
49120             };
49121         }
49122         return Roo.decode(response.responseText);
49123     }
49124 });
49125
49126 Roo.form.Action.ACTION_TYPES = {
49127     'load' : Roo.form.Action.Load,
49128     'submit' : Roo.form.Action.Submit
49129 };/*
49130  * Based on:
49131  * Ext JS Library 1.1.1
49132  * Copyright(c) 2006-2007, Ext JS, LLC.
49133  *
49134  * Originally Released Under LGPL - original licence link has changed is not relivant.
49135  *
49136  * Fork - LGPL
49137  * <script type="text/javascript">
49138  */
49139  
49140 /**
49141  * @class Roo.form.Layout
49142  * @extends Roo.Component
49143  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
49144  * @constructor
49145  * @param {Object} config Configuration options
49146  */
49147 Roo.form.Layout = function(config){
49148     var xitems = [];
49149     if (config.items) {
49150         xitems = config.items;
49151         delete config.items;
49152     }
49153     Roo.form.Layout.superclass.constructor.call(this, config);
49154     this.stack = [];
49155     Roo.each(xitems, this.addxtype, this);
49156      
49157 };
49158
49159 Roo.extend(Roo.form.Layout, Roo.Component, {
49160     /**
49161      * @cfg {String/Object} autoCreate
49162      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
49163      */
49164     /**
49165      * @cfg {String/Object/Function} style
49166      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
49167      * a function which returns such a specification.
49168      */
49169     /**
49170      * @cfg {String} labelAlign
49171      * Valid values are "left," "top" and "right" (defaults to "left")
49172      */
49173     /**
49174      * @cfg {Number} labelWidth
49175      * Fixed width in pixels of all field labels (defaults to undefined)
49176      */
49177     /**
49178      * @cfg {Boolean} clear
49179      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
49180      */
49181     clear : true,
49182     /**
49183      * @cfg {String} labelSeparator
49184      * The separator to use after field labels (defaults to ':')
49185      */
49186     labelSeparator : ':',
49187     /**
49188      * @cfg {Boolean} hideLabels
49189      * True to suppress the display of field labels in this layout (defaults to false)
49190      */
49191     hideLabels : false,
49192
49193     // private
49194     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
49195     
49196     isLayout : true,
49197     
49198     // private
49199     onRender : function(ct, position){
49200         if(this.el){ // from markup
49201             this.el = Roo.get(this.el);
49202         }else {  // generate
49203             var cfg = this.getAutoCreate();
49204             this.el = ct.createChild(cfg, position);
49205         }
49206         if(this.style){
49207             this.el.applyStyles(this.style);
49208         }
49209         if(this.labelAlign){
49210             this.el.addClass('x-form-label-'+this.labelAlign);
49211         }
49212         if(this.hideLabels){
49213             this.labelStyle = "display:none";
49214             this.elementStyle = "padding-left:0;";
49215         }else{
49216             if(typeof this.labelWidth == 'number'){
49217                 this.labelStyle = "width:"+this.labelWidth+"px;";
49218                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
49219             }
49220             if(this.labelAlign == 'top'){
49221                 this.labelStyle = "width:auto;";
49222                 this.elementStyle = "padding-left:0;";
49223             }
49224         }
49225         var stack = this.stack;
49226         var slen = stack.length;
49227         if(slen > 0){
49228             if(!this.fieldTpl){
49229                 var t = new Roo.Template(
49230                     '<div class="x-form-item {5}">',
49231                         '<label for="{0}" style="{2}">{1}{4}</label>',
49232                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
49233                         '</div>',
49234                     '</div><div class="x-form-clear-left"></div>'
49235                 );
49236                 t.disableFormats = true;
49237                 t.compile();
49238                 Roo.form.Layout.prototype.fieldTpl = t;
49239             }
49240             for(var i = 0; i < slen; i++) {
49241                 if(stack[i].isFormField){
49242                     this.renderField(stack[i]);
49243                 }else{
49244                     this.renderComponent(stack[i]);
49245                 }
49246             }
49247         }
49248         if(this.clear){
49249             this.el.createChild({cls:'x-form-clear'});
49250         }
49251     },
49252
49253     // private
49254     renderField : function(f){
49255         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
49256                f.id, //0
49257                f.fieldLabel, //1
49258                f.labelStyle||this.labelStyle||'', //2
49259                this.elementStyle||'', //3
49260                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
49261                f.itemCls||this.itemCls||''  //5
49262        ], true).getPrevSibling());
49263     },
49264
49265     // private
49266     renderComponent : function(c){
49267         c.render(c.isLayout ? this.el : this.el.createChild());    
49268     },
49269     /**
49270      * Adds a object form elements (using the xtype property as the factory method.)
49271      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
49272      * @param {Object} config 
49273      */
49274     addxtype : function(o)
49275     {
49276         // create the lement.
49277         o.form = this.form;
49278         var fe = Roo.factory(o, Roo.form);
49279         this.form.allItems.push(fe);
49280         this.stack.push(fe);
49281         
49282         if (fe.isFormField) {
49283             this.form.items.add(fe);
49284         }
49285          
49286         return fe;
49287     }
49288 });
49289
49290 /**
49291  * @class Roo.form.Column
49292  * @extends Roo.form.Layout
49293  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
49294  * @constructor
49295  * @param {Object} config Configuration options
49296  */
49297 Roo.form.Column = function(config){
49298     Roo.form.Column.superclass.constructor.call(this, config);
49299 };
49300
49301 Roo.extend(Roo.form.Column, Roo.form.Layout, {
49302     /**
49303      * @cfg {Number/String} width
49304      * The fixed width of the column in pixels or CSS value (defaults to "auto")
49305      */
49306     /**
49307      * @cfg {String/Object} autoCreate
49308      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
49309      */
49310
49311     // private
49312     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
49313
49314     // private
49315     onRender : function(ct, position){
49316         Roo.form.Column.superclass.onRender.call(this, ct, position);
49317         if(this.width){
49318             this.el.setWidth(this.width);
49319         }
49320     }
49321 });
49322
49323
49324 /**
49325  * @class Roo.form.Row
49326  * @extends Roo.form.Layout
49327  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
49328  * @constructor
49329  * @param {Object} config Configuration options
49330  */
49331
49332  
49333 Roo.form.Row = function(config){
49334     Roo.form.Row.superclass.constructor.call(this, config);
49335 };
49336  
49337 Roo.extend(Roo.form.Row, Roo.form.Layout, {
49338       /**
49339      * @cfg {Number/String} width
49340      * The fixed width of the column in pixels or CSS value (defaults to "auto")
49341      */
49342     /**
49343      * @cfg {Number/String} height
49344      * The fixed height of the column in pixels or CSS value (defaults to "auto")
49345      */
49346     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
49347     
49348     padWidth : 20,
49349     // private
49350     onRender : function(ct, position){
49351         //console.log('row render');
49352         if(!this.rowTpl){
49353             var t = new Roo.Template(
49354                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
49355                     '<label for="{0}" style="{2}">{1}{4}</label>',
49356                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
49357                     '</div>',
49358                 '</div>'
49359             );
49360             t.disableFormats = true;
49361             t.compile();
49362             Roo.form.Layout.prototype.rowTpl = t;
49363         }
49364         this.fieldTpl = this.rowTpl;
49365         
49366         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
49367         var labelWidth = 100;
49368         
49369         if ((this.labelAlign != 'top')) {
49370             if (typeof this.labelWidth == 'number') {
49371                 labelWidth = this.labelWidth
49372             }
49373             this.padWidth =  20 + labelWidth;
49374             
49375         }
49376         
49377         Roo.form.Column.superclass.onRender.call(this, ct, position);
49378         if(this.width){
49379             this.el.setWidth(this.width);
49380         }
49381         if(this.height){
49382             this.el.setHeight(this.height);
49383         }
49384     },
49385     
49386     // private
49387     renderField : function(f){
49388         f.fieldEl = this.fieldTpl.append(this.el, [
49389                f.id, f.fieldLabel,
49390                f.labelStyle||this.labelStyle||'',
49391                this.elementStyle||'',
49392                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
49393                f.itemCls||this.itemCls||'',
49394                f.width ? f.width + this.padWidth : 160 + this.padWidth
49395        ],true);
49396     }
49397 });
49398  
49399
49400 /**
49401  * @class Roo.form.FieldSet
49402  * @extends Roo.form.Layout
49403  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
49404  * @constructor
49405  * @param {Object} config Configuration options
49406  */
49407 Roo.form.FieldSet = function(config){
49408     Roo.form.FieldSet.superclass.constructor.call(this, config);
49409 };
49410
49411 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
49412     /**
49413      * @cfg {String} legend
49414      * The text to display as the legend for the FieldSet (defaults to '')
49415      */
49416     /**
49417      * @cfg {String/Object} autoCreate
49418      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
49419      */
49420
49421     // private
49422     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
49423
49424     // private
49425     onRender : function(ct, position){
49426         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
49427         if(this.legend){
49428             this.setLegend(this.legend);
49429         }
49430     },
49431
49432     // private
49433     setLegend : function(text){
49434         if(this.rendered){
49435             this.el.child('legend').update(text);
49436         }
49437     }
49438 });/*
49439  * Based on:
49440  * Ext JS Library 1.1.1
49441  * Copyright(c) 2006-2007, Ext JS, LLC.
49442  *
49443  * Originally Released Under LGPL - original licence link has changed is not relivant.
49444  *
49445  * Fork - LGPL
49446  * <script type="text/javascript">
49447  */
49448 /**
49449  * @class Roo.form.VTypes
49450  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
49451  * @singleton
49452  */
49453 Roo.form.VTypes = function(){
49454     // closure these in so they are only created once.
49455     var alpha = /^[a-zA-Z_]+$/;
49456     var alphanum = /^[a-zA-Z0-9_]+$/;
49457     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
49458     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
49459
49460     // All these messages and functions are configurable
49461     return {
49462         /**
49463          * The function used to validate email addresses
49464          * @param {String} value The email address
49465          */
49466         'email' : function(v){
49467             return email.test(v);
49468         },
49469         /**
49470          * The error text to display when the email validation function returns false
49471          * @type String
49472          */
49473         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
49474         /**
49475          * The keystroke filter mask to be applied on email input
49476          * @type RegExp
49477          */
49478         'emailMask' : /[a-z0-9_\.\-@]/i,
49479
49480         /**
49481          * The function used to validate URLs
49482          * @param {String} value The URL
49483          */
49484         'url' : function(v){
49485             return url.test(v);
49486         },
49487         /**
49488          * The error text to display when the url validation function returns false
49489          * @type String
49490          */
49491         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
49492         
49493         /**
49494          * The function used to validate alpha values
49495          * @param {String} value The value
49496          */
49497         'alpha' : function(v){
49498             return alpha.test(v);
49499         },
49500         /**
49501          * The error text to display when the alpha validation function returns false
49502          * @type String
49503          */
49504         'alphaText' : 'This field should only contain letters and _',
49505         /**
49506          * The keystroke filter mask to be applied on alpha input
49507          * @type RegExp
49508          */
49509         'alphaMask' : /[a-z_]/i,
49510
49511         /**
49512          * The function used to validate alphanumeric values
49513          * @param {String} value The value
49514          */
49515         'alphanum' : function(v){
49516             return alphanum.test(v);
49517         },
49518         /**
49519          * The error text to display when the alphanumeric validation function returns false
49520          * @type String
49521          */
49522         'alphanumText' : 'This field should only contain letters, numbers and _',
49523         /**
49524          * The keystroke filter mask to be applied on alphanumeric input
49525          * @type RegExp
49526          */
49527         'alphanumMask' : /[a-z0-9_]/i
49528     };
49529 }();//<script type="text/javascript">
49530
49531 /**
49532  * @class Roo.form.FCKeditor
49533  * @extends Roo.form.TextArea
49534  * Wrapper around the FCKEditor http://www.fckeditor.net
49535  * @constructor
49536  * Creates a new FCKeditor
49537  * @param {Object} config Configuration options
49538  */
49539 Roo.form.FCKeditor = function(config){
49540     Roo.form.FCKeditor.superclass.constructor.call(this, config);
49541     this.addEvents({
49542          /**
49543          * @event editorinit
49544          * Fired when the editor is initialized - you can add extra handlers here..
49545          * @param {FCKeditor} this
49546          * @param {Object} the FCK object.
49547          */
49548         editorinit : true
49549     });
49550     
49551     
49552 };
49553 Roo.form.FCKeditor.editors = { };
49554 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
49555 {
49556     //defaultAutoCreate : {
49557     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
49558     //},
49559     // private
49560     /**
49561      * @cfg {Object} fck options - see fck manual for details.
49562      */
49563     fckconfig : false,
49564     
49565     /**
49566      * @cfg {Object} fck toolbar set (Basic or Default)
49567      */
49568     toolbarSet : 'Basic',
49569     /**
49570      * @cfg {Object} fck BasePath
49571      */ 
49572     basePath : '/fckeditor/',
49573     
49574     
49575     frame : false,
49576     
49577     value : '',
49578     
49579    
49580     onRender : function(ct, position)
49581     {
49582         if(!this.el){
49583             this.defaultAutoCreate = {
49584                 tag: "textarea",
49585                 style:"width:300px;height:60px;",
49586                 autocomplete: "new-password"
49587             };
49588         }
49589         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
49590         /*
49591         if(this.grow){
49592             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
49593             if(this.preventScrollbars){
49594                 this.el.setStyle("overflow", "hidden");
49595             }
49596             this.el.setHeight(this.growMin);
49597         }
49598         */
49599         //console.log('onrender' + this.getId() );
49600         Roo.form.FCKeditor.editors[this.getId()] = this;
49601          
49602
49603         this.replaceTextarea() ;
49604         
49605     },
49606     
49607     getEditor : function() {
49608         return this.fckEditor;
49609     },
49610     /**
49611      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
49612      * @param {Mixed} value The value to set
49613      */
49614     
49615     
49616     setValue : function(value)
49617     {
49618         //console.log('setValue: ' + value);
49619         
49620         if(typeof(value) == 'undefined') { // not sure why this is happending...
49621             return;
49622         }
49623         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
49624         
49625         //if(!this.el || !this.getEditor()) {
49626         //    this.value = value;
49627             //this.setValue.defer(100,this,[value]);    
49628         //    return;
49629         //} 
49630         
49631         if(!this.getEditor()) {
49632             return;
49633         }
49634         
49635         this.getEditor().SetData(value);
49636         
49637         //
49638
49639     },
49640
49641     /**
49642      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
49643      * @return {Mixed} value The field value
49644      */
49645     getValue : function()
49646     {
49647         
49648         if (this.frame && this.frame.dom.style.display == 'none') {
49649             return Roo.form.FCKeditor.superclass.getValue.call(this);
49650         }
49651         
49652         if(!this.el || !this.getEditor()) {
49653            
49654            // this.getValue.defer(100,this); 
49655             return this.value;
49656         }
49657        
49658         
49659         var value=this.getEditor().GetData();
49660         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
49661         return Roo.form.FCKeditor.superclass.getValue.call(this);
49662         
49663
49664     },
49665
49666     /**
49667      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
49668      * @return {Mixed} value The field value
49669      */
49670     getRawValue : function()
49671     {
49672         if (this.frame && this.frame.dom.style.display == 'none') {
49673             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
49674         }
49675         
49676         if(!this.el || !this.getEditor()) {
49677             //this.getRawValue.defer(100,this); 
49678             return this.value;
49679             return;
49680         }
49681         
49682         
49683         
49684         var value=this.getEditor().GetData();
49685         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
49686         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
49687          
49688     },
49689     
49690     setSize : function(w,h) {
49691         
49692         
49693         
49694         //if (this.frame && this.frame.dom.style.display == 'none') {
49695         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
49696         //    return;
49697         //}
49698         //if(!this.el || !this.getEditor()) {
49699         //    this.setSize.defer(100,this, [w,h]); 
49700         //    return;
49701         //}
49702         
49703         
49704         
49705         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
49706         
49707         this.frame.dom.setAttribute('width', w);
49708         this.frame.dom.setAttribute('height', h);
49709         this.frame.setSize(w,h);
49710         
49711     },
49712     
49713     toggleSourceEdit : function(value) {
49714         
49715       
49716          
49717         this.el.dom.style.display = value ? '' : 'none';
49718         this.frame.dom.style.display = value ?  'none' : '';
49719         
49720     },
49721     
49722     
49723     focus: function(tag)
49724     {
49725         if (this.frame.dom.style.display == 'none') {
49726             return Roo.form.FCKeditor.superclass.focus.call(this);
49727         }
49728         if(!this.el || !this.getEditor()) {
49729             this.focus.defer(100,this, [tag]); 
49730             return;
49731         }
49732         
49733         
49734         
49735         
49736         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
49737         this.getEditor().Focus();
49738         if (tgs.length) {
49739             if (!this.getEditor().Selection.GetSelection()) {
49740                 this.focus.defer(100,this, [tag]); 
49741                 return;
49742             }
49743             
49744             
49745             var r = this.getEditor().EditorDocument.createRange();
49746             r.setStart(tgs[0],0);
49747             r.setEnd(tgs[0],0);
49748             this.getEditor().Selection.GetSelection().removeAllRanges();
49749             this.getEditor().Selection.GetSelection().addRange(r);
49750             this.getEditor().Focus();
49751         }
49752         
49753     },
49754     
49755     
49756     
49757     replaceTextarea : function()
49758     {
49759         if ( document.getElementById( this.getId() + '___Frame' ) ) {
49760             return ;
49761         }
49762         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
49763         //{
49764             // We must check the elements firstly using the Id and then the name.
49765         var oTextarea = document.getElementById( this.getId() );
49766         
49767         var colElementsByName = document.getElementsByName( this.getId() ) ;
49768          
49769         oTextarea.style.display = 'none' ;
49770
49771         if ( oTextarea.tabIndex ) {            
49772             this.TabIndex = oTextarea.tabIndex ;
49773         }
49774         
49775         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
49776         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
49777         this.frame = Roo.get(this.getId() + '___Frame')
49778     },
49779     
49780     _getConfigHtml : function()
49781     {
49782         var sConfig = '' ;
49783
49784         for ( var o in this.fckconfig ) {
49785             sConfig += sConfig.length > 0  ? '&amp;' : '';
49786             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
49787         }
49788
49789         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
49790     },
49791     
49792     
49793     _getIFrameHtml : function()
49794     {
49795         var sFile = 'fckeditor.html' ;
49796         /* no idea what this is about..
49797         try
49798         {
49799             if ( (/fcksource=true/i).test( window.top.location.search ) )
49800                 sFile = 'fckeditor.original.html' ;
49801         }
49802         catch (e) { 
49803         */
49804
49805         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
49806         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
49807         
49808         
49809         var html = '<iframe id="' + this.getId() +
49810             '___Frame" src="' + sLink +
49811             '" width="' + this.width +
49812             '" height="' + this.height + '"' +
49813             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
49814             ' frameborder="0" scrolling="no"></iframe>' ;
49815
49816         return html ;
49817     },
49818     
49819     _insertHtmlBefore : function( html, element )
49820     {
49821         if ( element.insertAdjacentHTML )       {
49822             // IE
49823             element.insertAdjacentHTML( 'beforeBegin', html ) ;
49824         } else { // Gecko
49825             var oRange = document.createRange() ;
49826             oRange.setStartBefore( element ) ;
49827             var oFragment = oRange.createContextualFragment( html );
49828             element.parentNode.insertBefore( oFragment, element ) ;
49829         }
49830     }
49831     
49832     
49833   
49834     
49835     
49836     
49837     
49838
49839 });
49840
49841 //Roo.reg('fckeditor', Roo.form.FCKeditor);
49842
49843 function FCKeditor_OnComplete(editorInstance){
49844     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
49845     f.fckEditor = editorInstance;
49846     //console.log("loaded");
49847     f.fireEvent('editorinit', f, editorInstance);
49848
49849   
49850
49851  
49852
49853
49854
49855
49856
49857
49858
49859
49860
49861
49862
49863
49864
49865
49866
49867 //<script type="text/javascript">
49868 /**
49869  * @class Roo.form.GridField
49870  * @extends Roo.form.Field
49871  * Embed a grid (or editable grid into a form)
49872  * STATUS ALPHA
49873  * 
49874  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
49875  * it needs 
49876  * xgrid.store = Roo.data.Store
49877  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
49878  * xgrid.store.reader = Roo.data.JsonReader 
49879  * 
49880  * 
49881  * @constructor
49882  * Creates a new GridField
49883  * @param {Object} config Configuration options
49884  */
49885 Roo.form.GridField = function(config){
49886     Roo.form.GridField.superclass.constructor.call(this, config);
49887      
49888 };
49889
49890 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
49891     /**
49892      * @cfg {Number} width  - used to restrict width of grid..
49893      */
49894     width : 100,
49895     /**
49896      * @cfg {Number} height - used to restrict height of grid..
49897      */
49898     height : 50,
49899      /**
49900      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
49901          * 
49902          *}
49903      */
49904     xgrid : false, 
49905     /**
49906      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
49907      * {tag: "input", type: "checkbox", autocomplete: "off"})
49908      */
49909    // defaultAutoCreate : { tag: 'div' },
49910     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
49911     /**
49912      * @cfg {String} addTitle Text to include for adding a title.
49913      */
49914     addTitle : false,
49915     //
49916     onResize : function(){
49917         Roo.form.Field.superclass.onResize.apply(this, arguments);
49918     },
49919
49920     initEvents : function(){
49921         // Roo.form.Checkbox.superclass.initEvents.call(this);
49922         // has no events...
49923        
49924     },
49925
49926
49927     getResizeEl : function(){
49928         return this.wrap;
49929     },
49930
49931     getPositionEl : function(){
49932         return this.wrap;
49933     },
49934
49935     // private
49936     onRender : function(ct, position){
49937         
49938         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
49939         var style = this.style;
49940         delete this.style;
49941         
49942         Roo.form.GridField.superclass.onRender.call(this, ct, position);
49943         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
49944         this.viewEl = this.wrap.createChild({ tag: 'div' });
49945         if (style) {
49946             this.viewEl.applyStyles(style);
49947         }
49948         if (this.width) {
49949             this.viewEl.setWidth(this.width);
49950         }
49951         if (this.height) {
49952             this.viewEl.setHeight(this.height);
49953         }
49954         //if(this.inputValue !== undefined){
49955         //this.setValue(this.value);
49956         
49957         
49958         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
49959         
49960         
49961         this.grid.render();
49962         this.grid.getDataSource().on('remove', this.refreshValue, this);
49963         this.grid.getDataSource().on('update', this.refreshValue, this);
49964         this.grid.on('afteredit', this.refreshValue, this);
49965  
49966     },
49967      
49968     
49969     /**
49970      * Sets the value of the item. 
49971      * @param {String} either an object  or a string..
49972      */
49973     setValue : function(v){
49974         //this.value = v;
49975         v = v || []; // empty set..
49976         // this does not seem smart - it really only affects memoryproxy grids..
49977         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
49978             var ds = this.grid.getDataSource();
49979             // assumes a json reader..
49980             var data = {}
49981             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
49982             ds.loadData( data);
49983         }
49984         // clear selection so it does not get stale.
49985         if (this.grid.sm) { 
49986             this.grid.sm.clearSelections();
49987         }
49988         
49989         Roo.form.GridField.superclass.setValue.call(this, v);
49990         this.refreshValue();
49991         // should load data in the grid really....
49992     },
49993     
49994     // private
49995     refreshValue: function() {
49996          var val = [];
49997         this.grid.getDataSource().each(function(r) {
49998             val.push(r.data);
49999         });
50000         this.el.dom.value = Roo.encode(val);
50001     }
50002     
50003      
50004     
50005     
50006 });/*
50007  * Based on:
50008  * Ext JS Library 1.1.1
50009  * Copyright(c) 2006-2007, Ext JS, LLC.
50010  *
50011  * Originally Released Under LGPL - original licence link has changed is not relivant.
50012  *
50013  * Fork - LGPL
50014  * <script type="text/javascript">
50015  */
50016 /**
50017  * @class Roo.form.DisplayField
50018  * @extends Roo.form.Field
50019  * A generic Field to display non-editable data.
50020  * @cfg {Boolean} closable (true|false) default false
50021  * @constructor
50022  * Creates a new Display Field item.
50023  * @param {Object} config Configuration options
50024  */
50025 Roo.form.DisplayField = function(config){
50026     Roo.form.DisplayField.superclass.constructor.call(this, config);
50027     
50028     this.addEvents({
50029         /**
50030          * @event close
50031          * Fires after the click the close btn
50032              * @param {Roo.form.DisplayField} this
50033              */
50034         close : true
50035     });
50036 };
50037
50038 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
50039     inputType:      'hidden',
50040     allowBlank:     true,
50041     readOnly:         true,
50042     
50043  
50044     /**
50045      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
50046      */
50047     focusClass : undefined,
50048     /**
50049      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
50050      */
50051     fieldClass: 'x-form-field',
50052     
50053      /**
50054      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
50055      */
50056     valueRenderer: undefined,
50057     
50058     width: 100,
50059     /**
50060      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
50061      * {tag: "input", type: "checkbox", autocomplete: "off"})
50062      */
50063      
50064  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
50065  
50066     closable : false,
50067     
50068     onResize : function(){
50069         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
50070         
50071     },
50072
50073     initEvents : function(){
50074         // Roo.form.Checkbox.superclass.initEvents.call(this);
50075         // has no events...
50076         
50077         if(this.closable){
50078             this.closeEl.on('click', this.onClose, this);
50079         }
50080        
50081     },
50082
50083
50084     getResizeEl : function(){
50085         return this.wrap;
50086     },
50087
50088     getPositionEl : function(){
50089         return this.wrap;
50090     },
50091
50092     // private
50093     onRender : function(ct, position){
50094         
50095         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
50096         //if(this.inputValue !== undefined){
50097         this.wrap = this.el.wrap();
50098         
50099         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
50100         
50101         if(this.closable){
50102             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
50103         }
50104         
50105         if (this.bodyStyle) {
50106             this.viewEl.applyStyles(this.bodyStyle);
50107         }
50108         //this.viewEl.setStyle('padding', '2px');
50109         
50110         this.setValue(this.value);
50111         
50112     },
50113 /*
50114     // private
50115     initValue : Roo.emptyFn,
50116
50117   */
50118
50119         // private
50120     onClick : function(){
50121         
50122     },
50123
50124     /**
50125      * Sets the checked state of the checkbox.
50126      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
50127      */
50128     setValue : function(v){
50129         this.value = v;
50130         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
50131         // this might be called before we have a dom element..
50132         if (!this.viewEl) {
50133             return;
50134         }
50135         this.viewEl.dom.innerHTML = html;
50136         Roo.form.DisplayField.superclass.setValue.call(this, v);
50137
50138     },
50139     
50140     onClose : function(e)
50141     {
50142         e.preventDefault();
50143         
50144         this.fireEvent('close', this);
50145     }
50146 });/*
50147  * 
50148  * Licence- LGPL
50149  * 
50150  */
50151
50152 /**
50153  * @class Roo.form.DayPicker
50154  * @extends Roo.form.Field
50155  * A Day picker show [M] [T] [W] ....
50156  * @constructor
50157  * Creates a new Day Picker
50158  * @param {Object} config Configuration options
50159  */
50160 Roo.form.DayPicker= function(config){
50161     Roo.form.DayPicker.superclass.constructor.call(this, config);
50162      
50163 };
50164
50165 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
50166     /**
50167      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
50168      */
50169     focusClass : undefined,
50170     /**
50171      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
50172      */
50173     fieldClass: "x-form-field",
50174    
50175     /**
50176      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
50177      * {tag: "input", type: "checkbox", autocomplete: "off"})
50178      */
50179     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
50180     
50181    
50182     actionMode : 'viewEl', 
50183     //
50184     // private
50185  
50186     inputType : 'hidden',
50187     
50188      
50189     inputElement: false, // real input element?
50190     basedOn: false, // ????
50191     
50192     isFormField: true, // not sure where this is needed!!!!
50193
50194     onResize : function(){
50195         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
50196         if(!this.boxLabel){
50197             this.el.alignTo(this.wrap, 'c-c');
50198         }
50199     },
50200
50201     initEvents : function(){
50202         Roo.form.Checkbox.superclass.initEvents.call(this);
50203         this.el.on("click", this.onClick,  this);
50204         this.el.on("change", this.onClick,  this);
50205     },
50206
50207
50208     getResizeEl : function(){
50209         return this.wrap;
50210     },
50211
50212     getPositionEl : function(){
50213         return this.wrap;
50214     },
50215
50216     
50217     // private
50218     onRender : function(ct, position){
50219         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
50220        
50221         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
50222         
50223         var r1 = '<table><tr>';
50224         var r2 = '<tr class="x-form-daypick-icons">';
50225         for (var i=0; i < 7; i++) {
50226             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
50227             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
50228         }
50229         
50230         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
50231         viewEl.select('img').on('click', this.onClick, this);
50232         this.viewEl = viewEl;   
50233         
50234         
50235         // this will not work on Chrome!!!
50236         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
50237         this.el.on('propertychange', this.setFromHidden,  this);  //ie
50238         
50239         
50240           
50241
50242     },
50243
50244     // private
50245     initValue : Roo.emptyFn,
50246
50247     /**
50248      * Returns the checked state of the checkbox.
50249      * @return {Boolean} True if checked, else false
50250      */
50251     getValue : function(){
50252         return this.el.dom.value;
50253         
50254     },
50255
50256         // private
50257     onClick : function(e){ 
50258         //this.setChecked(!this.checked);
50259         Roo.get(e.target).toggleClass('x-menu-item-checked');
50260         this.refreshValue();
50261         //if(this.el.dom.checked != this.checked){
50262         //    this.setValue(this.el.dom.checked);
50263        // }
50264     },
50265     
50266     // private
50267     refreshValue : function()
50268     {
50269         var val = '';
50270         this.viewEl.select('img',true).each(function(e,i,n)  {
50271             val += e.is(".x-menu-item-checked") ? String(n) : '';
50272         });
50273         this.setValue(val, true);
50274     },
50275
50276     /**
50277      * Sets the checked state of the checkbox.
50278      * On is always based on a string comparison between inputValue and the param.
50279      * @param {Boolean/String} value - the value to set 
50280      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
50281      */
50282     setValue : function(v,suppressEvent){
50283         if (!this.el.dom) {
50284             return;
50285         }
50286         var old = this.el.dom.value ;
50287         this.el.dom.value = v;
50288         if (suppressEvent) {
50289             return ;
50290         }
50291          
50292         // update display..
50293         this.viewEl.select('img',true).each(function(e,i,n)  {
50294             
50295             var on = e.is(".x-menu-item-checked");
50296             var newv = v.indexOf(String(n)) > -1;
50297             if (on != newv) {
50298                 e.toggleClass('x-menu-item-checked');
50299             }
50300             
50301         });
50302         
50303         
50304         this.fireEvent('change', this, v, old);
50305         
50306         
50307     },
50308    
50309     // handle setting of hidden value by some other method!!?!?
50310     setFromHidden: function()
50311     {
50312         if(!this.el){
50313             return;
50314         }
50315         //console.log("SET FROM HIDDEN");
50316         //alert('setFrom hidden');
50317         this.setValue(this.el.dom.value);
50318     },
50319     
50320     onDestroy : function()
50321     {
50322         if(this.viewEl){
50323             Roo.get(this.viewEl).remove();
50324         }
50325          
50326         Roo.form.DayPicker.superclass.onDestroy.call(this);
50327     }
50328
50329 });/*
50330  * RooJS Library 1.1.1
50331  * Copyright(c) 2008-2011  Alan Knowles
50332  *
50333  * License - LGPL
50334  */
50335  
50336
50337 /**
50338  * @class Roo.form.ComboCheck
50339  * @extends Roo.form.ComboBox
50340  * A combobox for multiple select items.
50341  *
50342  * FIXME - could do with a reset button..
50343  * 
50344  * @constructor
50345  * Create a new ComboCheck
50346  * @param {Object} config Configuration options
50347  */
50348 Roo.form.ComboCheck = function(config){
50349     Roo.form.ComboCheck.superclass.constructor.call(this, config);
50350     // should verify some data...
50351     // like
50352     // hiddenName = required..
50353     // displayField = required
50354     // valudField == required
50355     var req= [ 'hiddenName', 'displayField', 'valueField' ];
50356     var _t = this;
50357     Roo.each(req, function(e) {
50358         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
50359             throw "Roo.form.ComboCheck : missing value for: " + e;
50360         }
50361     });
50362     
50363     
50364 };
50365
50366 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
50367      
50368      
50369     editable : false,
50370      
50371     selectedClass: 'x-menu-item-checked', 
50372     
50373     // private
50374     onRender : function(ct, position){
50375         var _t = this;
50376         
50377         
50378         
50379         if(!this.tpl){
50380             var cls = 'x-combo-list';
50381
50382             
50383             this.tpl =  new Roo.Template({
50384                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
50385                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
50386                    '<span>{' + this.displayField + '}</span>' +
50387                     '</div>' 
50388                 
50389             });
50390         }
50391  
50392         
50393         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
50394         this.view.singleSelect = false;
50395         this.view.multiSelect = true;
50396         this.view.toggleSelect = true;
50397         this.pageTb.add(new Roo.Toolbar.Fill(), {
50398             
50399             text: 'Done',
50400             handler: function()
50401             {
50402                 _t.collapse();
50403             }
50404         });
50405     },
50406     
50407     onViewOver : function(e, t){
50408         // do nothing...
50409         return;
50410         
50411     },
50412     
50413     onViewClick : function(doFocus,index){
50414         return;
50415         
50416     },
50417     select: function () {
50418         //Roo.log("SELECT CALLED");
50419     },
50420      
50421     selectByValue : function(xv, scrollIntoView){
50422         var ar = this.getValueArray();
50423         var sels = [];
50424         
50425         Roo.each(ar, function(v) {
50426             if(v === undefined || v === null){
50427                 return;
50428             }
50429             var r = this.findRecord(this.valueField, v);
50430             if(r){
50431                 sels.push(this.store.indexOf(r))
50432                 
50433             }
50434         },this);
50435         this.view.select(sels);
50436         return false;
50437     },
50438     
50439     
50440     
50441     onSelect : function(record, index){
50442        // Roo.log("onselect Called");
50443        // this is only called by the clear button now..
50444         this.view.clearSelections();
50445         this.setValue('[]');
50446         if (this.value != this.valueBefore) {
50447             this.fireEvent('change', this, this.value, this.valueBefore);
50448             this.valueBefore = this.value;
50449         }
50450     },
50451     getValueArray : function()
50452     {
50453         var ar = [] ;
50454         
50455         try {
50456             //Roo.log(this.value);
50457             if (typeof(this.value) == 'undefined') {
50458                 return [];
50459             }
50460             var ar = Roo.decode(this.value);
50461             return  ar instanceof Array ? ar : []; //?? valid?
50462             
50463         } catch(e) {
50464             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
50465             return [];
50466         }
50467          
50468     },
50469     expand : function ()
50470     {
50471         
50472         Roo.form.ComboCheck.superclass.expand.call(this);
50473         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
50474         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
50475         
50476
50477     },
50478     
50479     collapse : function(){
50480         Roo.form.ComboCheck.superclass.collapse.call(this);
50481         var sl = this.view.getSelectedIndexes();
50482         var st = this.store;
50483         var nv = [];
50484         var tv = [];
50485         var r;
50486         Roo.each(sl, function(i) {
50487             r = st.getAt(i);
50488             nv.push(r.get(this.valueField));
50489         },this);
50490         this.setValue(Roo.encode(nv));
50491         if (this.value != this.valueBefore) {
50492
50493             this.fireEvent('change', this, this.value, this.valueBefore);
50494             this.valueBefore = this.value;
50495         }
50496         
50497     },
50498     
50499     setValue : function(v){
50500         // Roo.log(v);
50501         this.value = v;
50502         
50503         var vals = this.getValueArray();
50504         var tv = [];
50505         Roo.each(vals, function(k) {
50506             var r = this.findRecord(this.valueField, k);
50507             if(r){
50508                 tv.push(r.data[this.displayField]);
50509             }else if(this.valueNotFoundText !== undefined){
50510                 tv.push( this.valueNotFoundText );
50511             }
50512         },this);
50513        // Roo.log(tv);
50514         
50515         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
50516         this.hiddenField.value = v;
50517         this.value = v;
50518     }
50519     
50520 });/*
50521  * Based on:
50522  * Ext JS Library 1.1.1
50523  * Copyright(c) 2006-2007, Ext JS, LLC.
50524  *
50525  * Originally Released Under LGPL - original licence link has changed is not relivant.
50526  *
50527  * Fork - LGPL
50528  * <script type="text/javascript">
50529  */
50530  
50531 /**
50532  * @class Roo.form.Signature
50533  * @extends Roo.form.Field
50534  * Signature field.  
50535  * @constructor
50536  * 
50537  * @param {Object} config Configuration options
50538  */
50539
50540 Roo.form.Signature = function(config){
50541     Roo.form.Signature.superclass.constructor.call(this, config);
50542     
50543     this.addEvents({// not in used??
50544          /**
50545          * @event confirm
50546          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
50547              * @param {Roo.form.Signature} combo This combo box
50548              */
50549         'confirm' : true,
50550         /**
50551          * @event reset
50552          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
50553              * @param {Roo.form.ComboBox} combo This combo box
50554              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
50555              */
50556         'reset' : true
50557     });
50558 };
50559
50560 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
50561     /**
50562      * @cfg {Object} labels Label to use when rendering a form.
50563      * defaults to 
50564      * labels : { 
50565      *      clear : "Clear",
50566      *      confirm : "Confirm"
50567      *  }
50568      */
50569     labels : { 
50570         clear : "Clear",
50571         confirm : "Confirm"
50572     },
50573     /**
50574      * @cfg {Number} width The signature panel width (defaults to 300)
50575      */
50576     width: 300,
50577     /**
50578      * @cfg {Number} height The signature panel height (defaults to 100)
50579      */
50580     height : 100,
50581     /**
50582      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
50583      */
50584     allowBlank : false,
50585     
50586     //private
50587     // {Object} signPanel The signature SVG panel element (defaults to {})
50588     signPanel : {},
50589     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
50590     isMouseDown : false,
50591     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
50592     isConfirmed : false,
50593     // {String} signatureTmp SVG mapping string (defaults to empty string)
50594     signatureTmp : '',
50595     
50596     
50597     defaultAutoCreate : { // modified by initCompnoent..
50598         tag: "input",
50599         type:"hidden"
50600     },
50601
50602     // private
50603     onRender : function(ct, position){
50604         
50605         Roo.form.Signature.superclass.onRender.call(this, ct, position);
50606         
50607         this.wrap = this.el.wrap({
50608             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
50609         });
50610         
50611         this.createToolbar(this);
50612         this.signPanel = this.wrap.createChild({
50613                 tag: 'div',
50614                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
50615             }, this.el
50616         );
50617             
50618         this.svgID = Roo.id();
50619         this.svgEl = this.signPanel.createChild({
50620               xmlns : 'http://www.w3.org/2000/svg',
50621               tag : 'svg',
50622               id : this.svgID + "-svg",
50623               width: this.width,
50624               height: this.height,
50625               viewBox: '0 0 '+this.width+' '+this.height,
50626               cn : [
50627                 {
50628                     tag: "rect",
50629                     id: this.svgID + "-svg-r",
50630                     width: this.width,
50631                     height: this.height,
50632                     fill: "#ffa"
50633                 },
50634                 {
50635                     tag: "line",
50636                     id: this.svgID + "-svg-l",
50637                     x1: "0", // start
50638                     y1: (this.height*0.8), // start set the line in 80% of height
50639                     x2: this.width, // end
50640                     y2: (this.height*0.8), // end set the line in 80% of height
50641                     'stroke': "#666",
50642                     'stroke-width': "1",
50643                     'stroke-dasharray': "3",
50644                     'shape-rendering': "crispEdges",
50645                     'pointer-events': "none"
50646                 },
50647                 {
50648                     tag: "path",
50649                     id: this.svgID + "-svg-p",
50650                     'stroke': "navy",
50651                     'stroke-width': "3",
50652                     'fill': "none",
50653                     'pointer-events': 'none'
50654                 }
50655               ]
50656         });
50657         this.createSVG();
50658         this.svgBox = this.svgEl.dom.getScreenCTM();
50659     },
50660     createSVG : function(){ 
50661         var svg = this.signPanel;
50662         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
50663         var t = this;
50664
50665         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
50666         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
50667         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
50668         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
50669         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
50670         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
50671         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
50672         
50673     },
50674     isTouchEvent : function(e){
50675         return e.type.match(/^touch/);
50676     },
50677     getCoords : function (e) {
50678         var pt    = this.svgEl.dom.createSVGPoint();
50679         pt.x = e.clientX; 
50680         pt.y = e.clientY;
50681         if (this.isTouchEvent(e)) {
50682             pt.x =  e.targetTouches[0].clientX;
50683             pt.y = e.targetTouches[0].clientY;
50684         }
50685         var a = this.svgEl.dom.getScreenCTM();
50686         var b = a.inverse();
50687         var mx = pt.matrixTransform(b);
50688         return mx.x + ',' + mx.y;
50689     },
50690     //mouse event headler 
50691     down : function (e) {
50692         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
50693         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
50694         
50695         this.isMouseDown = true;
50696         
50697         e.preventDefault();
50698     },
50699     move : function (e) {
50700         if (this.isMouseDown) {
50701             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
50702             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
50703         }
50704         
50705         e.preventDefault();
50706     },
50707     up : function (e) {
50708         this.isMouseDown = false;
50709         var sp = this.signatureTmp.split(' ');
50710         
50711         if(sp.length > 1){
50712             if(!sp[sp.length-2].match(/^L/)){
50713                 sp.pop();
50714                 sp.pop();
50715                 sp.push("");
50716                 this.signatureTmp = sp.join(" ");
50717             }
50718         }
50719         if(this.getValue() != this.signatureTmp){
50720             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
50721             this.isConfirmed = false;
50722         }
50723         e.preventDefault();
50724     },
50725     
50726     /**
50727      * Protected method that will not generally be called directly. It
50728      * is called when the editor creates its toolbar. Override this method if you need to
50729      * add custom toolbar buttons.
50730      * @param {HtmlEditor} editor
50731      */
50732     createToolbar : function(editor){
50733          function btn(id, toggle, handler){
50734             var xid = fid + '-'+ id ;
50735             return {
50736                 id : xid,
50737                 cmd : id,
50738                 cls : 'x-btn-icon x-edit-'+id,
50739                 enableToggle:toggle !== false,
50740                 scope: editor, // was editor...
50741                 handler:handler||editor.relayBtnCmd,
50742                 clickEvent:'mousedown',
50743                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
50744                 tabIndex:-1
50745             };
50746         }
50747         
50748         
50749         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
50750         this.tb = tb;
50751         this.tb.add(
50752            {
50753                 cls : ' x-signature-btn x-signature-'+id,
50754                 scope: editor, // was editor...
50755                 handler: this.reset,
50756                 clickEvent:'mousedown',
50757                 text: this.labels.clear
50758             },
50759             {
50760                  xtype : 'Fill',
50761                  xns: Roo.Toolbar
50762             }, 
50763             {
50764                 cls : '  x-signature-btn x-signature-'+id,
50765                 scope: editor, // was editor...
50766                 handler: this.confirmHandler,
50767                 clickEvent:'mousedown',
50768                 text: this.labels.confirm
50769             }
50770         );
50771     
50772     },
50773     //public
50774     /**
50775      * when user is clicked confirm then show this image.....
50776      * 
50777      * @return {String} Image Data URI
50778      */
50779     getImageDataURI : function(){
50780         var svg = this.svgEl.dom.parentNode.innerHTML;
50781         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
50782         return src; 
50783     },
50784     /**
50785      * 
50786      * @return {Boolean} this.isConfirmed
50787      */
50788     getConfirmed : function(){
50789         return this.isConfirmed;
50790     },
50791     /**
50792      * 
50793      * @return {Number} this.width
50794      */
50795     getWidth : function(){
50796         return this.width;
50797     },
50798     /**
50799      * 
50800      * @return {Number} this.height
50801      */
50802     getHeight : function(){
50803         return this.height;
50804     },
50805     // private
50806     getSignature : function(){
50807         return this.signatureTmp;
50808     },
50809     // private
50810     reset : function(){
50811         this.signatureTmp = '';
50812         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
50813         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
50814         this.isConfirmed = false;
50815         Roo.form.Signature.superclass.reset.call(this);
50816     },
50817     setSignature : function(s){
50818         this.signatureTmp = s;
50819         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
50820         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
50821         this.setValue(s);
50822         this.isConfirmed = false;
50823         Roo.form.Signature.superclass.reset.call(this);
50824     }, 
50825     test : function(){
50826 //        Roo.log(this.signPanel.dom.contentWindow.up())
50827     },
50828     //private
50829     setConfirmed : function(){
50830         
50831         
50832         
50833 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
50834     },
50835     // private
50836     confirmHandler : function(){
50837         if(!this.getSignature()){
50838             return;
50839         }
50840         
50841         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
50842         this.setValue(this.getSignature());
50843         this.isConfirmed = true;
50844         
50845         this.fireEvent('confirm', this);
50846     },
50847     // private
50848     // Subclasses should provide the validation implementation by overriding this
50849     validateValue : function(value){
50850         if(this.allowBlank){
50851             return true;
50852         }
50853         
50854         if(this.isConfirmed){
50855             return true;
50856         }
50857         return false;
50858     }
50859 });/*
50860  * Based on:
50861  * Ext JS Library 1.1.1
50862  * Copyright(c) 2006-2007, Ext JS, LLC.
50863  *
50864  * Originally Released Under LGPL - original licence link has changed is not relivant.
50865  *
50866  * Fork - LGPL
50867  * <script type="text/javascript">
50868  */
50869  
50870
50871 /**
50872  * @class Roo.form.ComboBox
50873  * @extends Roo.form.TriggerField
50874  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
50875  * @constructor
50876  * Create a new ComboBox.
50877  * @param {Object} config Configuration options
50878  */
50879 Roo.form.Select = function(config){
50880     Roo.form.Select.superclass.constructor.call(this, config);
50881      
50882 };
50883
50884 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
50885     /**
50886      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
50887      */
50888     /**
50889      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
50890      * rendering into an Roo.Editor, defaults to false)
50891      */
50892     /**
50893      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
50894      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
50895      */
50896     /**
50897      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
50898      */
50899     /**
50900      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
50901      * the dropdown list (defaults to undefined, with no header element)
50902      */
50903
50904      /**
50905      * @cfg {String/Roo.Template} tpl The template to use to render the output
50906      */
50907      
50908     // private
50909     defaultAutoCreate : {tag: "select"  },
50910     /**
50911      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
50912      */
50913     listWidth: undefined,
50914     /**
50915      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
50916      * mode = 'remote' or 'text' if mode = 'local')
50917      */
50918     displayField: undefined,
50919     /**
50920      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
50921      * mode = 'remote' or 'value' if mode = 'local'). 
50922      * Note: use of a valueField requires the user make a selection
50923      * in order for a value to be mapped.
50924      */
50925     valueField: undefined,
50926     
50927     
50928     /**
50929      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
50930      * field's data value (defaults to the underlying DOM element's name)
50931      */
50932     hiddenName: undefined,
50933     /**
50934      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
50935      */
50936     listClass: '',
50937     /**
50938      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
50939      */
50940     selectedClass: 'x-combo-selected',
50941     /**
50942      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
50943      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
50944      * which displays a downward arrow icon).
50945      */
50946     triggerClass : 'x-form-arrow-trigger',
50947     /**
50948      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
50949      */
50950     shadow:'sides',
50951     /**
50952      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
50953      * anchor positions (defaults to 'tl-bl')
50954      */
50955     listAlign: 'tl-bl?',
50956     /**
50957      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
50958      */
50959     maxHeight: 300,
50960     /**
50961      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
50962      * query specified by the allQuery config option (defaults to 'query')
50963      */
50964     triggerAction: 'query',
50965     /**
50966      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
50967      * (defaults to 4, does not apply if editable = false)
50968      */
50969     minChars : 4,
50970     /**
50971      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
50972      * delay (typeAheadDelay) if it matches a known value (defaults to false)
50973      */
50974     typeAhead: false,
50975     /**
50976      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
50977      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
50978      */
50979     queryDelay: 500,
50980     /**
50981      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
50982      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
50983      */
50984     pageSize: 0,
50985     /**
50986      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
50987      * when editable = true (defaults to false)
50988      */
50989     selectOnFocus:false,
50990     /**
50991      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
50992      */
50993     queryParam: 'query',
50994     /**
50995      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
50996      * when mode = 'remote' (defaults to 'Loading...')
50997      */
50998     loadingText: 'Loading...',
50999     /**
51000      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
51001      */
51002     resizable: false,
51003     /**
51004      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
51005      */
51006     handleHeight : 8,
51007     /**
51008      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
51009      * traditional select (defaults to true)
51010      */
51011     editable: true,
51012     /**
51013      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
51014      */
51015     allQuery: '',
51016     /**
51017      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
51018      */
51019     mode: 'remote',
51020     /**
51021      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
51022      * listWidth has a higher value)
51023      */
51024     minListWidth : 70,
51025     /**
51026      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
51027      * allow the user to set arbitrary text into the field (defaults to false)
51028      */
51029     forceSelection:false,
51030     /**
51031      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
51032      * if typeAhead = true (defaults to 250)
51033      */
51034     typeAheadDelay : 250,
51035     /**
51036      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
51037      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
51038      */
51039     valueNotFoundText : undefined,
51040     
51041     /**
51042      * @cfg {String} defaultValue The value displayed after loading the store.
51043      */
51044     defaultValue: '',
51045     
51046     /**
51047      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
51048      */
51049     blockFocus : false,
51050     
51051     /**
51052      * @cfg {Boolean} disableClear Disable showing of clear button.
51053      */
51054     disableClear : false,
51055     /**
51056      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
51057      */
51058     alwaysQuery : false,
51059     
51060     //private
51061     addicon : false,
51062     editicon: false,
51063     
51064     // element that contains real text value.. (when hidden is used..)
51065      
51066     // private
51067     onRender : function(ct, position){
51068         Roo.form.Field.prototype.onRender.call(this, ct, position);
51069         
51070         if(this.store){
51071             this.store.on('beforeload', this.onBeforeLoad, this);
51072             this.store.on('load', this.onLoad, this);
51073             this.store.on('loadexception', this.onLoadException, this);
51074             this.store.load({});
51075         }
51076         
51077         
51078         
51079     },
51080
51081     // private
51082     initEvents : function(){
51083         //Roo.form.ComboBox.superclass.initEvents.call(this);
51084  
51085     },
51086
51087     onDestroy : function(){
51088        
51089         if(this.store){
51090             this.store.un('beforeload', this.onBeforeLoad, this);
51091             this.store.un('load', this.onLoad, this);
51092             this.store.un('loadexception', this.onLoadException, this);
51093         }
51094         //Roo.form.ComboBox.superclass.onDestroy.call(this);
51095     },
51096
51097     // private
51098     fireKey : function(e){
51099         if(e.isNavKeyPress() && !this.list.isVisible()){
51100             this.fireEvent("specialkey", this, e);
51101         }
51102     },
51103
51104     // private
51105     onResize: function(w, h){
51106         
51107         return; 
51108     
51109         
51110     },
51111
51112     /**
51113      * Allow or prevent the user from directly editing the field text.  If false is passed,
51114      * the user will only be able to select from the items defined in the dropdown list.  This method
51115      * is the runtime equivalent of setting the 'editable' config option at config time.
51116      * @param {Boolean} value True to allow the user to directly edit the field text
51117      */
51118     setEditable : function(value){
51119          
51120     },
51121
51122     // private
51123     onBeforeLoad : function(){
51124         
51125         Roo.log("Select before load");
51126         return;
51127     
51128         this.innerList.update(this.loadingText ?
51129                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
51130         //this.restrictHeight();
51131         this.selectedIndex = -1;
51132     },
51133
51134     // private
51135     onLoad : function(){
51136
51137     
51138         var dom = this.el.dom;
51139         dom.innerHTML = '';
51140          var od = dom.ownerDocument;
51141          
51142         if (this.emptyText) {
51143             var op = od.createElement('option');
51144             op.setAttribute('value', '');
51145             op.innerHTML = String.format('{0}', this.emptyText);
51146             dom.appendChild(op);
51147         }
51148         if(this.store.getCount() > 0){
51149            
51150             var vf = this.valueField;
51151             var df = this.displayField;
51152             this.store.data.each(function(r) {
51153                 // which colmsn to use... testing - cdoe / title..
51154                 var op = od.createElement('option');
51155                 op.setAttribute('value', r.data[vf]);
51156                 op.innerHTML = String.format('{0}', r.data[df]);
51157                 dom.appendChild(op);
51158             });
51159             if (typeof(this.defaultValue != 'undefined')) {
51160                 this.setValue(this.defaultValue);
51161             }
51162             
51163              
51164         }else{
51165             //this.onEmptyResults();
51166         }
51167         //this.el.focus();
51168     },
51169     // private
51170     onLoadException : function()
51171     {
51172         dom.innerHTML = '';
51173             
51174         Roo.log("Select on load exception");
51175         return;
51176     
51177         this.collapse();
51178         Roo.log(this.store.reader.jsonData);
51179         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
51180             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
51181         }
51182         
51183         
51184     },
51185     // private
51186     onTypeAhead : function(){
51187          
51188     },
51189
51190     // private
51191     onSelect : function(record, index){
51192         Roo.log('on select?');
51193         return;
51194         if(this.fireEvent('beforeselect', this, record, index) !== false){
51195             this.setFromData(index > -1 ? record.data : false);
51196             this.collapse();
51197             this.fireEvent('select', this, record, index);
51198         }
51199     },
51200
51201     /**
51202      * Returns the currently selected field value or empty string if no value is set.
51203      * @return {String} value The selected value
51204      */
51205     getValue : function(){
51206         var dom = this.el.dom;
51207         this.value = dom.options[dom.selectedIndex].value;
51208         return this.value;
51209         
51210     },
51211
51212     /**
51213      * Clears any text/value currently set in the field
51214      */
51215     clearValue : function(){
51216         this.value = '';
51217         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
51218         
51219     },
51220
51221     /**
51222      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
51223      * will be displayed in the field.  If the value does not match the data value of an existing item,
51224      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
51225      * Otherwise the field will be blank (although the value will still be set).
51226      * @param {String} value The value to match
51227      */
51228     setValue : function(v){
51229         var d = this.el.dom;
51230         for (var i =0; i < d.options.length;i++) {
51231             if (v == d.options[i].value) {
51232                 d.selectedIndex = i;
51233                 this.value = v;
51234                 return;
51235             }
51236         }
51237         this.clearValue();
51238     },
51239     /**
51240      * @property {Object} the last set data for the element
51241      */
51242     
51243     lastData : false,
51244     /**
51245      * Sets the value of the field based on a object which is related to the record format for the store.
51246      * @param {Object} value the value to set as. or false on reset?
51247      */
51248     setFromData : function(o){
51249         Roo.log('setfrom data?');
51250          
51251         
51252         
51253     },
51254     // private
51255     reset : function(){
51256         this.clearValue();
51257     },
51258     // private
51259     findRecord : function(prop, value){
51260         
51261         return false;
51262     
51263         var record;
51264         if(this.store.getCount() > 0){
51265             this.store.each(function(r){
51266                 if(r.data[prop] == value){
51267                     record = r;
51268                     return false;
51269                 }
51270                 return true;
51271             });
51272         }
51273         return record;
51274     },
51275     
51276     getName: function()
51277     {
51278         // returns hidden if it's set..
51279         if (!this.rendered) {return ''};
51280         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
51281         
51282     },
51283      
51284
51285     
51286
51287     // private
51288     onEmptyResults : function(){
51289         Roo.log('empty results');
51290         //this.collapse();
51291     },
51292
51293     /**
51294      * Returns true if the dropdown list is expanded, else false.
51295      */
51296     isExpanded : function(){
51297         return false;
51298     },
51299
51300     /**
51301      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
51302      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
51303      * @param {String} value The data value of the item to select
51304      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
51305      * selected item if it is not currently in view (defaults to true)
51306      * @return {Boolean} True if the value matched an item in the list, else false
51307      */
51308     selectByValue : function(v, scrollIntoView){
51309         Roo.log('select By Value');
51310         return false;
51311     
51312         if(v !== undefined && v !== null){
51313             var r = this.findRecord(this.valueField || this.displayField, v);
51314             if(r){
51315                 this.select(this.store.indexOf(r), scrollIntoView);
51316                 return true;
51317             }
51318         }
51319         return false;
51320     },
51321
51322     /**
51323      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
51324      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
51325      * @param {Number} index The zero-based index of the list item to select
51326      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
51327      * selected item if it is not currently in view (defaults to true)
51328      */
51329     select : function(index, scrollIntoView){
51330         Roo.log('select ');
51331         return  ;
51332         
51333         this.selectedIndex = index;
51334         this.view.select(index);
51335         if(scrollIntoView !== false){
51336             var el = this.view.getNode(index);
51337             if(el){
51338                 this.innerList.scrollChildIntoView(el, false);
51339             }
51340         }
51341     },
51342
51343       
51344
51345     // private
51346     validateBlur : function(){
51347         
51348         return;
51349         
51350     },
51351
51352     // private
51353     initQuery : function(){
51354         this.doQuery(this.getRawValue());
51355     },
51356
51357     // private
51358     doForce : function(){
51359         if(this.el.dom.value.length > 0){
51360             this.el.dom.value =
51361                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
51362              
51363         }
51364     },
51365
51366     /**
51367      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
51368      * query allowing the query action to be canceled if needed.
51369      * @param {String} query The SQL query to execute
51370      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
51371      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
51372      * saved in the current store (defaults to false)
51373      */
51374     doQuery : function(q, forceAll){
51375         
51376         Roo.log('doQuery?');
51377         if(q === undefined || q === null){
51378             q = '';
51379         }
51380         var qe = {
51381             query: q,
51382             forceAll: forceAll,
51383             combo: this,
51384             cancel:false
51385         };
51386         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
51387             return false;
51388         }
51389         q = qe.query;
51390         forceAll = qe.forceAll;
51391         if(forceAll === true || (q.length >= this.minChars)){
51392             if(this.lastQuery != q || this.alwaysQuery){
51393                 this.lastQuery = q;
51394                 if(this.mode == 'local'){
51395                     this.selectedIndex = -1;
51396                     if(forceAll){
51397                         this.store.clearFilter();
51398                     }else{
51399                         this.store.filter(this.displayField, q);
51400                     }
51401                     this.onLoad();
51402                 }else{
51403                     this.store.baseParams[this.queryParam] = q;
51404                     this.store.load({
51405                         params: this.getParams(q)
51406                     });
51407                     this.expand();
51408                 }
51409             }else{
51410                 this.selectedIndex = -1;
51411                 this.onLoad();   
51412             }
51413         }
51414     },
51415
51416     // private
51417     getParams : function(q){
51418         var p = {};
51419         //p[this.queryParam] = q;
51420         if(this.pageSize){
51421             p.start = 0;
51422             p.limit = this.pageSize;
51423         }
51424         return p;
51425     },
51426
51427     /**
51428      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
51429      */
51430     collapse : function(){
51431         
51432     },
51433
51434     // private
51435     collapseIf : function(e){
51436         
51437     },
51438
51439     /**
51440      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
51441      */
51442     expand : function(){
51443         
51444     } ,
51445
51446     // private
51447      
51448
51449     /** 
51450     * @cfg {Boolean} grow 
51451     * @hide 
51452     */
51453     /** 
51454     * @cfg {Number} growMin 
51455     * @hide 
51456     */
51457     /** 
51458     * @cfg {Number} growMax 
51459     * @hide 
51460     */
51461     /**
51462      * @hide
51463      * @method autoSize
51464      */
51465     
51466     setWidth : function()
51467     {
51468         
51469     },
51470     getResizeEl : function(){
51471         return this.el;
51472     }
51473 });//<script type="text/javasscript">
51474  
51475
51476 /**
51477  * @class Roo.DDView
51478  * A DnD enabled version of Roo.View.
51479  * @param {Element/String} container The Element in which to create the View.
51480  * @param {String} tpl The template string used to create the markup for each element of the View
51481  * @param {Object} config The configuration properties. These include all the config options of
51482  * {@link Roo.View} plus some specific to this class.<br>
51483  * <p>
51484  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
51485  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
51486  * <p>
51487  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
51488 .x-view-drag-insert-above {
51489         border-top:1px dotted #3366cc;
51490 }
51491 .x-view-drag-insert-below {
51492         border-bottom:1px dotted #3366cc;
51493 }
51494 </code></pre>
51495  * 
51496  */
51497  
51498 Roo.DDView = function(container, tpl, config) {
51499     Roo.DDView.superclass.constructor.apply(this, arguments);
51500     this.getEl().setStyle("outline", "0px none");
51501     this.getEl().unselectable();
51502     if (this.dragGroup) {
51503         this.setDraggable(this.dragGroup.split(","));
51504     }
51505     if (this.dropGroup) {
51506         this.setDroppable(this.dropGroup.split(","));
51507     }
51508     if (this.deletable) {
51509         this.setDeletable();
51510     }
51511     this.isDirtyFlag = false;
51512         this.addEvents({
51513                 "drop" : true
51514         });
51515 };
51516
51517 Roo.extend(Roo.DDView, Roo.View, {
51518 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
51519 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
51520 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
51521 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
51522
51523         isFormField: true,
51524
51525         reset: Roo.emptyFn,
51526         
51527         clearInvalid: Roo.form.Field.prototype.clearInvalid,
51528
51529         validate: function() {
51530                 return true;
51531         },
51532         
51533         destroy: function() {
51534                 this.purgeListeners();
51535                 this.getEl.removeAllListeners();
51536                 this.getEl().remove();
51537                 if (this.dragZone) {
51538                         if (this.dragZone.destroy) {
51539                                 this.dragZone.destroy();
51540                         }
51541                 }
51542                 if (this.dropZone) {
51543                         if (this.dropZone.destroy) {
51544                                 this.dropZone.destroy();
51545                         }
51546                 }
51547         },
51548
51549 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
51550         getName: function() {
51551                 return this.name;
51552         },
51553
51554 /**     Loads the View from a JSON string representing the Records to put into the Store. */
51555         setValue: function(v) {
51556                 if (!this.store) {
51557                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
51558                 }
51559                 var data = {};
51560                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
51561                 this.store.proxy = new Roo.data.MemoryProxy(data);
51562                 this.store.load();
51563         },
51564
51565 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
51566         getValue: function() {
51567                 var result = '(';
51568                 this.store.each(function(rec) {
51569                         result += rec.id + ',';
51570                 });
51571                 return result.substr(0, result.length - 1) + ')';
51572         },
51573         
51574         getIds: function() {
51575                 var i = 0, result = new Array(this.store.getCount());
51576                 this.store.each(function(rec) {
51577                         result[i++] = rec.id;
51578                 });
51579                 return result;
51580         },
51581         
51582         isDirty: function() {
51583                 return this.isDirtyFlag;
51584         },
51585
51586 /**
51587  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
51588  *      whole Element becomes the target, and this causes the drop gesture to append.
51589  */
51590     getTargetFromEvent : function(e) {
51591                 var target = e.getTarget();
51592                 while ((target !== null) && (target.parentNode != this.el.dom)) {
51593                 target = target.parentNode;
51594                 }
51595                 if (!target) {
51596                         target = this.el.dom.lastChild || this.el.dom;
51597                 }
51598                 return target;
51599     },
51600
51601 /**
51602  *      Create the drag data which consists of an object which has the property "ddel" as
51603  *      the drag proxy element. 
51604  */
51605     getDragData : function(e) {
51606         var target = this.findItemFromChild(e.getTarget());
51607                 if(target) {
51608                         this.handleSelection(e);
51609                         var selNodes = this.getSelectedNodes();
51610             var dragData = {
51611                 source: this,
51612                 copy: this.copy || (this.allowCopy && e.ctrlKey),
51613                 nodes: selNodes,
51614                 records: []
51615                         };
51616                         var selectedIndices = this.getSelectedIndexes();
51617                         for (var i = 0; i < selectedIndices.length; i++) {
51618                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
51619                         }
51620                         if (selNodes.length == 1) {
51621                                 dragData.ddel = target.cloneNode(true); // the div element
51622                         } else {
51623                                 var div = document.createElement('div'); // create the multi element drag "ghost"
51624                                 div.className = 'multi-proxy';
51625                                 for (var i = 0, len = selNodes.length; i < len; i++) {
51626                                         div.appendChild(selNodes[i].cloneNode(true));
51627                                 }
51628                                 dragData.ddel = div;
51629                         }
51630             //console.log(dragData)
51631             //console.log(dragData.ddel.innerHTML)
51632                         return dragData;
51633                 }
51634         //console.log('nodragData')
51635                 return false;
51636     },
51637     
51638 /**     Specify to which ddGroup items in this DDView may be dragged. */
51639     setDraggable: function(ddGroup) {
51640         if (ddGroup instanceof Array) {
51641                 Roo.each(ddGroup, this.setDraggable, this);
51642                 return;
51643         }
51644         if (this.dragZone) {
51645                 this.dragZone.addToGroup(ddGroup);
51646         } else {
51647                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
51648                                 containerScroll: true,
51649                                 ddGroup: ddGroup 
51650
51651                         });
51652 //                      Draggability implies selection. DragZone's mousedown selects the element.
51653                         if (!this.multiSelect) { this.singleSelect = true; }
51654
51655 //                      Wire the DragZone's handlers up to methods in *this*
51656                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
51657                 }
51658     },
51659
51660 /**     Specify from which ddGroup this DDView accepts drops. */
51661     setDroppable: function(ddGroup) {
51662         if (ddGroup instanceof Array) {
51663                 Roo.each(ddGroup, this.setDroppable, this);
51664                 return;
51665         }
51666         if (this.dropZone) {
51667                 this.dropZone.addToGroup(ddGroup);
51668         } else {
51669                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
51670                                 containerScroll: true,
51671                                 ddGroup: ddGroup
51672                         });
51673
51674 //                      Wire the DropZone's handlers up to methods in *this*
51675                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
51676                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
51677                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
51678                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
51679                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
51680                 }
51681     },
51682
51683 /**     Decide whether to drop above or below a View node. */
51684     getDropPoint : function(e, n, dd){
51685         if (n == this.el.dom) { return "above"; }
51686                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
51687                 var c = t + (b - t) / 2;
51688                 var y = Roo.lib.Event.getPageY(e);
51689                 if(y <= c) {
51690                         return "above";
51691                 }else{
51692                         return "below";
51693                 }
51694     },
51695
51696     onNodeEnter : function(n, dd, e, data){
51697                 return false;
51698     },
51699     
51700     onNodeOver : function(n, dd, e, data){
51701                 var pt = this.getDropPoint(e, n, dd);
51702                 // set the insert point style on the target node
51703                 var dragElClass = this.dropNotAllowed;
51704                 if (pt) {
51705                         var targetElClass;
51706                         if (pt == "above"){
51707                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
51708                                 targetElClass = "x-view-drag-insert-above";
51709                         } else {
51710                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
51711                                 targetElClass = "x-view-drag-insert-below";
51712                         }
51713                         if (this.lastInsertClass != targetElClass){
51714                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
51715                                 this.lastInsertClass = targetElClass;
51716                         }
51717                 }
51718                 return dragElClass;
51719         },
51720
51721     onNodeOut : function(n, dd, e, data){
51722                 this.removeDropIndicators(n);
51723     },
51724
51725     onNodeDrop : function(n, dd, e, data){
51726         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
51727                 return false;
51728         }
51729         var pt = this.getDropPoint(e, n, dd);
51730                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
51731                 if (pt == "below") { insertAt++; }
51732                 for (var i = 0; i < data.records.length; i++) {
51733                         var r = data.records[i];
51734                         var dup = this.store.getById(r.id);
51735                         if (dup && (dd != this.dragZone)) {
51736                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
51737                         } else {
51738                                 if (data.copy) {
51739                                         this.store.insert(insertAt++, r.copy());
51740                                 } else {
51741                                         data.source.isDirtyFlag = true;
51742                                         r.store.remove(r);
51743                                         this.store.insert(insertAt++, r);
51744                                 }
51745                                 this.isDirtyFlag = true;
51746                         }
51747                 }
51748                 this.dragZone.cachedTarget = null;
51749                 return true;
51750     },
51751
51752     removeDropIndicators : function(n){
51753                 if(n){
51754                         Roo.fly(n).removeClass([
51755                                 "x-view-drag-insert-above",
51756                                 "x-view-drag-insert-below"]);
51757                         this.lastInsertClass = "_noclass";
51758                 }
51759     },
51760
51761 /**
51762  *      Utility method. Add a delete option to the DDView's context menu.
51763  *      @param {String} imageUrl The URL of the "delete" icon image.
51764  */
51765         setDeletable: function(imageUrl) {
51766                 if (!this.singleSelect && !this.multiSelect) {
51767                         this.singleSelect = true;
51768                 }
51769                 var c = this.getContextMenu();
51770                 this.contextMenu.on("itemclick", function(item) {
51771                         switch (item.id) {
51772                                 case "delete":
51773                                         this.remove(this.getSelectedIndexes());
51774                                         break;
51775                         }
51776                 }, this);
51777                 this.contextMenu.add({
51778                         icon: imageUrl,
51779                         id: "delete",
51780                         text: 'Delete'
51781                 });
51782         },
51783         
51784 /**     Return the context menu for this DDView. */
51785         getContextMenu: function() {
51786                 if (!this.contextMenu) {
51787 //                      Create the View's context menu
51788                         this.contextMenu = new Roo.menu.Menu({
51789                                 id: this.id + "-contextmenu"
51790                         });
51791                         this.el.on("contextmenu", this.showContextMenu, this);
51792                 }
51793                 return this.contextMenu;
51794         },
51795         
51796         disableContextMenu: function() {
51797                 if (this.contextMenu) {
51798                         this.el.un("contextmenu", this.showContextMenu, this);
51799                 }
51800         },
51801
51802         showContextMenu: function(e, item) {
51803         item = this.findItemFromChild(e.getTarget());
51804                 if (item) {
51805                         e.stopEvent();
51806                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
51807                         this.contextMenu.showAt(e.getXY());
51808             }
51809     },
51810
51811 /**
51812  *      Remove {@link Roo.data.Record}s at the specified indices.
51813  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
51814  */
51815     remove: function(selectedIndices) {
51816                 selectedIndices = [].concat(selectedIndices);
51817                 for (var i = 0; i < selectedIndices.length; i++) {
51818                         var rec = this.store.getAt(selectedIndices[i]);
51819                         this.store.remove(rec);
51820                 }
51821     },
51822
51823 /**
51824  *      Double click fires the event, but also, if this is draggable, and there is only one other
51825  *      related DropZone, it transfers the selected node.
51826  */
51827     onDblClick : function(e){
51828         var item = this.findItemFromChild(e.getTarget());
51829         if(item){
51830             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
51831                 return false;
51832             }
51833             if (this.dragGroup) {
51834                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
51835                     while (targets.indexOf(this.dropZone) > -1) {
51836                             targets.remove(this.dropZone);
51837                                 }
51838                     if (targets.length == 1) {
51839                                         this.dragZone.cachedTarget = null;
51840                         var el = Roo.get(targets[0].getEl());
51841                         var box = el.getBox(true);
51842                         targets[0].onNodeDrop(el.dom, {
51843                                 target: el.dom,
51844                                 xy: [box.x, box.y + box.height - 1]
51845                         }, null, this.getDragData(e));
51846                     }
51847                 }
51848         }
51849     },
51850     
51851     handleSelection: function(e) {
51852                 this.dragZone.cachedTarget = null;
51853         var item = this.findItemFromChild(e.getTarget());
51854         if (!item) {
51855                 this.clearSelections(true);
51856                 return;
51857         }
51858                 if (item && (this.multiSelect || this.singleSelect)){
51859                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
51860                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
51861                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
51862                                 this.unselect(item);
51863                         } else {
51864                                 this.select(item, this.multiSelect && e.ctrlKey);
51865                                 this.lastSelection = item;
51866                         }
51867                 }
51868     },
51869
51870     onItemClick : function(item, index, e){
51871                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
51872                         return false;
51873                 }
51874                 return true;
51875     },
51876
51877     unselect : function(nodeInfo, suppressEvent){
51878                 var node = this.getNode(nodeInfo);
51879                 if(node && this.isSelected(node)){
51880                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
51881                                 Roo.fly(node).removeClass(this.selectedClass);
51882                                 this.selections.remove(node);
51883                                 if(!suppressEvent){
51884                                         this.fireEvent("selectionchange", this, this.selections);
51885                                 }
51886                         }
51887                 }
51888     }
51889 });
51890 /*
51891  * Based on:
51892  * Ext JS Library 1.1.1
51893  * Copyright(c) 2006-2007, Ext JS, LLC.
51894  *
51895  * Originally Released Under LGPL - original licence link has changed is not relivant.
51896  *
51897  * Fork - LGPL
51898  * <script type="text/javascript">
51899  */
51900  
51901 /**
51902  * @class Roo.LayoutManager
51903  * @extends Roo.util.Observable
51904  * Base class for layout managers.
51905  */
51906 Roo.LayoutManager = function(container, config){
51907     Roo.LayoutManager.superclass.constructor.call(this);
51908     this.el = Roo.get(container);
51909     // ie scrollbar fix
51910     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
51911         document.body.scroll = "no";
51912     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
51913         this.el.position('relative');
51914     }
51915     this.id = this.el.id;
51916     this.el.addClass("x-layout-container");
51917     /** false to disable window resize monitoring @type Boolean */
51918     this.monitorWindowResize = true;
51919     this.regions = {};
51920     this.addEvents({
51921         /**
51922          * @event layout
51923          * Fires when a layout is performed. 
51924          * @param {Roo.LayoutManager} this
51925          */
51926         "layout" : true,
51927         /**
51928          * @event regionresized
51929          * Fires when the user resizes a region. 
51930          * @param {Roo.LayoutRegion} region The resized region
51931          * @param {Number} newSize The new size (width for east/west, height for north/south)
51932          */
51933         "regionresized" : true,
51934         /**
51935          * @event regioncollapsed
51936          * Fires when a region is collapsed. 
51937          * @param {Roo.LayoutRegion} region The collapsed region
51938          */
51939         "regioncollapsed" : true,
51940         /**
51941          * @event regionexpanded
51942          * Fires when a region is expanded.  
51943          * @param {Roo.LayoutRegion} region The expanded region
51944          */
51945         "regionexpanded" : true
51946     });
51947     this.updating = false;
51948     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
51949 };
51950
51951 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
51952     /**
51953      * Returns true if this layout is currently being updated
51954      * @return {Boolean}
51955      */
51956     isUpdating : function(){
51957         return this.updating; 
51958     },
51959     
51960     /**
51961      * Suspend the LayoutManager from doing auto-layouts while
51962      * making multiple add or remove calls
51963      */
51964     beginUpdate : function(){
51965         this.updating = true;    
51966     },
51967     
51968     /**
51969      * Restore auto-layouts and optionally disable the manager from performing a layout
51970      * @param {Boolean} noLayout true to disable a layout update 
51971      */
51972     endUpdate : function(noLayout){
51973         this.updating = false;
51974         if(!noLayout){
51975             this.layout();
51976         }    
51977     },
51978     
51979     layout: function(){
51980         
51981     },
51982     
51983     onRegionResized : function(region, newSize){
51984         this.fireEvent("regionresized", region, newSize);
51985         this.layout();
51986     },
51987     
51988     onRegionCollapsed : function(region){
51989         this.fireEvent("regioncollapsed", region);
51990     },
51991     
51992     onRegionExpanded : function(region){
51993         this.fireEvent("regionexpanded", region);
51994     },
51995         
51996     /**
51997      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
51998      * performs box-model adjustments.
51999      * @return {Object} The size as an object {width: (the width), height: (the height)}
52000      */
52001     getViewSize : function(){
52002         var size;
52003         if(this.el.dom != document.body){
52004             size = this.el.getSize();
52005         }else{
52006             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
52007         }
52008         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
52009         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
52010         return size;
52011     },
52012     
52013     /**
52014      * Returns the Element this layout is bound to.
52015      * @return {Roo.Element}
52016      */
52017     getEl : function(){
52018         return this.el;
52019     },
52020     
52021     /**
52022      * Returns the specified region.
52023      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
52024      * @return {Roo.LayoutRegion}
52025      */
52026     getRegion : function(target){
52027         return this.regions[target.toLowerCase()];
52028     },
52029     
52030     onWindowResize : function(){
52031         if(this.monitorWindowResize){
52032             this.layout();
52033         }
52034     }
52035 });/*
52036  * Based on:
52037  * Ext JS Library 1.1.1
52038  * Copyright(c) 2006-2007, Ext JS, LLC.
52039  *
52040  * Originally Released Under LGPL - original licence link has changed is not relivant.
52041  *
52042  * Fork - LGPL
52043  * <script type="text/javascript">
52044  */
52045 /**
52046  * @class Roo.BorderLayout
52047  * @extends Roo.LayoutManager
52048  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
52049  * please see: <br><br>
52050  * <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>
52051  * <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>
52052  * Example:
52053  <pre><code>
52054  var layout = new Roo.BorderLayout(document.body, {
52055     north: {
52056         initialSize: 25,
52057         titlebar: false
52058     },
52059     west: {
52060         split:true,
52061         initialSize: 200,
52062         minSize: 175,
52063         maxSize: 400,
52064         titlebar: true,
52065         collapsible: true
52066     },
52067     east: {
52068         split:true,
52069         initialSize: 202,
52070         minSize: 175,
52071         maxSize: 400,
52072         titlebar: true,
52073         collapsible: true
52074     },
52075     south: {
52076         split:true,
52077         initialSize: 100,
52078         minSize: 100,
52079         maxSize: 200,
52080         titlebar: true,
52081         collapsible: true
52082     },
52083     center: {
52084         titlebar: true,
52085         autoScroll:true,
52086         resizeTabs: true,
52087         minTabWidth: 50,
52088         preferredTabWidth: 150
52089     }
52090 });
52091
52092 // shorthand
52093 var CP = Roo.ContentPanel;
52094
52095 layout.beginUpdate();
52096 layout.add("north", new CP("north", "North"));
52097 layout.add("south", new CP("south", {title: "South", closable: true}));
52098 layout.add("west", new CP("west", {title: "West"}));
52099 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
52100 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
52101 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
52102 layout.getRegion("center").showPanel("center1");
52103 layout.endUpdate();
52104 </code></pre>
52105
52106 <b>The container the layout is rendered into can be either the body element or any other element.
52107 If it is not the body element, the container needs to either be an absolute positioned element,
52108 or you will need to add "position:relative" to the css of the container.  You will also need to specify
52109 the container size if it is not the body element.</b>
52110
52111 * @constructor
52112 * Create a new BorderLayout
52113 * @param {String/HTMLElement/Element} container The container this layout is bound to
52114 * @param {Object} config Configuration options
52115  */
52116 Roo.BorderLayout = function(container, config){
52117     config = config || {};
52118     Roo.BorderLayout.superclass.constructor.call(this, container, config);
52119     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
52120     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
52121         var target = this.factory.validRegions[i];
52122         if(config[target]){
52123             this.addRegion(target, config[target]);
52124         }
52125     }
52126 };
52127
52128 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
52129     /**
52130      * Creates and adds a new region if it doesn't already exist.
52131      * @param {String} target The target region key (north, south, east, west or center).
52132      * @param {Object} config The regions config object
52133      * @return {BorderLayoutRegion} The new region
52134      */
52135     addRegion : function(target, config){
52136         if(!this.regions[target]){
52137             var r = this.factory.create(target, this, config);
52138             this.bindRegion(target, r);
52139         }
52140         return this.regions[target];
52141     },
52142
52143     // private (kinda)
52144     bindRegion : function(name, r){
52145         this.regions[name] = r;
52146         r.on("visibilitychange", this.layout, this);
52147         r.on("paneladded", this.layout, this);
52148         r.on("panelremoved", this.layout, this);
52149         r.on("invalidated", this.layout, this);
52150         r.on("resized", this.onRegionResized, this);
52151         r.on("collapsed", this.onRegionCollapsed, this);
52152         r.on("expanded", this.onRegionExpanded, this);
52153     },
52154
52155     /**
52156      * Performs a layout update.
52157      */
52158     layout : function(){
52159         if(this.updating) {
52160             return;
52161         }
52162         var size = this.getViewSize();
52163         var w = size.width;
52164         var h = size.height;
52165         var centerW = w;
52166         var centerH = h;
52167         var centerY = 0;
52168         var centerX = 0;
52169         //var x = 0, y = 0;
52170
52171         var rs = this.regions;
52172         var north = rs["north"];
52173         var south = rs["south"]; 
52174         var west = rs["west"];
52175         var east = rs["east"];
52176         var center = rs["center"];
52177         //if(this.hideOnLayout){ // not supported anymore
52178             //c.el.setStyle("display", "none");
52179         //}
52180         if(north && north.isVisible()){
52181             var b = north.getBox();
52182             var m = north.getMargins();
52183             b.width = w - (m.left+m.right);
52184             b.x = m.left;
52185             b.y = m.top;
52186             centerY = b.height + b.y + m.bottom;
52187             centerH -= centerY;
52188             north.updateBox(this.safeBox(b));
52189         }
52190         if(south && south.isVisible()){
52191             var b = south.getBox();
52192             var m = south.getMargins();
52193             b.width = w - (m.left+m.right);
52194             b.x = m.left;
52195             var totalHeight = (b.height + m.top + m.bottom);
52196             b.y = h - totalHeight + m.top;
52197             centerH -= totalHeight;
52198             south.updateBox(this.safeBox(b));
52199         }
52200         if(west && west.isVisible()){
52201             var b = west.getBox();
52202             var m = west.getMargins();
52203             b.height = centerH - (m.top+m.bottom);
52204             b.x = m.left;
52205             b.y = centerY + m.top;
52206             var totalWidth = (b.width + m.left + m.right);
52207             centerX += totalWidth;
52208             centerW -= totalWidth;
52209             west.updateBox(this.safeBox(b));
52210         }
52211         if(east && east.isVisible()){
52212             var b = east.getBox();
52213             var m = east.getMargins();
52214             b.height = centerH - (m.top+m.bottom);
52215             var totalWidth = (b.width + m.left + m.right);
52216             b.x = w - totalWidth + m.left;
52217             b.y = centerY + m.top;
52218             centerW -= totalWidth;
52219             east.updateBox(this.safeBox(b));
52220         }
52221         if(center){
52222             var m = center.getMargins();
52223             var centerBox = {
52224                 x: centerX + m.left,
52225                 y: centerY + m.top,
52226                 width: centerW - (m.left+m.right),
52227                 height: centerH - (m.top+m.bottom)
52228             };
52229             //if(this.hideOnLayout){
52230                 //center.el.setStyle("display", "block");
52231             //}
52232             center.updateBox(this.safeBox(centerBox));
52233         }
52234         this.el.repaint();
52235         this.fireEvent("layout", this);
52236     },
52237
52238     // private
52239     safeBox : function(box){
52240         box.width = Math.max(0, box.width);
52241         box.height = Math.max(0, box.height);
52242         return box;
52243     },
52244
52245     /**
52246      * Adds a ContentPanel (or subclass) to this layout.
52247      * @param {String} target The target region key (north, south, east, west or center).
52248      * @param {Roo.ContentPanel} panel The panel to add
52249      * @return {Roo.ContentPanel} The added panel
52250      */
52251     add : function(target, panel){
52252          
52253         target = target.toLowerCase();
52254         return this.regions[target].add(panel);
52255     },
52256
52257     /**
52258      * Remove a ContentPanel (or subclass) to this layout.
52259      * @param {String} target The target region key (north, south, east, west or center).
52260      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
52261      * @return {Roo.ContentPanel} The removed panel
52262      */
52263     remove : function(target, panel){
52264         target = target.toLowerCase();
52265         return this.regions[target].remove(panel);
52266     },
52267
52268     /**
52269      * Searches all regions for a panel with the specified id
52270      * @param {String} panelId
52271      * @return {Roo.ContentPanel} The panel or null if it wasn't found
52272      */
52273     findPanel : function(panelId){
52274         var rs = this.regions;
52275         for(var target in rs){
52276             if(typeof rs[target] != "function"){
52277                 var p = rs[target].getPanel(panelId);
52278                 if(p){
52279                     return p;
52280                 }
52281             }
52282         }
52283         return null;
52284     },
52285
52286     /**
52287      * Searches all regions for a panel with the specified id and activates (shows) it.
52288      * @param {String/ContentPanel} panelId The panels id or the panel itself
52289      * @return {Roo.ContentPanel} The shown panel or null
52290      */
52291     showPanel : function(panelId) {
52292       var rs = this.regions;
52293       for(var target in rs){
52294          var r = rs[target];
52295          if(typeof r != "function"){
52296             if(r.hasPanel(panelId)){
52297                return r.showPanel(panelId);
52298             }
52299          }
52300       }
52301       return null;
52302    },
52303
52304    /**
52305      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
52306      * @param {Roo.state.Provider} provider (optional) An alternate state provider
52307      */
52308     restoreState : function(provider){
52309         if(!provider){
52310             provider = Roo.state.Manager;
52311         }
52312         var sm = new Roo.LayoutStateManager();
52313         sm.init(this, provider);
52314     },
52315
52316     /**
52317      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
52318      * object should contain properties for each region to add ContentPanels to, and each property's value should be
52319      * a valid ContentPanel config object.  Example:
52320      * <pre><code>
52321 // Create the main layout
52322 var layout = new Roo.BorderLayout('main-ct', {
52323     west: {
52324         split:true,
52325         minSize: 175,
52326         titlebar: true
52327     },
52328     center: {
52329         title:'Components'
52330     }
52331 }, 'main-ct');
52332
52333 // Create and add multiple ContentPanels at once via configs
52334 layout.batchAdd({
52335    west: {
52336        id: 'source-files',
52337        autoCreate:true,
52338        title:'Ext Source Files',
52339        autoScroll:true,
52340        fitToFrame:true
52341    },
52342    center : {
52343        el: cview,
52344        autoScroll:true,
52345        fitToFrame:true,
52346        toolbar: tb,
52347        resizeEl:'cbody'
52348    }
52349 });
52350 </code></pre>
52351      * @param {Object} regions An object containing ContentPanel configs by region name
52352      */
52353     batchAdd : function(regions){
52354         this.beginUpdate();
52355         for(var rname in regions){
52356             var lr = this.regions[rname];
52357             if(lr){
52358                 this.addTypedPanels(lr, regions[rname]);
52359             }
52360         }
52361         this.endUpdate();
52362     },
52363
52364     // private
52365     addTypedPanels : function(lr, ps){
52366         if(typeof ps == 'string'){
52367             lr.add(new Roo.ContentPanel(ps));
52368         }
52369         else if(ps instanceof Array){
52370             for(var i =0, len = ps.length; i < len; i++){
52371                 this.addTypedPanels(lr, ps[i]);
52372             }
52373         }
52374         else if(!ps.events){ // raw config?
52375             var el = ps.el;
52376             delete ps.el; // prevent conflict
52377             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
52378         }
52379         else {  // panel object assumed!
52380             lr.add(ps);
52381         }
52382     },
52383     /**
52384      * Adds a xtype elements to the layout.
52385      * <pre><code>
52386
52387 layout.addxtype({
52388        xtype : 'ContentPanel',
52389        region: 'west',
52390        items: [ .... ]
52391    }
52392 );
52393
52394 layout.addxtype({
52395         xtype : 'NestedLayoutPanel',
52396         region: 'west',
52397         layout: {
52398            center: { },
52399            west: { }   
52400         },
52401         items : [ ... list of content panels or nested layout panels.. ]
52402    }
52403 );
52404 </code></pre>
52405      * @param {Object} cfg Xtype definition of item to add.
52406      */
52407     addxtype : function(cfg)
52408     {
52409         // basically accepts a pannel...
52410         // can accept a layout region..!?!?
52411         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
52412         
52413         if (!cfg.xtype.match(/Panel$/)) {
52414             return false;
52415         }
52416         var ret = false;
52417         
52418         if (typeof(cfg.region) == 'undefined') {
52419             Roo.log("Failed to add Panel, region was not set");
52420             Roo.log(cfg);
52421             return false;
52422         }
52423         var region = cfg.region;
52424         delete cfg.region;
52425         
52426           
52427         var xitems = [];
52428         if (cfg.items) {
52429             xitems = cfg.items;
52430             delete cfg.items;
52431         }
52432         var nb = false;
52433         
52434         switch(cfg.xtype) 
52435         {
52436             case 'ContentPanel':  // ContentPanel (el, cfg)
52437             case 'ScrollPanel':  // ContentPanel (el, cfg)
52438             case 'ViewPanel': 
52439                 if(cfg.autoCreate) {
52440                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
52441                 } else {
52442                     var el = this.el.createChild();
52443                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
52444                 }
52445                 
52446                 this.add(region, ret);
52447                 break;
52448             
52449             
52450             case 'TreePanel': // our new panel!
52451                 cfg.el = this.el.createChild();
52452                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
52453                 this.add(region, ret);
52454                 break;
52455             
52456             case 'NestedLayoutPanel': 
52457                 // create a new Layout (which is  a Border Layout...
52458                 var el = this.el.createChild();
52459                 var clayout = cfg.layout;
52460                 delete cfg.layout;
52461                 clayout.items   = clayout.items  || [];
52462                 // replace this exitems with the clayout ones..
52463                 xitems = clayout.items;
52464                  
52465                 
52466                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
52467                     cfg.background = false;
52468                 }
52469                 var layout = new Roo.BorderLayout(el, clayout);
52470                 
52471                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
52472                 //console.log('adding nested layout panel '  + cfg.toSource());
52473                 this.add(region, ret);
52474                 nb = {}; /// find first...
52475                 break;
52476                 
52477             case 'GridPanel': 
52478             
52479                 // needs grid and region
52480                 
52481                 //var el = this.getRegion(region).el.createChild();
52482                 var el = this.el.createChild();
52483                 // create the grid first...
52484                 
52485                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
52486                 delete cfg.grid;
52487                 if (region == 'center' && this.active ) {
52488                     cfg.background = false;
52489                 }
52490                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
52491                 
52492                 this.add(region, ret);
52493                 if (cfg.background) {
52494                     ret.on('activate', function(gp) {
52495                         if (!gp.grid.rendered) {
52496                             gp.grid.render();
52497                         }
52498                     });
52499                 } else {
52500                     grid.render();
52501                 }
52502                 break;
52503            
52504            
52505            
52506                 
52507                 
52508                 
52509             default:
52510                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
52511                     
52512                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
52513                     this.add(region, ret);
52514                 } else {
52515                 
52516                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
52517                     return null;
52518                 }
52519                 
52520              // GridPanel (grid, cfg)
52521             
52522         }
52523         this.beginUpdate();
52524         // add children..
52525         var region = '';
52526         var abn = {};
52527         Roo.each(xitems, function(i)  {
52528             region = nb && i.region ? i.region : false;
52529             
52530             var add = ret.addxtype(i);
52531            
52532             if (region) {
52533                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
52534                 if (!i.background) {
52535                     abn[region] = nb[region] ;
52536                 }
52537             }
52538             
52539         });
52540         this.endUpdate();
52541
52542         // make the last non-background panel active..
52543         //if (nb) { Roo.log(abn); }
52544         if (nb) {
52545             
52546             for(var r in abn) {
52547                 region = this.getRegion(r);
52548                 if (region) {
52549                     // tried using nb[r], but it does not work..
52550                      
52551                     region.showPanel(abn[r]);
52552                    
52553                 }
52554             }
52555         }
52556         return ret;
52557         
52558     }
52559 });
52560
52561 /**
52562  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
52563  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
52564  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
52565  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
52566  * <pre><code>
52567 // shorthand
52568 var CP = Roo.ContentPanel;
52569
52570 var layout = Roo.BorderLayout.create({
52571     north: {
52572         initialSize: 25,
52573         titlebar: false,
52574         panels: [new CP("north", "North")]
52575     },
52576     west: {
52577         split:true,
52578         initialSize: 200,
52579         minSize: 175,
52580         maxSize: 400,
52581         titlebar: true,
52582         collapsible: true,
52583         panels: [new CP("west", {title: "West"})]
52584     },
52585     east: {
52586         split:true,
52587         initialSize: 202,
52588         minSize: 175,
52589         maxSize: 400,
52590         titlebar: true,
52591         collapsible: true,
52592         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
52593     },
52594     south: {
52595         split:true,
52596         initialSize: 100,
52597         minSize: 100,
52598         maxSize: 200,
52599         titlebar: true,
52600         collapsible: true,
52601         panels: [new CP("south", {title: "South", closable: true})]
52602     },
52603     center: {
52604         titlebar: true,
52605         autoScroll:true,
52606         resizeTabs: true,
52607         minTabWidth: 50,
52608         preferredTabWidth: 150,
52609         panels: [
52610             new CP("center1", {title: "Close Me", closable: true}),
52611             new CP("center2", {title: "Center Panel", closable: false})
52612         ]
52613     }
52614 }, document.body);
52615
52616 layout.getRegion("center").showPanel("center1");
52617 </code></pre>
52618  * @param config
52619  * @param targetEl
52620  */
52621 Roo.BorderLayout.create = function(config, targetEl){
52622     var layout = new Roo.BorderLayout(targetEl || document.body, config);
52623     layout.beginUpdate();
52624     var regions = Roo.BorderLayout.RegionFactory.validRegions;
52625     for(var j = 0, jlen = regions.length; j < jlen; j++){
52626         var lr = regions[j];
52627         if(layout.regions[lr] && config[lr].panels){
52628             var r = layout.regions[lr];
52629             var ps = config[lr].panels;
52630             layout.addTypedPanels(r, ps);
52631         }
52632     }
52633     layout.endUpdate();
52634     return layout;
52635 };
52636
52637 // private
52638 Roo.BorderLayout.RegionFactory = {
52639     // private
52640     validRegions : ["north","south","east","west","center"],
52641
52642     // private
52643     create : function(target, mgr, config){
52644         target = target.toLowerCase();
52645         if(config.lightweight || config.basic){
52646             return new Roo.BasicLayoutRegion(mgr, config, target);
52647         }
52648         switch(target){
52649             case "north":
52650                 return new Roo.NorthLayoutRegion(mgr, config);
52651             case "south":
52652                 return new Roo.SouthLayoutRegion(mgr, config);
52653             case "east":
52654                 return new Roo.EastLayoutRegion(mgr, config);
52655             case "west":
52656                 return new Roo.WestLayoutRegion(mgr, config);
52657             case "center":
52658                 return new Roo.CenterLayoutRegion(mgr, config);
52659         }
52660         throw 'Layout region "'+target+'" not supported.';
52661     }
52662 };/*
52663  * Based on:
52664  * Ext JS Library 1.1.1
52665  * Copyright(c) 2006-2007, Ext JS, LLC.
52666  *
52667  * Originally Released Under LGPL - original licence link has changed is not relivant.
52668  *
52669  * Fork - LGPL
52670  * <script type="text/javascript">
52671  */
52672  
52673 /**
52674  * @class Roo.BasicLayoutRegion
52675  * @extends Roo.util.Observable
52676  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
52677  * and does not have a titlebar, tabs or any other features. All it does is size and position 
52678  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
52679  */
52680 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
52681     this.mgr = mgr;
52682     this.position  = pos;
52683     this.events = {
52684         /**
52685          * @scope Roo.BasicLayoutRegion
52686          */
52687         
52688         /**
52689          * @event beforeremove
52690          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
52691          * @param {Roo.LayoutRegion} this
52692          * @param {Roo.ContentPanel} panel The panel
52693          * @param {Object} e The cancel event object
52694          */
52695         "beforeremove" : true,
52696         /**
52697          * @event invalidated
52698          * Fires when the layout for this region is changed.
52699          * @param {Roo.LayoutRegion} this
52700          */
52701         "invalidated" : true,
52702         /**
52703          * @event visibilitychange
52704          * Fires when this region is shown or hidden 
52705          * @param {Roo.LayoutRegion} this
52706          * @param {Boolean} visibility true or false
52707          */
52708         "visibilitychange" : true,
52709         /**
52710          * @event paneladded
52711          * Fires when a panel is added. 
52712          * @param {Roo.LayoutRegion} this
52713          * @param {Roo.ContentPanel} panel The panel
52714          */
52715         "paneladded" : true,
52716         /**
52717          * @event panelremoved
52718          * Fires when a panel is removed. 
52719          * @param {Roo.LayoutRegion} this
52720          * @param {Roo.ContentPanel} panel The panel
52721          */
52722         "panelremoved" : true,
52723         /**
52724          * @event beforecollapse
52725          * Fires when this region before collapse.
52726          * @param {Roo.LayoutRegion} this
52727          */
52728         "beforecollapse" : true,
52729         /**
52730          * @event collapsed
52731          * Fires when this region is collapsed.
52732          * @param {Roo.LayoutRegion} this
52733          */
52734         "collapsed" : true,
52735         /**
52736          * @event expanded
52737          * Fires when this region is expanded.
52738          * @param {Roo.LayoutRegion} this
52739          */
52740         "expanded" : true,
52741         /**
52742          * @event slideshow
52743          * Fires when this region is slid into view.
52744          * @param {Roo.LayoutRegion} this
52745          */
52746         "slideshow" : true,
52747         /**
52748          * @event slidehide
52749          * Fires when this region slides out of view. 
52750          * @param {Roo.LayoutRegion} this
52751          */
52752         "slidehide" : true,
52753         /**
52754          * @event panelactivated
52755          * Fires when a panel is activated. 
52756          * @param {Roo.LayoutRegion} this
52757          * @param {Roo.ContentPanel} panel The activated panel
52758          */
52759         "panelactivated" : true,
52760         /**
52761          * @event resized
52762          * Fires when the user resizes this region. 
52763          * @param {Roo.LayoutRegion} this
52764          * @param {Number} newSize The new size (width for east/west, height for north/south)
52765          */
52766         "resized" : true
52767     };
52768     /** A collection of panels in this region. @type Roo.util.MixedCollection */
52769     this.panels = new Roo.util.MixedCollection();
52770     this.panels.getKey = this.getPanelId.createDelegate(this);
52771     this.box = null;
52772     this.activePanel = null;
52773     // ensure listeners are added...
52774     
52775     if (config.listeners || config.events) {
52776         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
52777             listeners : config.listeners || {},
52778             events : config.events || {}
52779         });
52780     }
52781     
52782     if(skipConfig !== true){
52783         this.applyConfig(config);
52784     }
52785 };
52786
52787 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
52788     getPanelId : function(p){
52789         return p.getId();
52790     },
52791     
52792     applyConfig : function(config){
52793         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
52794         this.config = config;
52795         
52796     },
52797     
52798     /**
52799      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
52800      * the width, for horizontal (north, south) the height.
52801      * @param {Number} newSize The new width or height
52802      */
52803     resizeTo : function(newSize){
52804         var el = this.el ? this.el :
52805                  (this.activePanel ? this.activePanel.getEl() : null);
52806         if(el){
52807             switch(this.position){
52808                 case "east":
52809                 case "west":
52810                     el.setWidth(newSize);
52811                     this.fireEvent("resized", this, newSize);
52812                 break;
52813                 case "north":
52814                 case "south":
52815                     el.setHeight(newSize);
52816                     this.fireEvent("resized", this, newSize);
52817                 break;                
52818             }
52819         }
52820     },
52821     
52822     getBox : function(){
52823         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
52824     },
52825     
52826     getMargins : function(){
52827         return this.margins;
52828     },
52829     
52830     updateBox : function(box){
52831         this.box = box;
52832         var el = this.activePanel.getEl();
52833         el.dom.style.left = box.x + "px";
52834         el.dom.style.top = box.y + "px";
52835         this.activePanel.setSize(box.width, box.height);
52836     },
52837     
52838     /**
52839      * Returns the container element for this region.
52840      * @return {Roo.Element}
52841      */
52842     getEl : function(){
52843         return this.activePanel;
52844     },
52845     
52846     /**
52847      * Returns true if this region is currently visible.
52848      * @return {Boolean}
52849      */
52850     isVisible : function(){
52851         return this.activePanel ? true : false;
52852     },
52853     
52854     setActivePanel : function(panel){
52855         panel = this.getPanel(panel);
52856         if(this.activePanel && this.activePanel != panel){
52857             this.activePanel.setActiveState(false);
52858             this.activePanel.getEl().setLeftTop(-10000,-10000);
52859         }
52860         this.activePanel = panel;
52861         panel.setActiveState(true);
52862         if(this.box){
52863             panel.setSize(this.box.width, this.box.height);
52864         }
52865         this.fireEvent("panelactivated", this, panel);
52866         this.fireEvent("invalidated");
52867     },
52868     
52869     /**
52870      * Show the specified panel.
52871      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
52872      * @return {Roo.ContentPanel} The shown panel or null
52873      */
52874     showPanel : function(panel){
52875         if(panel = this.getPanel(panel)){
52876             this.setActivePanel(panel);
52877         }
52878         return panel;
52879     },
52880     
52881     /**
52882      * Get the active panel for this region.
52883      * @return {Roo.ContentPanel} The active panel or null
52884      */
52885     getActivePanel : function(){
52886         return this.activePanel;
52887     },
52888     
52889     /**
52890      * Add the passed ContentPanel(s)
52891      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
52892      * @return {Roo.ContentPanel} The panel added (if only one was added)
52893      */
52894     add : function(panel){
52895         if(arguments.length > 1){
52896             for(var i = 0, len = arguments.length; i < len; i++) {
52897                 this.add(arguments[i]);
52898             }
52899             return null;
52900         }
52901         if(this.hasPanel(panel)){
52902             this.showPanel(panel);
52903             return panel;
52904         }
52905         var el = panel.getEl();
52906         if(el.dom.parentNode != this.mgr.el.dom){
52907             this.mgr.el.dom.appendChild(el.dom);
52908         }
52909         if(panel.setRegion){
52910             panel.setRegion(this);
52911         }
52912         this.panels.add(panel);
52913         el.setStyle("position", "absolute");
52914         if(!panel.background){
52915             this.setActivePanel(panel);
52916             if(this.config.initialSize && this.panels.getCount()==1){
52917                 this.resizeTo(this.config.initialSize);
52918             }
52919         }
52920         this.fireEvent("paneladded", this, panel);
52921         return panel;
52922     },
52923     
52924     /**
52925      * Returns true if the panel is in this region.
52926      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
52927      * @return {Boolean}
52928      */
52929     hasPanel : function(panel){
52930         if(typeof panel == "object"){ // must be panel obj
52931             panel = panel.getId();
52932         }
52933         return this.getPanel(panel) ? true : false;
52934     },
52935     
52936     /**
52937      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
52938      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
52939      * @param {Boolean} preservePanel Overrides the config preservePanel option
52940      * @return {Roo.ContentPanel} The panel that was removed
52941      */
52942     remove : function(panel, preservePanel){
52943         panel = this.getPanel(panel);
52944         if(!panel){
52945             return null;
52946         }
52947         var e = {};
52948         this.fireEvent("beforeremove", this, panel, e);
52949         if(e.cancel === true){
52950             return null;
52951         }
52952         var panelId = panel.getId();
52953         this.panels.removeKey(panelId);
52954         return panel;
52955     },
52956     
52957     /**
52958      * Returns the panel specified or null if it's not in this region.
52959      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
52960      * @return {Roo.ContentPanel}
52961      */
52962     getPanel : function(id){
52963         if(typeof id == "object"){ // must be panel obj
52964             return id;
52965         }
52966         return this.panels.get(id);
52967     },
52968     
52969     /**
52970      * Returns this regions position (north/south/east/west/center).
52971      * @return {String} 
52972      */
52973     getPosition: function(){
52974         return this.position;    
52975     }
52976 });/*
52977  * Based on:
52978  * Ext JS Library 1.1.1
52979  * Copyright(c) 2006-2007, Ext JS, LLC.
52980  *
52981  * Originally Released Under LGPL - original licence link has changed is not relivant.
52982  *
52983  * Fork - LGPL
52984  * <script type="text/javascript">
52985  */
52986  
52987 /**
52988  * @class Roo.LayoutRegion
52989  * @extends Roo.BasicLayoutRegion
52990  * This class represents a region in a layout manager.
52991  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
52992  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
52993  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
52994  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
52995  * @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})
52996  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
52997  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
52998  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
52999  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
53000  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
53001  * @cfg {String}    title           The title for the region (overrides panel titles)
53002  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
53003  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
53004  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
53005  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
53006  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
53007  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
53008  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
53009  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
53010  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
53011  * @cfg {Boolean}   showPin         True to show a pin button
53012  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
53013  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
53014  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
53015  * @cfg {Number}    width           For East/West panels
53016  * @cfg {Number}    height          For North/South panels
53017  * @cfg {Boolean}   split           To show the splitter
53018  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
53019  */
53020 Roo.LayoutRegion = function(mgr, config, pos){
53021     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
53022     var dh = Roo.DomHelper;
53023     /** This region's container element 
53024     * @type Roo.Element */
53025     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
53026     /** This region's title element 
53027     * @type Roo.Element */
53028
53029     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
53030         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
53031         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
53032     ]}, true);
53033     this.titleEl.enableDisplayMode();
53034     /** This region's title text element 
53035     * @type HTMLElement */
53036     this.titleTextEl = this.titleEl.dom.firstChild;
53037     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
53038     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
53039     this.closeBtn.enableDisplayMode();
53040     this.closeBtn.on("click", this.closeClicked, this);
53041     this.closeBtn.hide();
53042
53043     this.createBody(config);
53044     this.visible = true;
53045     this.collapsed = false;
53046
53047     if(config.hideWhenEmpty){
53048         this.hide();
53049         this.on("paneladded", this.validateVisibility, this);
53050         this.on("panelremoved", this.validateVisibility, this);
53051     }
53052     this.applyConfig(config);
53053 };
53054
53055 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
53056
53057     createBody : function(){
53058         /** This region's body element 
53059         * @type Roo.Element */
53060         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
53061     },
53062
53063     applyConfig : function(c){
53064         if(c.collapsible && this.position != "center" && !this.collapsedEl){
53065             var dh = Roo.DomHelper;
53066             if(c.titlebar !== false){
53067                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
53068                 this.collapseBtn.on("click", this.collapse, this);
53069                 this.collapseBtn.enableDisplayMode();
53070
53071                 if(c.showPin === true || this.showPin){
53072                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
53073                     this.stickBtn.enableDisplayMode();
53074                     this.stickBtn.on("click", this.expand, this);
53075                     this.stickBtn.hide();
53076                 }
53077             }
53078             /** This region's collapsed element
53079             * @type Roo.Element */
53080             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
53081                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
53082             ]}, true);
53083             if(c.floatable !== false){
53084                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
53085                this.collapsedEl.on("click", this.collapseClick, this);
53086             }
53087
53088             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
53089                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
53090                    id: "message", unselectable: "on", style:{"float":"left"}});
53091                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
53092              }
53093             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
53094             this.expandBtn.on("click", this.expand, this);
53095         }
53096         if(this.collapseBtn){
53097             this.collapseBtn.setVisible(c.collapsible == true);
53098         }
53099         this.cmargins = c.cmargins || this.cmargins ||
53100                          (this.position == "west" || this.position == "east" ?
53101                              {top: 0, left: 2, right:2, bottom: 0} :
53102                              {top: 2, left: 0, right:0, bottom: 2});
53103         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
53104         this.bottomTabs = c.tabPosition != "top";
53105         this.autoScroll = c.autoScroll || false;
53106         if(this.autoScroll){
53107             this.bodyEl.setStyle("overflow", "auto");
53108         }else{
53109             this.bodyEl.setStyle("overflow", "hidden");
53110         }
53111         //if(c.titlebar !== false){
53112             if((!c.titlebar && !c.title) || c.titlebar === false){
53113                 this.titleEl.hide();
53114             }else{
53115                 this.titleEl.show();
53116                 if(c.title){
53117                     this.titleTextEl.innerHTML = c.title;
53118                 }
53119             }
53120         //}
53121         this.duration = c.duration || .30;
53122         this.slideDuration = c.slideDuration || .45;
53123         this.config = c;
53124         if(c.collapsed){
53125             this.collapse(true);
53126         }
53127         if(c.hidden){
53128             this.hide();
53129         }
53130     },
53131     /**
53132      * Returns true if this region is currently visible.
53133      * @return {Boolean}
53134      */
53135     isVisible : function(){
53136         return this.visible;
53137     },
53138
53139     /**
53140      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
53141      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
53142      */
53143     setCollapsedTitle : function(title){
53144         title = title || "&#160;";
53145         if(this.collapsedTitleTextEl){
53146             this.collapsedTitleTextEl.innerHTML = title;
53147         }
53148     },
53149
53150     getBox : function(){
53151         var b;
53152         if(!this.collapsed){
53153             b = this.el.getBox(false, true);
53154         }else{
53155             b = this.collapsedEl.getBox(false, true);
53156         }
53157         return b;
53158     },
53159
53160     getMargins : function(){
53161         return this.collapsed ? this.cmargins : this.margins;
53162     },
53163
53164     highlight : function(){
53165         this.el.addClass("x-layout-panel-dragover");
53166     },
53167
53168     unhighlight : function(){
53169         this.el.removeClass("x-layout-panel-dragover");
53170     },
53171
53172     updateBox : function(box){
53173         this.box = box;
53174         if(!this.collapsed){
53175             this.el.dom.style.left = box.x + "px";
53176             this.el.dom.style.top = box.y + "px";
53177             this.updateBody(box.width, box.height);
53178         }else{
53179             this.collapsedEl.dom.style.left = box.x + "px";
53180             this.collapsedEl.dom.style.top = box.y + "px";
53181             this.collapsedEl.setSize(box.width, box.height);
53182         }
53183         if(this.tabs){
53184             this.tabs.autoSizeTabs();
53185         }
53186     },
53187
53188     updateBody : function(w, h){
53189         if(w !== null){
53190             this.el.setWidth(w);
53191             w -= this.el.getBorderWidth("rl");
53192             if(this.config.adjustments){
53193                 w += this.config.adjustments[0];
53194             }
53195         }
53196         if(h !== null){
53197             this.el.setHeight(h);
53198             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
53199             h -= this.el.getBorderWidth("tb");
53200             if(this.config.adjustments){
53201                 h += this.config.adjustments[1];
53202             }
53203             this.bodyEl.setHeight(h);
53204             if(this.tabs){
53205                 h = this.tabs.syncHeight(h);
53206             }
53207         }
53208         if(this.panelSize){
53209             w = w !== null ? w : this.panelSize.width;
53210             h = h !== null ? h : this.panelSize.height;
53211         }
53212         if(this.activePanel){
53213             var el = this.activePanel.getEl();
53214             w = w !== null ? w : el.getWidth();
53215             h = h !== null ? h : el.getHeight();
53216             this.panelSize = {width: w, height: h};
53217             this.activePanel.setSize(w, h);
53218         }
53219         if(Roo.isIE && this.tabs){
53220             this.tabs.el.repaint();
53221         }
53222     },
53223
53224     /**
53225      * Returns the container element for this region.
53226      * @return {Roo.Element}
53227      */
53228     getEl : function(){
53229         return this.el;
53230     },
53231
53232     /**
53233      * Hides this region.
53234      */
53235     hide : function(){
53236         if(!this.collapsed){
53237             this.el.dom.style.left = "-2000px";
53238             this.el.hide();
53239         }else{
53240             this.collapsedEl.dom.style.left = "-2000px";
53241             this.collapsedEl.hide();
53242         }
53243         this.visible = false;
53244         this.fireEvent("visibilitychange", this, false);
53245     },
53246
53247     /**
53248      * Shows this region if it was previously hidden.
53249      */
53250     show : function(){
53251         if(!this.collapsed){
53252             this.el.show();
53253         }else{
53254             this.collapsedEl.show();
53255         }
53256         this.visible = true;
53257         this.fireEvent("visibilitychange", this, true);
53258     },
53259
53260     closeClicked : function(){
53261         if(this.activePanel){
53262             this.remove(this.activePanel);
53263         }
53264     },
53265
53266     collapseClick : function(e){
53267         if(this.isSlid){
53268            e.stopPropagation();
53269            this.slideIn();
53270         }else{
53271            e.stopPropagation();
53272            this.slideOut();
53273         }
53274     },
53275
53276     /**
53277      * Collapses this region.
53278      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
53279      */
53280     collapse : function(skipAnim, skipCheck){
53281         if(this.collapsed) {
53282             return;
53283         }
53284         
53285         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
53286             
53287             this.collapsed = true;
53288             if(this.split){
53289                 this.split.el.hide();
53290             }
53291             if(this.config.animate && skipAnim !== true){
53292                 this.fireEvent("invalidated", this);
53293                 this.animateCollapse();
53294             }else{
53295                 this.el.setLocation(-20000,-20000);
53296                 this.el.hide();
53297                 this.collapsedEl.show();
53298                 this.fireEvent("collapsed", this);
53299                 this.fireEvent("invalidated", this);
53300             }
53301         }
53302         
53303     },
53304
53305     animateCollapse : function(){
53306         // overridden
53307     },
53308
53309     /**
53310      * Expands this region if it was previously collapsed.
53311      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
53312      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
53313      */
53314     expand : function(e, skipAnim){
53315         if(e) {
53316             e.stopPropagation();
53317         }
53318         if(!this.collapsed || this.el.hasActiveFx()) {
53319             return;
53320         }
53321         if(this.isSlid){
53322             this.afterSlideIn();
53323             skipAnim = true;
53324         }
53325         this.collapsed = false;
53326         if(this.config.animate && skipAnim !== true){
53327             this.animateExpand();
53328         }else{
53329             this.el.show();
53330             if(this.split){
53331                 this.split.el.show();
53332             }
53333             this.collapsedEl.setLocation(-2000,-2000);
53334             this.collapsedEl.hide();
53335             this.fireEvent("invalidated", this);
53336             this.fireEvent("expanded", this);
53337         }
53338     },
53339
53340     animateExpand : function(){
53341         // overridden
53342     },
53343
53344     initTabs : function()
53345     {
53346         this.bodyEl.setStyle("overflow", "hidden");
53347         var ts = new Roo.TabPanel(
53348                 this.bodyEl.dom,
53349                 {
53350                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
53351                     disableTooltips: this.config.disableTabTips,
53352                     toolbar : this.config.toolbar
53353                 }
53354         );
53355         if(this.config.hideTabs){
53356             ts.stripWrap.setDisplayed(false);
53357         }
53358         this.tabs = ts;
53359         ts.resizeTabs = this.config.resizeTabs === true;
53360         ts.minTabWidth = this.config.minTabWidth || 40;
53361         ts.maxTabWidth = this.config.maxTabWidth || 250;
53362         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
53363         ts.monitorResize = false;
53364         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
53365         ts.bodyEl.addClass('x-layout-tabs-body');
53366         this.panels.each(this.initPanelAsTab, this);
53367     },
53368
53369     initPanelAsTab : function(panel){
53370         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
53371                     this.config.closeOnTab && panel.isClosable());
53372         if(panel.tabTip !== undefined){
53373             ti.setTooltip(panel.tabTip);
53374         }
53375         ti.on("activate", function(){
53376               this.setActivePanel(panel);
53377         }, this);
53378         if(this.config.closeOnTab){
53379             ti.on("beforeclose", function(t, e){
53380                 e.cancel = true;
53381                 this.remove(panel);
53382             }, this);
53383         }
53384         return ti;
53385     },
53386
53387     updatePanelTitle : function(panel, title){
53388         if(this.activePanel == panel){
53389             this.updateTitle(title);
53390         }
53391         if(this.tabs){
53392             var ti = this.tabs.getTab(panel.getEl().id);
53393             ti.setText(title);
53394             if(panel.tabTip !== undefined){
53395                 ti.setTooltip(panel.tabTip);
53396             }
53397         }
53398     },
53399
53400     updateTitle : function(title){
53401         if(this.titleTextEl && !this.config.title){
53402             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
53403         }
53404     },
53405
53406     setActivePanel : function(panel){
53407         panel = this.getPanel(panel);
53408         if(this.activePanel && this.activePanel != panel){
53409             this.activePanel.setActiveState(false);
53410         }
53411         this.activePanel = panel;
53412         panel.setActiveState(true);
53413         if(this.panelSize){
53414             panel.setSize(this.panelSize.width, this.panelSize.height);
53415         }
53416         if(this.closeBtn){
53417             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
53418         }
53419         this.updateTitle(panel.getTitle());
53420         if(this.tabs){
53421             this.fireEvent("invalidated", this);
53422         }
53423         this.fireEvent("panelactivated", this, panel);
53424     },
53425
53426     /**
53427      * Shows the specified panel.
53428      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
53429      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
53430      */
53431     showPanel : function(panel)
53432     {
53433         panel = this.getPanel(panel);
53434         if(panel){
53435             if(this.tabs){
53436                 var tab = this.tabs.getTab(panel.getEl().id);
53437                 if(tab.isHidden()){
53438                     this.tabs.unhideTab(tab.id);
53439                 }
53440                 tab.activate();
53441             }else{
53442                 this.setActivePanel(panel);
53443             }
53444         }
53445         return panel;
53446     },
53447
53448     /**
53449      * Get the active panel for this region.
53450      * @return {Roo.ContentPanel} The active panel or null
53451      */
53452     getActivePanel : function(){
53453         return this.activePanel;
53454     },
53455
53456     validateVisibility : function(){
53457         if(this.panels.getCount() < 1){
53458             this.updateTitle("&#160;");
53459             this.closeBtn.hide();
53460             this.hide();
53461         }else{
53462             if(!this.isVisible()){
53463                 this.show();
53464             }
53465         }
53466     },
53467
53468     /**
53469      * Adds the passed ContentPanel(s) to this region.
53470      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
53471      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
53472      */
53473     add : function(panel){
53474         if(arguments.length > 1){
53475             for(var i = 0, len = arguments.length; i < len; i++) {
53476                 this.add(arguments[i]);
53477             }
53478             return null;
53479         }
53480         if(this.hasPanel(panel)){
53481             this.showPanel(panel);
53482             return panel;
53483         }
53484         panel.setRegion(this);
53485         this.panels.add(panel);
53486         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
53487             this.bodyEl.dom.appendChild(panel.getEl().dom);
53488             if(panel.background !== true){
53489                 this.setActivePanel(panel);
53490             }
53491             this.fireEvent("paneladded", this, panel);
53492             return panel;
53493         }
53494         if(!this.tabs){
53495             this.initTabs();
53496         }else{
53497             this.initPanelAsTab(panel);
53498         }
53499         if(panel.background !== true){
53500             this.tabs.activate(panel.getEl().id);
53501         }
53502         this.fireEvent("paneladded", this, panel);
53503         return panel;
53504     },
53505
53506     /**
53507      * Hides the tab for the specified panel.
53508      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
53509      */
53510     hidePanel : function(panel){
53511         if(this.tabs && (panel = this.getPanel(panel))){
53512             this.tabs.hideTab(panel.getEl().id);
53513         }
53514     },
53515
53516     /**
53517      * Unhides the tab for a previously hidden panel.
53518      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
53519      */
53520     unhidePanel : function(panel){
53521         if(this.tabs && (panel = this.getPanel(panel))){
53522             this.tabs.unhideTab(panel.getEl().id);
53523         }
53524     },
53525
53526     clearPanels : function(){
53527         while(this.panels.getCount() > 0){
53528              this.remove(this.panels.first());
53529         }
53530     },
53531
53532     /**
53533      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
53534      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
53535      * @param {Boolean} preservePanel Overrides the config preservePanel option
53536      * @return {Roo.ContentPanel} The panel that was removed
53537      */
53538     remove : function(panel, preservePanel){
53539         panel = this.getPanel(panel);
53540         if(!panel){
53541             return null;
53542         }
53543         var e = {};
53544         this.fireEvent("beforeremove", this, panel, e);
53545         if(e.cancel === true){
53546             return null;
53547         }
53548         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
53549         var panelId = panel.getId();
53550         this.panels.removeKey(panelId);
53551         if(preservePanel){
53552             document.body.appendChild(panel.getEl().dom);
53553         }
53554         if(this.tabs){
53555             this.tabs.removeTab(panel.getEl().id);
53556         }else if (!preservePanel){
53557             this.bodyEl.dom.removeChild(panel.getEl().dom);
53558         }
53559         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
53560             var p = this.panels.first();
53561             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
53562             tempEl.appendChild(p.getEl().dom);
53563             this.bodyEl.update("");
53564             this.bodyEl.dom.appendChild(p.getEl().dom);
53565             tempEl = null;
53566             this.updateTitle(p.getTitle());
53567             this.tabs = null;
53568             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
53569             this.setActivePanel(p);
53570         }
53571         panel.setRegion(null);
53572         if(this.activePanel == panel){
53573             this.activePanel = null;
53574         }
53575         if(this.config.autoDestroy !== false && preservePanel !== true){
53576             try{panel.destroy();}catch(e){}
53577         }
53578         this.fireEvent("panelremoved", this, panel);
53579         return panel;
53580     },
53581
53582     /**
53583      * Returns the TabPanel component used by this region
53584      * @return {Roo.TabPanel}
53585      */
53586     getTabs : function(){
53587         return this.tabs;
53588     },
53589
53590     createTool : function(parentEl, className){
53591         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
53592             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
53593         btn.addClassOnOver("x-layout-tools-button-over");
53594         return btn;
53595     }
53596 });/*
53597  * Based on:
53598  * Ext JS Library 1.1.1
53599  * Copyright(c) 2006-2007, Ext JS, LLC.
53600  *
53601  * Originally Released Under LGPL - original licence link has changed is not relivant.
53602  *
53603  * Fork - LGPL
53604  * <script type="text/javascript">
53605  */
53606  
53607
53608
53609 /**
53610  * @class Roo.SplitLayoutRegion
53611  * @extends Roo.LayoutRegion
53612  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
53613  */
53614 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
53615     this.cursor = cursor;
53616     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
53617 };
53618
53619 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
53620     splitTip : "Drag to resize.",
53621     collapsibleSplitTip : "Drag to resize. Double click to hide.",
53622     useSplitTips : false,
53623
53624     applyConfig : function(config){
53625         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
53626         if(config.split){
53627             if(!this.split){
53628                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
53629                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
53630                 /** The SplitBar for this region 
53631                 * @type Roo.SplitBar */
53632                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
53633                 this.split.on("moved", this.onSplitMove, this);
53634                 this.split.useShim = config.useShim === true;
53635                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
53636                 if(this.useSplitTips){
53637                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
53638                 }
53639                 if(config.collapsible){
53640                     this.split.el.on("dblclick", this.collapse,  this);
53641                 }
53642             }
53643             if(typeof config.minSize != "undefined"){
53644                 this.split.minSize = config.minSize;
53645             }
53646             if(typeof config.maxSize != "undefined"){
53647                 this.split.maxSize = config.maxSize;
53648             }
53649             if(config.hideWhenEmpty || config.hidden || config.collapsed){
53650                 this.hideSplitter();
53651             }
53652         }
53653     },
53654
53655     getHMaxSize : function(){
53656          var cmax = this.config.maxSize || 10000;
53657          var center = this.mgr.getRegion("center");
53658          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
53659     },
53660
53661     getVMaxSize : function(){
53662          var cmax = this.config.maxSize || 10000;
53663          var center = this.mgr.getRegion("center");
53664          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
53665     },
53666
53667     onSplitMove : function(split, newSize){
53668         this.fireEvent("resized", this, newSize);
53669     },
53670     
53671     /** 
53672      * Returns the {@link Roo.SplitBar} for this region.
53673      * @return {Roo.SplitBar}
53674      */
53675     getSplitBar : function(){
53676         return this.split;
53677     },
53678     
53679     hide : function(){
53680         this.hideSplitter();
53681         Roo.SplitLayoutRegion.superclass.hide.call(this);
53682     },
53683
53684     hideSplitter : function(){
53685         if(this.split){
53686             this.split.el.setLocation(-2000,-2000);
53687             this.split.el.hide();
53688         }
53689     },
53690
53691     show : function(){
53692         if(this.split){
53693             this.split.el.show();
53694         }
53695         Roo.SplitLayoutRegion.superclass.show.call(this);
53696     },
53697     
53698     beforeSlide: function(){
53699         if(Roo.isGecko){// firefox overflow auto bug workaround
53700             this.bodyEl.clip();
53701             if(this.tabs) {
53702                 this.tabs.bodyEl.clip();
53703             }
53704             if(this.activePanel){
53705                 this.activePanel.getEl().clip();
53706                 
53707                 if(this.activePanel.beforeSlide){
53708                     this.activePanel.beforeSlide();
53709                 }
53710             }
53711         }
53712     },
53713     
53714     afterSlide : function(){
53715         if(Roo.isGecko){// firefox overflow auto bug workaround
53716             this.bodyEl.unclip();
53717             if(this.tabs) {
53718                 this.tabs.bodyEl.unclip();
53719             }
53720             if(this.activePanel){
53721                 this.activePanel.getEl().unclip();
53722                 if(this.activePanel.afterSlide){
53723                     this.activePanel.afterSlide();
53724                 }
53725             }
53726         }
53727     },
53728
53729     initAutoHide : function(){
53730         if(this.autoHide !== false){
53731             if(!this.autoHideHd){
53732                 var st = new Roo.util.DelayedTask(this.slideIn, this);
53733                 this.autoHideHd = {
53734                     "mouseout": function(e){
53735                         if(!e.within(this.el, true)){
53736                             st.delay(500);
53737                         }
53738                     },
53739                     "mouseover" : function(e){
53740                         st.cancel();
53741                     },
53742                     scope : this
53743                 };
53744             }
53745             this.el.on(this.autoHideHd);
53746         }
53747     },
53748
53749     clearAutoHide : function(){
53750         if(this.autoHide !== false){
53751             this.el.un("mouseout", this.autoHideHd.mouseout);
53752             this.el.un("mouseover", this.autoHideHd.mouseover);
53753         }
53754     },
53755
53756     clearMonitor : function(){
53757         Roo.get(document).un("click", this.slideInIf, this);
53758     },
53759
53760     // these names are backwards but not changed for compat
53761     slideOut : function(){
53762         if(this.isSlid || this.el.hasActiveFx()){
53763             return;
53764         }
53765         this.isSlid = true;
53766         if(this.collapseBtn){
53767             this.collapseBtn.hide();
53768         }
53769         this.closeBtnState = this.closeBtn.getStyle('display');
53770         this.closeBtn.hide();
53771         if(this.stickBtn){
53772             this.stickBtn.show();
53773         }
53774         this.el.show();
53775         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
53776         this.beforeSlide();
53777         this.el.setStyle("z-index", 10001);
53778         this.el.slideIn(this.getSlideAnchor(), {
53779             callback: function(){
53780                 this.afterSlide();
53781                 this.initAutoHide();
53782                 Roo.get(document).on("click", this.slideInIf, this);
53783                 this.fireEvent("slideshow", this);
53784             },
53785             scope: this,
53786             block: true
53787         });
53788     },
53789
53790     afterSlideIn : function(){
53791         this.clearAutoHide();
53792         this.isSlid = false;
53793         this.clearMonitor();
53794         this.el.setStyle("z-index", "");
53795         if(this.collapseBtn){
53796             this.collapseBtn.show();
53797         }
53798         this.closeBtn.setStyle('display', this.closeBtnState);
53799         if(this.stickBtn){
53800             this.stickBtn.hide();
53801         }
53802         this.fireEvent("slidehide", this);
53803     },
53804
53805     slideIn : function(cb){
53806         if(!this.isSlid || this.el.hasActiveFx()){
53807             Roo.callback(cb);
53808             return;
53809         }
53810         this.isSlid = false;
53811         this.beforeSlide();
53812         this.el.slideOut(this.getSlideAnchor(), {
53813             callback: function(){
53814                 this.el.setLeftTop(-10000, -10000);
53815                 this.afterSlide();
53816                 this.afterSlideIn();
53817                 Roo.callback(cb);
53818             },
53819             scope: this,
53820             block: true
53821         });
53822     },
53823     
53824     slideInIf : function(e){
53825         if(!e.within(this.el)){
53826             this.slideIn();
53827         }
53828     },
53829
53830     animateCollapse : function(){
53831         this.beforeSlide();
53832         this.el.setStyle("z-index", 20000);
53833         var anchor = this.getSlideAnchor();
53834         this.el.slideOut(anchor, {
53835             callback : function(){
53836                 this.el.setStyle("z-index", "");
53837                 this.collapsedEl.slideIn(anchor, {duration:.3});
53838                 this.afterSlide();
53839                 this.el.setLocation(-10000,-10000);
53840                 this.el.hide();
53841                 this.fireEvent("collapsed", this);
53842             },
53843             scope: this,
53844             block: true
53845         });
53846     },
53847
53848     animateExpand : function(){
53849         this.beforeSlide();
53850         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
53851         this.el.setStyle("z-index", 20000);
53852         this.collapsedEl.hide({
53853             duration:.1
53854         });
53855         this.el.slideIn(this.getSlideAnchor(), {
53856             callback : function(){
53857                 this.el.setStyle("z-index", "");
53858                 this.afterSlide();
53859                 if(this.split){
53860                     this.split.el.show();
53861                 }
53862                 this.fireEvent("invalidated", this);
53863                 this.fireEvent("expanded", this);
53864             },
53865             scope: this,
53866             block: true
53867         });
53868     },
53869
53870     anchors : {
53871         "west" : "left",
53872         "east" : "right",
53873         "north" : "top",
53874         "south" : "bottom"
53875     },
53876
53877     sanchors : {
53878         "west" : "l",
53879         "east" : "r",
53880         "north" : "t",
53881         "south" : "b"
53882     },
53883
53884     canchors : {
53885         "west" : "tl-tr",
53886         "east" : "tr-tl",
53887         "north" : "tl-bl",
53888         "south" : "bl-tl"
53889     },
53890
53891     getAnchor : function(){
53892         return this.anchors[this.position];
53893     },
53894
53895     getCollapseAnchor : function(){
53896         return this.canchors[this.position];
53897     },
53898
53899     getSlideAnchor : function(){
53900         return this.sanchors[this.position];
53901     },
53902
53903     getAlignAdj : function(){
53904         var cm = this.cmargins;
53905         switch(this.position){
53906             case "west":
53907                 return [0, 0];
53908             break;
53909             case "east":
53910                 return [0, 0];
53911             break;
53912             case "north":
53913                 return [0, 0];
53914             break;
53915             case "south":
53916                 return [0, 0];
53917             break;
53918         }
53919     },
53920
53921     getExpandAdj : function(){
53922         var c = this.collapsedEl, cm = this.cmargins;
53923         switch(this.position){
53924             case "west":
53925                 return [-(cm.right+c.getWidth()+cm.left), 0];
53926             break;
53927             case "east":
53928                 return [cm.right+c.getWidth()+cm.left, 0];
53929             break;
53930             case "north":
53931                 return [0, -(cm.top+cm.bottom+c.getHeight())];
53932             break;
53933             case "south":
53934                 return [0, cm.top+cm.bottom+c.getHeight()];
53935             break;
53936         }
53937     }
53938 });/*
53939  * Based on:
53940  * Ext JS Library 1.1.1
53941  * Copyright(c) 2006-2007, Ext JS, LLC.
53942  *
53943  * Originally Released Under LGPL - original licence link has changed is not relivant.
53944  *
53945  * Fork - LGPL
53946  * <script type="text/javascript">
53947  */
53948 /*
53949  * These classes are private internal classes
53950  */
53951 Roo.CenterLayoutRegion = function(mgr, config){
53952     Roo.LayoutRegion.call(this, mgr, config, "center");
53953     this.visible = true;
53954     this.minWidth = config.minWidth || 20;
53955     this.minHeight = config.minHeight || 20;
53956 };
53957
53958 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
53959     hide : function(){
53960         // center panel can't be hidden
53961     },
53962     
53963     show : function(){
53964         // center panel can't be hidden
53965     },
53966     
53967     getMinWidth: function(){
53968         return this.minWidth;
53969     },
53970     
53971     getMinHeight: function(){
53972         return this.minHeight;
53973     }
53974 });
53975
53976
53977 Roo.NorthLayoutRegion = function(mgr, config){
53978     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
53979     if(this.split){
53980         this.split.placement = Roo.SplitBar.TOP;
53981         this.split.orientation = Roo.SplitBar.VERTICAL;
53982         this.split.el.addClass("x-layout-split-v");
53983     }
53984     var size = config.initialSize || config.height;
53985     if(typeof size != "undefined"){
53986         this.el.setHeight(size);
53987     }
53988 };
53989 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
53990     orientation: Roo.SplitBar.VERTICAL,
53991     getBox : function(){
53992         if(this.collapsed){
53993             return this.collapsedEl.getBox();
53994         }
53995         var box = this.el.getBox();
53996         if(this.split){
53997             box.height += this.split.el.getHeight();
53998         }
53999         return box;
54000     },
54001     
54002     updateBox : function(box){
54003         if(this.split && !this.collapsed){
54004             box.height -= this.split.el.getHeight();
54005             this.split.el.setLeft(box.x);
54006             this.split.el.setTop(box.y+box.height);
54007             this.split.el.setWidth(box.width);
54008         }
54009         if(this.collapsed){
54010             this.updateBody(box.width, null);
54011         }
54012         Roo.LayoutRegion.prototype.updateBox.call(this, box);
54013     }
54014 });
54015
54016 Roo.SouthLayoutRegion = function(mgr, config){
54017     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
54018     if(this.split){
54019         this.split.placement = Roo.SplitBar.BOTTOM;
54020         this.split.orientation = Roo.SplitBar.VERTICAL;
54021         this.split.el.addClass("x-layout-split-v");
54022     }
54023     var size = config.initialSize || config.height;
54024     if(typeof size != "undefined"){
54025         this.el.setHeight(size);
54026     }
54027 };
54028 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
54029     orientation: Roo.SplitBar.VERTICAL,
54030     getBox : function(){
54031         if(this.collapsed){
54032             return this.collapsedEl.getBox();
54033         }
54034         var box = this.el.getBox();
54035         if(this.split){
54036             var sh = this.split.el.getHeight();
54037             box.height += sh;
54038             box.y -= sh;
54039         }
54040         return box;
54041     },
54042     
54043     updateBox : function(box){
54044         if(this.split && !this.collapsed){
54045             var sh = this.split.el.getHeight();
54046             box.height -= sh;
54047             box.y += sh;
54048             this.split.el.setLeft(box.x);
54049             this.split.el.setTop(box.y-sh);
54050             this.split.el.setWidth(box.width);
54051         }
54052         if(this.collapsed){
54053             this.updateBody(box.width, null);
54054         }
54055         Roo.LayoutRegion.prototype.updateBox.call(this, box);
54056     }
54057 });
54058
54059 Roo.EastLayoutRegion = function(mgr, config){
54060     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
54061     if(this.split){
54062         this.split.placement = Roo.SplitBar.RIGHT;
54063         this.split.orientation = Roo.SplitBar.HORIZONTAL;
54064         this.split.el.addClass("x-layout-split-h");
54065     }
54066     var size = config.initialSize || config.width;
54067     if(typeof size != "undefined"){
54068         this.el.setWidth(size);
54069     }
54070 };
54071 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
54072     orientation: Roo.SplitBar.HORIZONTAL,
54073     getBox : function(){
54074         if(this.collapsed){
54075             return this.collapsedEl.getBox();
54076         }
54077         var box = this.el.getBox();
54078         if(this.split){
54079             var sw = this.split.el.getWidth();
54080             box.width += sw;
54081             box.x -= sw;
54082         }
54083         return box;
54084     },
54085
54086     updateBox : function(box){
54087         if(this.split && !this.collapsed){
54088             var sw = this.split.el.getWidth();
54089             box.width -= sw;
54090             this.split.el.setLeft(box.x);
54091             this.split.el.setTop(box.y);
54092             this.split.el.setHeight(box.height);
54093             box.x += sw;
54094         }
54095         if(this.collapsed){
54096             this.updateBody(null, box.height);
54097         }
54098         Roo.LayoutRegion.prototype.updateBox.call(this, box);
54099     }
54100 });
54101
54102 Roo.WestLayoutRegion = function(mgr, config){
54103     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
54104     if(this.split){
54105         this.split.placement = Roo.SplitBar.LEFT;
54106         this.split.orientation = Roo.SplitBar.HORIZONTAL;
54107         this.split.el.addClass("x-layout-split-h");
54108     }
54109     var size = config.initialSize || config.width;
54110     if(typeof size != "undefined"){
54111         this.el.setWidth(size);
54112     }
54113 };
54114 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
54115     orientation: Roo.SplitBar.HORIZONTAL,
54116     getBox : function(){
54117         if(this.collapsed){
54118             return this.collapsedEl.getBox();
54119         }
54120         var box = this.el.getBox();
54121         if(this.split){
54122             box.width += this.split.el.getWidth();
54123         }
54124         return box;
54125     },
54126     
54127     updateBox : function(box){
54128         if(this.split && !this.collapsed){
54129             var sw = this.split.el.getWidth();
54130             box.width -= sw;
54131             this.split.el.setLeft(box.x+box.width);
54132             this.split.el.setTop(box.y);
54133             this.split.el.setHeight(box.height);
54134         }
54135         if(this.collapsed){
54136             this.updateBody(null, box.height);
54137         }
54138         Roo.LayoutRegion.prototype.updateBox.call(this, box);
54139     }
54140 });
54141 /*
54142  * Based on:
54143  * Ext JS Library 1.1.1
54144  * Copyright(c) 2006-2007, Ext JS, LLC.
54145  *
54146  * Originally Released Under LGPL - original licence link has changed is not relivant.
54147  *
54148  * Fork - LGPL
54149  * <script type="text/javascript">
54150  */
54151  
54152  
54153 /*
54154  * Private internal class for reading and applying state
54155  */
54156 Roo.LayoutStateManager = function(layout){
54157      // default empty state
54158      this.state = {
54159         north: {},
54160         south: {},
54161         east: {},
54162         west: {}       
54163     };
54164 };
54165
54166 Roo.LayoutStateManager.prototype = {
54167     init : function(layout, provider){
54168         this.provider = provider;
54169         var state = provider.get(layout.id+"-layout-state");
54170         if(state){
54171             var wasUpdating = layout.isUpdating();
54172             if(!wasUpdating){
54173                 layout.beginUpdate();
54174             }
54175             for(var key in state){
54176                 if(typeof state[key] != "function"){
54177                     var rstate = state[key];
54178                     var r = layout.getRegion(key);
54179                     if(r && rstate){
54180                         if(rstate.size){
54181                             r.resizeTo(rstate.size);
54182                         }
54183                         if(rstate.collapsed == true){
54184                             r.collapse(true);
54185                         }else{
54186                             r.expand(null, true);
54187                         }
54188                     }
54189                 }
54190             }
54191             if(!wasUpdating){
54192                 layout.endUpdate();
54193             }
54194             this.state = state; 
54195         }
54196         this.layout = layout;
54197         layout.on("regionresized", this.onRegionResized, this);
54198         layout.on("regioncollapsed", this.onRegionCollapsed, this);
54199         layout.on("regionexpanded", this.onRegionExpanded, this);
54200     },
54201     
54202     storeState : function(){
54203         this.provider.set(this.layout.id+"-layout-state", this.state);
54204     },
54205     
54206     onRegionResized : function(region, newSize){
54207         this.state[region.getPosition()].size = newSize;
54208         this.storeState();
54209     },
54210     
54211     onRegionCollapsed : function(region){
54212         this.state[region.getPosition()].collapsed = true;
54213         this.storeState();
54214     },
54215     
54216     onRegionExpanded : function(region){
54217         this.state[region.getPosition()].collapsed = false;
54218         this.storeState();
54219     }
54220 };/*
54221  * Based on:
54222  * Ext JS Library 1.1.1
54223  * Copyright(c) 2006-2007, Ext JS, LLC.
54224  *
54225  * Originally Released Under LGPL - original licence link has changed is not relivant.
54226  *
54227  * Fork - LGPL
54228  * <script type="text/javascript">
54229  */
54230 /**
54231  * @class Roo.ContentPanel
54232  * @extends Roo.util.Observable
54233  * A basic ContentPanel element.
54234  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
54235  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
54236  * @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
54237  * @cfg {Boolean}   closable      True if the panel can be closed/removed
54238  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
54239  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
54240  * @cfg {Toolbar}   toolbar       A toolbar for this panel
54241  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
54242  * @cfg {String} title          The title for this panel
54243  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
54244  * @cfg {String} url            Calls {@link #setUrl} with this value
54245  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
54246  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
54247  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
54248  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
54249  * @cfg {String}    style  Extra style to add to the content panel 
54250
54251  * @constructor
54252  * Create a new ContentPanel.
54253  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
54254  * @param {String/Object} config A string to set only the title or a config object
54255  * @param {String} content (optional) Set the HTML content for this panel
54256  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
54257  */
54258 Roo.ContentPanel = function(el, config, content){
54259     
54260      
54261     /*
54262     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
54263         config = el;
54264         el = Roo.id();
54265     }
54266     if (config && config.parentLayout) { 
54267         el = config.parentLayout.el.createChild(); 
54268     }
54269     */
54270     if(el.autoCreate){ // xtype is available if this is called from factory
54271         config = el;
54272         el = Roo.id();
54273     }
54274     this.el = Roo.get(el);
54275     if(!this.el && config && config.autoCreate){
54276         if(typeof config.autoCreate == "object"){
54277             if(!config.autoCreate.id){
54278                 config.autoCreate.id = config.id||el;
54279             }
54280             this.el = Roo.DomHelper.append(document.body,
54281                         config.autoCreate, true);
54282         }else{
54283             this.el = Roo.DomHelper.append(document.body,
54284                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
54285         }
54286     }
54287     
54288     
54289     this.closable = false;
54290     this.loaded = false;
54291     this.active = false;
54292     if(typeof config == "string"){
54293         this.title = config;
54294     }else{
54295         Roo.apply(this, config);
54296     }
54297     
54298     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
54299         this.wrapEl = this.el.wrap();
54300         this.toolbar.container = this.el.insertSibling(false, 'before');
54301         this.toolbar = new Roo.Toolbar(this.toolbar);
54302     }
54303     
54304     // xtype created footer. - not sure if will work as we normally have to render first..
54305     if (this.footer && !this.footer.el && this.footer.xtype) {
54306         if (!this.wrapEl) {
54307             this.wrapEl = this.el.wrap();
54308         }
54309     
54310         this.footer.container = this.wrapEl.createChild();
54311          
54312         this.footer = Roo.factory(this.footer, Roo);
54313         
54314     }
54315     
54316     if(this.resizeEl){
54317         this.resizeEl = Roo.get(this.resizeEl, true);
54318     }else{
54319         this.resizeEl = this.el;
54320     }
54321     // handle view.xtype
54322     
54323  
54324     
54325     
54326     this.addEvents({
54327         /**
54328          * @event activate
54329          * Fires when this panel is activated. 
54330          * @param {Roo.ContentPanel} this
54331          */
54332         "activate" : true,
54333         /**
54334          * @event deactivate
54335          * Fires when this panel is activated. 
54336          * @param {Roo.ContentPanel} this
54337          */
54338         "deactivate" : true,
54339
54340         /**
54341          * @event resize
54342          * Fires when this panel is resized if fitToFrame is true.
54343          * @param {Roo.ContentPanel} this
54344          * @param {Number} width The width after any component adjustments
54345          * @param {Number} height The height after any component adjustments
54346          */
54347         "resize" : true,
54348         
54349          /**
54350          * @event render
54351          * Fires when this tab is created
54352          * @param {Roo.ContentPanel} this
54353          */
54354         "render" : true
54355          
54356         
54357     });
54358     
54359
54360     
54361     
54362     if(this.autoScroll){
54363         this.resizeEl.setStyle("overflow", "auto");
54364     } else {
54365         // fix randome scrolling
54366         this.el.on('scroll', function() {
54367             Roo.log('fix random scolling');
54368             this.scrollTo('top',0); 
54369         });
54370     }
54371     content = content || this.content;
54372     if(content){
54373         this.setContent(content);
54374     }
54375     if(config && config.url){
54376         this.setUrl(this.url, this.params, this.loadOnce);
54377     }
54378     
54379     
54380     
54381     Roo.ContentPanel.superclass.constructor.call(this);
54382     
54383     if (this.view && typeof(this.view.xtype) != 'undefined') {
54384         this.view.el = this.el.appendChild(document.createElement("div"));
54385         this.view = Roo.factory(this.view); 
54386         this.view.render  &&  this.view.render(false, '');  
54387     }
54388     
54389     
54390     this.fireEvent('render', this);
54391 };
54392
54393 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
54394     tabTip:'',
54395     setRegion : function(region){
54396         this.region = region;
54397         if(region){
54398            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
54399         }else{
54400            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
54401         } 
54402     },
54403     
54404     /**
54405      * Returns the toolbar for this Panel if one was configured. 
54406      * @return {Roo.Toolbar} 
54407      */
54408     getToolbar : function(){
54409         return this.toolbar;
54410     },
54411     
54412     setActiveState : function(active){
54413         this.active = active;
54414         if(!active){
54415             this.fireEvent("deactivate", this);
54416         }else{
54417             this.fireEvent("activate", this);
54418         }
54419     },
54420     /**
54421      * Updates this panel's element
54422      * @param {String} content The new content
54423      * @param {Boolean} loadScripts (optional) true to look for and process scripts
54424     */
54425     setContent : function(content, loadScripts){
54426         this.el.update(content, loadScripts);
54427     },
54428
54429     ignoreResize : function(w, h){
54430         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
54431             return true;
54432         }else{
54433             this.lastSize = {width: w, height: h};
54434             return false;
54435         }
54436     },
54437     /**
54438      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
54439      * @return {Roo.UpdateManager} The UpdateManager
54440      */
54441     getUpdateManager : function(){
54442         return this.el.getUpdateManager();
54443     },
54444      /**
54445      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
54446      * @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:
54447 <pre><code>
54448 panel.load({
54449     url: "your-url.php",
54450     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
54451     callback: yourFunction,
54452     scope: yourObject, //(optional scope)
54453     discardUrl: false,
54454     nocache: false,
54455     text: "Loading...",
54456     timeout: 30,
54457     scripts: false
54458 });
54459 </code></pre>
54460      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
54461      * 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.
54462      * @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}
54463      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
54464      * @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.
54465      * @return {Roo.ContentPanel} this
54466      */
54467     load : function(){
54468         var um = this.el.getUpdateManager();
54469         um.update.apply(um, arguments);
54470         return this;
54471     },
54472
54473
54474     /**
54475      * 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.
54476      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
54477      * @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)
54478      * @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)
54479      * @return {Roo.UpdateManager} The UpdateManager
54480      */
54481     setUrl : function(url, params, loadOnce){
54482         if(this.refreshDelegate){
54483             this.removeListener("activate", this.refreshDelegate);
54484         }
54485         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
54486         this.on("activate", this.refreshDelegate);
54487         return this.el.getUpdateManager();
54488     },
54489     
54490     _handleRefresh : function(url, params, loadOnce){
54491         if(!loadOnce || !this.loaded){
54492             var updater = this.el.getUpdateManager();
54493             updater.update(url, params, this._setLoaded.createDelegate(this));
54494         }
54495     },
54496     
54497     _setLoaded : function(){
54498         this.loaded = true;
54499     }, 
54500     
54501     /**
54502      * Returns this panel's id
54503      * @return {String} 
54504      */
54505     getId : function(){
54506         return this.el.id;
54507     },
54508     
54509     /** 
54510      * Returns this panel's element - used by regiosn to add.
54511      * @return {Roo.Element} 
54512      */
54513     getEl : function(){
54514         return this.wrapEl || this.el;
54515     },
54516     
54517     adjustForComponents : function(width, height)
54518     {
54519         //Roo.log('adjustForComponents ');
54520         if(this.resizeEl != this.el){
54521             width -= this.el.getFrameWidth('lr');
54522             height -= this.el.getFrameWidth('tb');
54523         }
54524         if(this.toolbar){
54525             var te = this.toolbar.getEl();
54526             height -= te.getHeight();
54527             te.setWidth(width);
54528         }
54529         if(this.footer){
54530             var te = this.footer.getEl();
54531             //Roo.log("footer:" + te.getHeight());
54532             
54533             height -= te.getHeight();
54534             te.setWidth(width);
54535         }
54536         
54537         
54538         if(this.adjustments){
54539             width += this.adjustments[0];
54540             height += this.adjustments[1];
54541         }
54542         return {"width": width, "height": height};
54543     },
54544     
54545     setSize : function(width, height){
54546         if(this.fitToFrame && !this.ignoreResize(width, height)){
54547             if(this.fitContainer && this.resizeEl != this.el){
54548                 this.el.setSize(width, height);
54549             }
54550             var size = this.adjustForComponents(width, height);
54551             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
54552             this.fireEvent('resize', this, size.width, size.height);
54553         }
54554     },
54555     
54556     /**
54557      * Returns this panel's title
54558      * @return {String} 
54559      */
54560     getTitle : function(){
54561         return this.title;
54562     },
54563     
54564     /**
54565      * Set this panel's title
54566      * @param {String} title
54567      */
54568     setTitle : function(title){
54569         this.title = title;
54570         if(this.region){
54571             this.region.updatePanelTitle(this, title);
54572         }
54573     },
54574     
54575     /**
54576      * Returns true is this panel was configured to be closable
54577      * @return {Boolean} 
54578      */
54579     isClosable : function(){
54580         return this.closable;
54581     },
54582     
54583     beforeSlide : function(){
54584         this.el.clip();
54585         this.resizeEl.clip();
54586     },
54587     
54588     afterSlide : function(){
54589         this.el.unclip();
54590         this.resizeEl.unclip();
54591     },
54592     
54593     /**
54594      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
54595      *   Will fail silently if the {@link #setUrl} method has not been called.
54596      *   This does not activate the panel, just updates its content.
54597      */
54598     refresh : function(){
54599         if(this.refreshDelegate){
54600            this.loaded = false;
54601            this.refreshDelegate();
54602         }
54603     },
54604     
54605     /**
54606      * Destroys this panel
54607      */
54608     destroy : function(){
54609         this.el.removeAllListeners();
54610         var tempEl = document.createElement("span");
54611         tempEl.appendChild(this.el.dom);
54612         tempEl.innerHTML = "";
54613         this.el.remove();
54614         this.el = null;
54615     },
54616     
54617     /**
54618      * form - if the content panel contains a form - this is a reference to it.
54619      * @type {Roo.form.Form}
54620      */
54621     form : false,
54622     /**
54623      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
54624      *    This contains a reference to it.
54625      * @type {Roo.View}
54626      */
54627     view : false,
54628     
54629       /**
54630      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
54631      * <pre><code>
54632
54633 layout.addxtype({
54634        xtype : 'Form',
54635        items: [ .... ]
54636    }
54637 );
54638
54639 </code></pre>
54640      * @param {Object} cfg Xtype definition of item to add.
54641      */
54642     
54643     addxtype : function(cfg) {
54644         // add form..
54645         if (cfg.xtype.match(/^Form$/)) {
54646             
54647             var el;
54648             //if (this.footer) {
54649             //    el = this.footer.container.insertSibling(false, 'before');
54650             //} else {
54651                 el = this.el.createChild();
54652             //}
54653
54654             this.form = new  Roo.form.Form(cfg);
54655             
54656             
54657             if ( this.form.allItems.length) {
54658                 this.form.render(el.dom);
54659             }
54660             return this.form;
54661         }
54662         // should only have one of theses..
54663         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
54664             // views.. should not be just added - used named prop 'view''
54665             
54666             cfg.el = this.el.appendChild(document.createElement("div"));
54667             // factory?
54668             
54669             var ret = new Roo.factory(cfg);
54670              
54671              ret.render && ret.render(false, ''); // render blank..
54672             this.view = ret;
54673             return ret;
54674         }
54675         return false;
54676     }
54677 });
54678
54679 /**
54680  * @class Roo.GridPanel
54681  * @extends Roo.ContentPanel
54682  * @constructor
54683  * Create a new GridPanel.
54684  * @param {Roo.grid.Grid} grid The grid for this panel
54685  * @param {String/Object} config A string to set only the panel's title, or a config object
54686  */
54687 Roo.GridPanel = function(grid, config){
54688     
54689   
54690     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
54691         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
54692         
54693     this.wrapper.dom.appendChild(grid.getGridEl().dom);
54694     
54695     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
54696     
54697     if(this.toolbar){
54698         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
54699     }
54700     // xtype created footer. - not sure if will work as we normally have to render first..
54701     if (this.footer && !this.footer.el && this.footer.xtype) {
54702         
54703         this.footer.container = this.grid.getView().getFooterPanel(true);
54704         this.footer.dataSource = this.grid.dataSource;
54705         this.footer = Roo.factory(this.footer, Roo);
54706         
54707     }
54708     
54709     grid.monitorWindowResize = false; // turn off autosizing
54710     grid.autoHeight = false;
54711     grid.autoWidth = false;
54712     this.grid = grid;
54713     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
54714 };
54715
54716 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
54717     getId : function(){
54718         return this.grid.id;
54719     },
54720     
54721     /**
54722      * Returns the grid for this panel
54723      * @return {Roo.grid.Grid} 
54724      */
54725     getGrid : function(){
54726         return this.grid;    
54727     },
54728     
54729     setSize : function(width, height){
54730         if(!this.ignoreResize(width, height)){
54731             var grid = this.grid;
54732             var size = this.adjustForComponents(width, height);
54733             grid.getGridEl().setSize(size.width, size.height);
54734             grid.autoSize();
54735         }
54736     },
54737     
54738     beforeSlide : function(){
54739         this.grid.getView().scroller.clip();
54740     },
54741     
54742     afterSlide : function(){
54743         this.grid.getView().scroller.unclip();
54744     },
54745     
54746     destroy : function(){
54747         this.grid.destroy();
54748         delete this.grid;
54749         Roo.GridPanel.superclass.destroy.call(this); 
54750     }
54751 });
54752
54753
54754 /**
54755  * @class Roo.NestedLayoutPanel
54756  * @extends Roo.ContentPanel
54757  * @constructor
54758  * Create a new NestedLayoutPanel.
54759  * 
54760  * 
54761  * @param {Roo.BorderLayout} layout The layout for this panel
54762  * @param {String/Object} config A string to set only the title or a config object
54763  */
54764 Roo.NestedLayoutPanel = function(layout, config)
54765 {
54766     // construct with only one argument..
54767     /* FIXME - implement nicer consturctors
54768     if (layout.layout) {
54769         config = layout;
54770         layout = config.layout;
54771         delete config.layout;
54772     }
54773     if (layout.xtype && !layout.getEl) {
54774         // then layout needs constructing..
54775         layout = Roo.factory(layout, Roo);
54776     }
54777     */
54778     
54779     
54780     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
54781     
54782     layout.monitorWindowResize = false; // turn off autosizing
54783     this.layout = layout;
54784     this.layout.getEl().addClass("x-layout-nested-layout");
54785     
54786     
54787     
54788     
54789 };
54790
54791 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
54792
54793     setSize : function(width, height){
54794         if(!this.ignoreResize(width, height)){
54795             var size = this.adjustForComponents(width, height);
54796             var el = this.layout.getEl();
54797             el.setSize(size.width, size.height);
54798             var touch = el.dom.offsetWidth;
54799             this.layout.layout();
54800             // ie requires a double layout on the first pass
54801             if(Roo.isIE && !this.initialized){
54802                 this.initialized = true;
54803                 this.layout.layout();
54804             }
54805         }
54806     },
54807     
54808     // activate all subpanels if not currently active..
54809     
54810     setActiveState : function(active){
54811         this.active = active;
54812         if(!active){
54813             this.fireEvent("deactivate", this);
54814             return;
54815         }
54816         
54817         this.fireEvent("activate", this);
54818         // not sure if this should happen before or after..
54819         if (!this.layout) {
54820             return; // should not happen..
54821         }
54822         var reg = false;
54823         for (var r in this.layout.regions) {
54824             reg = this.layout.getRegion(r);
54825             if (reg.getActivePanel()) {
54826                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
54827                 reg.setActivePanel(reg.getActivePanel());
54828                 continue;
54829             }
54830             if (!reg.panels.length) {
54831                 continue;
54832             }
54833             reg.showPanel(reg.getPanel(0));
54834         }
54835         
54836         
54837         
54838         
54839     },
54840     
54841     /**
54842      * Returns the nested BorderLayout for this panel
54843      * @return {Roo.BorderLayout} 
54844      */
54845     getLayout : function(){
54846         return this.layout;
54847     },
54848     
54849      /**
54850      * Adds a xtype elements to the layout of the nested panel
54851      * <pre><code>
54852
54853 panel.addxtype({
54854        xtype : 'ContentPanel',
54855        region: 'west',
54856        items: [ .... ]
54857    }
54858 );
54859
54860 panel.addxtype({
54861         xtype : 'NestedLayoutPanel',
54862         region: 'west',
54863         layout: {
54864            center: { },
54865            west: { }   
54866         },
54867         items : [ ... list of content panels or nested layout panels.. ]
54868    }
54869 );
54870 </code></pre>
54871      * @param {Object} cfg Xtype definition of item to add.
54872      */
54873     addxtype : function(cfg) {
54874         return this.layout.addxtype(cfg);
54875     
54876     }
54877 });
54878
54879 Roo.ScrollPanel = function(el, config, content){
54880     config = config || {};
54881     config.fitToFrame = true;
54882     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
54883     
54884     this.el.dom.style.overflow = "hidden";
54885     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
54886     this.el.removeClass("x-layout-inactive-content");
54887     this.el.on("mousewheel", this.onWheel, this);
54888
54889     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
54890     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
54891     up.unselectable(); down.unselectable();
54892     up.on("click", this.scrollUp, this);
54893     down.on("click", this.scrollDown, this);
54894     up.addClassOnOver("x-scroller-btn-over");
54895     down.addClassOnOver("x-scroller-btn-over");
54896     up.addClassOnClick("x-scroller-btn-click");
54897     down.addClassOnClick("x-scroller-btn-click");
54898     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
54899
54900     this.resizeEl = this.el;
54901     this.el = wrap; this.up = up; this.down = down;
54902 };
54903
54904 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
54905     increment : 100,
54906     wheelIncrement : 5,
54907     scrollUp : function(){
54908         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
54909     },
54910
54911     scrollDown : function(){
54912         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
54913     },
54914
54915     afterScroll : function(){
54916         var el = this.resizeEl;
54917         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
54918         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
54919         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
54920     },
54921
54922     setSize : function(){
54923         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
54924         this.afterScroll();
54925     },
54926
54927     onWheel : function(e){
54928         var d = e.getWheelDelta();
54929         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
54930         this.afterScroll();
54931         e.stopEvent();
54932     },
54933
54934     setContent : function(content, loadScripts){
54935         this.resizeEl.update(content, loadScripts);
54936     }
54937
54938 });
54939
54940
54941
54942
54943
54944
54945
54946
54947
54948 /**
54949  * @class Roo.TreePanel
54950  * @extends Roo.ContentPanel
54951  * @constructor
54952  * Create a new TreePanel. - defaults to fit/scoll contents.
54953  * @param {String/Object} config A string to set only the panel's title, or a config object
54954  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
54955  */
54956 Roo.TreePanel = function(config){
54957     var el = config.el;
54958     var tree = config.tree;
54959     delete config.tree; 
54960     delete config.el; // hopefull!
54961     
54962     // wrapper for IE7 strict & safari scroll issue
54963     
54964     var treeEl = el.createChild();
54965     config.resizeEl = treeEl;
54966     
54967     
54968     
54969     Roo.TreePanel.superclass.constructor.call(this, el, config);
54970  
54971  
54972     this.tree = new Roo.tree.TreePanel(treeEl , tree);
54973     //console.log(tree);
54974     this.on('activate', function()
54975     {
54976         if (this.tree.rendered) {
54977             return;
54978         }
54979         //console.log('render tree');
54980         this.tree.render();
54981     });
54982     // this should not be needed.. - it's actually the 'el' that resizes?
54983     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
54984     
54985     //this.on('resize',  function (cp, w, h) {
54986     //        this.tree.innerCt.setWidth(w);
54987     //        this.tree.innerCt.setHeight(h);
54988     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
54989     //});
54990
54991         
54992     
54993 };
54994
54995 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
54996     fitToFrame : true,
54997     autoScroll : true
54998 });
54999
55000
55001
55002
55003
55004
55005
55006
55007
55008
55009
55010 /*
55011  * Based on:
55012  * Ext JS Library 1.1.1
55013  * Copyright(c) 2006-2007, Ext JS, LLC.
55014  *
55015  * Originally Released Under LGPL - original licence link has changed is not relivant.
55016  *
55017  * Fork - LGPL
55018  * <script type="text/javascript">
55019  */
55020  
55021
55022 /**
55023  * @class Roo.ReaderLayout
55024  * @extends Roo.BorderLayout
55025  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
55026  * center region containing two nested regions (a top one for a list view and one for item preview below),
55027  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
55028  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
55029  * expedites the setup of the overall layout and regions for this common application style.
55030  * Example:
55031  <pre><code>
55032 var reader = new Roo.ReaderLayout();
55033 var CP = Roo.ContentPanel;  // shortcut for adding
55034
55035 reader.beginUpdate();
55036 reader.add("north", new CP("north", "North"));
55037 reader.add("west", new CP("west", {title: "West"}));
55038 reader.add("east", new CP("east", {title: "East"}));
55039
55040 reader.regions.listView.add(new CP("listView", "List"));
55041 reader.regions.preview.add(new CP("preview", "Preview"));
55042 reader.endUpdate();
55043 </code></pre>
55044 * @constructor
55045 * Create a new ReaderLayout
55046 * @param {Object} config Configuration options
55047 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
55048 * document.body if omitted)
55049 */
55050 Roo.ReaderLayout = function(config, renderTo){
55051     var c = config || {size:{}};
55052     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
55053         north: c.north !== false ? Roo.apply({
55054             split:false,
55055             initialSize: 32,
55056             titlebar: false
55057         }, c.north) : false,
55058         west: c.west !== false ? Roo.apply({
55059             split:true,
55060             initialSize: 200,
55061             minSize: 175,
55062             maxSize: 400,
55063             titlebar: true,
55064             collapsible: true,
55065             animate: true,
55066             margins:{left:5,right:0,bottom:5,top:5},
55067             cmargins:{left:5,right:5,bottom:5,top:5}
55068         }, c.west) : false,
55069         east: c.east !== false ? Roo.apply({
55070             split:true,
55071             initialSize: 200,
55072             minSize: 175,
55073             maxSize: 400,
55074             titlebar: true,
55075             collapsible: true,
55076             animate: true,
55077             margins:{left:0,right:5,bottom:5,top:5},
55078             cmargins:{left:5,right:5,bottom:5,top:5}
55079         }, c.east) : false,
55080         center: Roo.apply({
55081             tabPosition: 'top',
55082             autoScroll:false,
55083             closeOnTab: true,
55084             titlebar:false,
55085             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
55086         }, c.center)
55087     });
55088
55089     this.el.addClass('x-reader');
55090
55091     this.beginUpdate();
55092
55093     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
55094         south: c.preview !== false ? Roo.apply({
55095             split:true,
55096             initialSize: 200,
55097             minSize: 100,
55098             autoScroll:true,
55099             collapsible:true,
55100             titlebar: true,
55101             cmargins:{top:5,left:0, right:0, bottom:0}
55102         }, c.preview) : false,
55103         center: Roo.apply({
55104             autoScroll:false,
55105             titlebar:false,
55106             minHeight:200
55107         }, c.listView)
55108     });
55109     this.add('center', new Roo.NestedLayoutPanel(inner,
55110             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
55111
55112     this.endUpdate();
55113
55114     this.regions.preview = inner.getRegion('south');
55115     this.regions.listView = inner.getRegion('center');
55116 };
55117
55118 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
55119  * Based on:
55120  * Ext JS Library 1.1.1
55121  * Copyright(c) 2006-2007, Ext JS, LLC.
55122  *
55123  * Originally Released Under LGPL - original licence link has changed is not relivant.
55124  *
55125  * Fork - LGPL
55126  * <script type="text/javascript">
55127  */
55128  
55129 /**
55130  * @class Roo.grid.Grid
55131  * @extends Roo.util.Observable
55132  * This class represents the primary interface of a component based grid control.
55133  * <br><br>Usage:<pre><code>
55134  var grid = new Roo.grid.Grid("my-container-id", {
55135      ds: myDataStore,
55136      cm: myColModel,
55137      selModel: mySelectionModel,
55138      autoSizeColumns: true,
55139      monitorWindowResize: false,
55140      trackMouseOver: true
55141  });
55142  // set any options
55143  grid.render();
55144  * </code></pre>
55145  * <b>Common Problems:</b><br/>
55146  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
55147  * element will correct this<br/>
55148  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
55149  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
55150  * are unpredictable.<br/>
55151  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
55152  * grid to calculate dimensions/offsets.<br/>
55153   * @constructor
55154  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
55155  * The container MUST have some type of size defined for the grid to fill. The container will be
55156  * automatically set to position relative if it isn't already.
55157  * @param {Object} config A config object that sets properties on this grid.
55158  */
55159 Roo.grid.Grid = function(container, config){
55160         // initialize the container
55161         this.container = Roo.get(container);
55162         this.container.update("");
55163         this.container.setStyle("overflow", "hidden");
55164     this.container.addClass('x-grid-container');
55165
55166     this.id = this.container.id;
55167
55168     Roo.apply(this, config);
55169     // check and correct shorthanded configs
55170     if(this.ds){
55171         this.dataSource = this.ds;
55172         delete this.ds;
55173     }
55174     if(this.cm){
55175         this.colModel = this.cm;
55176         delete this.cm;
55177     }
55178     if(this.sm){
55179         this.selModel = this.sm;
55180         delete this.sm;
55181     }
55182
55183     if (this.selModel) {
55184         this.selModel = Roo.factory(this.selModel, Roo.grid);
55185         this.sm = this.selModel;
55186         this.sm.xmodule = this.xmodule || false;
55187     }
55188     if (typeof(this.colModel.config) == 'undefined') {
55189         this.colModel = new Roo.grid.ColumnModel(this.colModel);
55190         this.cm = this.colModel;
55191         this.cm.xmodule = this.xmodule || false;
55192     }
55193     if (this.dataSource) {
55194         this.dataSource= Roo.factory(this.dataSource, Roo.data);
55195         this.ds = this.dataSource;
55196         this.ds.xmodule = this.xmodule || false;
55197          
55198     }
55199     
55200     
55201     
55202     if(this.width){
55203         this.container.setWidth(this.width);
55204     }
55205
55206     if(this.height){
55207         this.container.setHeight(this.height);
55208     }
55209     /** @private */
55210         this.addEvents({
55211         // raw events
55212         /**
55213          * @event click
55214          * The raw click event for the entire grid.
55215          * @param {Roo.EventObject} e
55216          */
55217         "click" : true,
55218         /**
55219          * @event dblclick
55220          * The raw dblclick event for the entire grid.
55221          * @param {Roo.EventObject} e
55222          */
55223         "dblclick" : true,
55224         /**
55225          * @event contextmenu
55226          * The raw contextmenu event for the entire grid.
55227          * @param {Roo.EventObject} e
55228          */
55229         "contextmenu" : true,
55230         /**
55231          * @event mousedown
55232          * The raw mousedown event for the entire grid.
55233          * @param {Roo.EventObject} e
55234          */
55235         "mousedown" : true,
55236         /**
55237          * @event mouseup
55238          * The raw mouseup event for the entire grid.
55239          * @param {Roo.EventObject} e
55240          */
55241         "mouseup" : true,
55242         /**
55243          * @event mouseover
55244          * The raw mouseover event for the entire grid.
55245          * @param {Roo.EventObject} e
55246          */
55247         "mouseover" : true,
55248         /**
55249          * @event mouseout
55250          * The raw mouseout event for the entire grid.
55251          * @param {Roo.EventObject} e
55252          */
55253         "mouseout" : true,
55254         /**
55255          * @event keypress
55256          * The raw keypress event for the entire grid.
55257          * @param {Roo.EventObject} e
55258          */
55259         "keypress" : true,
55260         /**
55261          * @event keydown
55262          * The raw keydown event for the entire grid.
55263          * @param {Roo.EventObject} e
55264          */
55265         "keydown" : true,
55266
55267         // custom events
55268
55269         /**
55270          * @event cellclick
55271          * Fires when a cell is clicked
55272          * @param {Grid} this
55273          * @param {Number} rowIndex
55274          * @param {Number} columnIndex
55275          * @param {Roo.EventObject} e
55276          */
55277         "cellclick" : true,
55278         /**
55279          * @event celldblclick
55280          * Fires when a cell is double clicked
55281          * @param {Grid} this
55282          * @param {Number} rowIndex
55283          * @param {Number} columnIndex
55284          * @param {Roo.EventObject} e
55285          */
55286         "celldblclick" : true,
55287         /**
55288          * @event rowclick
55289          * Fires when a row is clicked
55290          * @param {Grid} this
55291          * @param {Number} rowIndex
55292          * @param {Roo.EventObject} e
55293          */
55294         "rowclick" : true,
55295         /**
55296          * @event rowdblclick
55297          * Fires when a row is double clicked
55298          * @param {Grid} this
55299          * @param {Number} rowIndex
55300          * @param {Roo.EventObject} e
55301          */
55302         "rowdblclick" : true,
55303         /**
55304          * @event headerclick
55305          * Fires when a header is clicked
55306          * @param {Grid} this
55307          * @param {Number} columnIndex
55308          * @param {Roo.EventObject} e
55309          */
55310         "headerclick" : true,
55311         /**
55312          * @event headerdblclick
55313          * Fires when a header cell is double clicked
55314          * @param {Grid} this
55315          * @param {Number} columnIndex
55316          * @param {Roo.EventObject} e
55317          */
55318         "headerdblclick" : true,
55319         /**
55320          * @event rowcontextmenu
55321          * Fires when a row is right clicked
55322          * @param {Grid} this
55323          * @param {Number} rowIndex
55324          * @param {Roo.EventObject} e
55325          */
55326         "rowcontextmenu" : true,
55327         /**
55328          * @event cellcontextmenu
55329          * Fires when a cell is right clicked
55330          * @param {Grid} this
55331          * @param {Number} rowIndex
55332          * @param {Number} cellIndex
55333          * @param {Roo.EventObject} e
55334          */
55335          "cellcontextmenu" : true,
55336         /**
55337          * @event headercontextmenu
55338          * Fires when a header is right clicked
55339          * @param {Grid} this
55340          * @param {Number} columnIndex
55341          * @param {Roo.EventObject} e
55342          */
55343         "headercontextmenu" : true,
55344         /**
55345          * @event bodyscroll
55346          * Fires when the body element is scrolled
55347          * @param {Number} scrollLeft
55348          * @param {Number} scrollTop
55349          */
55350         "bodyscroll" : true,
55351         /**
55352          * @event columnresize
55353          * Fires when the user resizes a column
55354          * @param {Number} columnIndex
55355          * @param {Number} newSize
55356          */
55357         "columnresize" : true,
55358         /**
55359          * @event columnmove
55360          * Fires when the user moves a column
55361          * @param {Number} oldIndex
55362          * @param {Number} newIndex
55363          */
55364         "columnmove" : true,
55365         /**
55366          * @event startdrag
55367          * Fires when row(s) start being dragged
55368          * @param {Grid} this
55369          * @param {Roo.GridDD} dd The drag drop object
55370          * @param {event} e The raw browser event
55371          */
55372         "startdrag" : true,
55373         /**
55374          * @event enddrag
55375          * Fires when a drag operation is complete
55376          * @param {Grid} this
55377          * @param {Roo.GridDD} dd The drag drop object
55378          * @param {event} e The raw browser event
55379          */
55380         "enddrag" : true,
55381         /**
55382          * @event dragdrop
55383          * Fires when dragged row(s) are dropped on a valid DD target
55384          * @param {Grid} this
55385          * @param {Roo.GridDD} dd The drag drop object
55386          * @param {String} targetId The target drag drop object
55387          * @param {event} e The raw browser event
55388          */
55389         "dragdrop" : true,
55390         /**
55391          * @event dragover
55392          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
55393          * @param {Grid} this
55394          * @param {Roo.GridDD} dd The drag drop object
55395          * @param {String} targetId The target drag drop object
55396          * @param {event} e The raw browser event
55397          */
55398         "dragover" : true,
55399         /**
55400          * @event dragenter
55401          *  Fires when the dragged row(s) first cross another DD target while being dragged
55402          * @param {Grid} this
55403          * @param {Roo.GridDD} dd The drag drop object
55404          * @param {String} targetId The target drag drop object
55405          * @param {event} e The raw browser event
55406          */
55407         "dragenter" : true,
55408         /**
55409          * @event dragout
55410          * Fires when the dragged row(s) leave another DD target while being dragged
55411          * @param {Grid} this
55412          * @param {Roo.GridDD} dd The drag drop object
55413          * @param {String} targetId The target drag drop object
55414          * @param {event} e The raw browser event
55415          */
55416         "dragout" : true,
55417         /**
55418          * @event rowclass
55419          * Fires when a row is rendered, so you can change add a style to it.
55420          * @param {GridView} gridview   The grid view
55421          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
55422          */
55423         'rowclass' : true,
55424
55425         /**
55426          * @event render
55427          * Fires when the grid is rendered
55428          * @param {Grid} grid
55429          */
55430         'render' : true
55431     });
55432
55433     Roo.grid.Grid.superclass.constructor.call(this);
55434 };
55435 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
55436     
55437     /**
55438      * @cfg {String} ddGroup - drag drop group.
55439      */
55440       /**
55441      * @cfg {String} dragGroup - drag group (?? not sure if needed.)
55442      */
55443
55444     /**
55445      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
55446      */
55447     minColumnWidth : 25,
55448
55449     /**
55450      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
55451      * <b>on initial render.</b> It is more efficient to explicitly size the columns
55452      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
55453      */
55454     autoSizeColumns : false,
55455
55456     /**
55457      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
55458      */
55459     autoSizeHeaders : true,
55460
55461     /**
55462      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
55463      */
55464     monitorWindowResize : true,
55465
55466     /**
55467      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
55468      * rows measured to get a columns size. Default is 0 (all rows).
55469      */
55470     maxRowsToMeasure : 0,
55471
55472     /**
55473      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
55474      */
55475     trackMouseOver : true,
55476
55477     /**
55478     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
55479     */
55480       /**
55481     * @cfg {Boolean} enableDrop  True to enable drop of elements. Default is false. (double check if this is needed?)
55482     */
55483     
55484     /**
55485     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
55486     */
55487     enableDragDrop : false,
55488     
55489     /**
55490     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
55491     */
55492     enableColumnMove : true,
55493     
55494     /**
55495     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
55496     */
55497     enableColumnHide : true,
55498     
55499     /**
55500     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
55501     */
55502     enableRowHeightSync : false,
55503     
55504     /**
55505     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
55506     */
55507     stripeRows : true,
55508     
55509     /**
55510     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
55511     */
55512     autoHeight : false,
55513
55514     /**
55515      * @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.
55516      */
55517     autoExpandColumn : false,
55518
55519     /**
55520     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
55521     * Default is 50.
55522     */
55523     autoExpandMin : 50,
55524
55525     /**
55526     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
55527     */
55528     autoExpandMax : 1000,
55529
55530     /**
55531     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
55532     */
55533     view : null,
55534
55535     /**
55536     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
55537     */
55538     loadMask : false,
55539     /**
55540     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
55541     */
55542     dropTarget: false,
55543     
55544    
55545     
55546     // private
55547     rendered : false,
55548
55549     /**
55550     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
55551     * of a fixed width. Default is false.
55552     */
55553     /**
55554     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
55555     */
55556     
55557     
55558     /**
55559     * @cfg {String} ddText Configures the text is the drag proxy (defaults to "%0 selected row(s)").
55560     * %0 is replaced with the number of selected rows.
55561     */
55562     ddText : "{0} selected row{1}",
55563     
55564     
55565     /**
55566      * Called once after all setup has been completed and the grid is ready to be rendered.
55567      * @return {Roo.grid.Grid} this
55568      */
55569     render : function()
55570     {
55571         var c = this.container;
55572         // try to detect autoHeight/width mode
55573         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
55574             this.autoHeight = true;
55575         }
55576         var view = this.getView();
55577         view.init(this);
55578
55579         c.on("click", this.onClick, this);
55580         c.on("dblclick", this.onDblClick, this);
55581         c.on("contextmenu", this.onContextMenu, this);
55582         c.on("keydown", this.onKeyDown, this);
55583         if (Roo.isTouch) {
55584             c.on("touchstart", this.onTouchStart, this);
55585         }
55586
55587         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
55588
55589         this.getSelectionModel().init(this);
55590
55591         view.render();
55592
55593         if(this.loadMask){
55594             this.loadMask = new Roo.LoadMask(this.container,
55595                     Roo.apply({store:this.dataSource}, this.loadMask));
55596         }
55597         
55598         
55599         if (this.toolbar && this.toolbar.xtype) {
55600             this.toolbar.container = this.getView().getHeaderPanel(true);
55601             this.toolbar = new Roo.Toolbar(this.toolbar);
55602         }
55603         if (this.footer && this.footer.xtype) {
55604             this.footer.dataSource = this.getDataSource();
55605             this.footer.container = this.getView().getFooterPanel(true);
55606             this.footer = Roo.factory(this.footer, Roo);
55607         }
55608         if (this.dropTarget && this.dropTarget.xtype) {
55609             delete this.dropTarget.xtype;
55610             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
55611         }
55612         
55613         
55614         this.rendered = true;
55615         this.fireEvent('render', this);
55616         return this;
55617     },
55618
55619     /**
55620      * Reconfigures the grid to use a different Store and Column Model.
55621      * The View will be bound to the new objects and refreshed.
55622      * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
55623      * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
55624      */
55625     reconfigure : function(dataSource, colModel){
55626         if(this.loadMask){
55627             this.loadMask.destroy();
55628             this.loadMask = new Roo.LoadMask(this.container,
55629                     Roo.apply({store:dataSource}, this.loadMask));
55630         }
55631         this.view.bind(dataSource, colModel);
55632         this.dataSource = dataSource;
55633         this.colModel = colModel;
55634         this.view.refresh(true);
55635     },
55636     /**
55637      * addColumns
55638      * Add's a column, default at the end..
55639      
55640      * @param {int} position to add (default end)
55641      * @param {Array} of objects of column configuration see {@link Roo.grid.ColumnModel} 
55642      */
55643     addColumns : function(pos, ar)
55644     {
55645         
55646         for (var i =0;i< ar.length;i++) {
55647             var cfg = ar[i];
55648             cfg.id = typeof(cfg.id) == 'undefined' ? Roo.id() : cfg.id; // don't normally use this..
55649             this.cm.lookup[cfg.id] = cfg;
55650         }
55651         
55652         
55653         if (typeof(pos) == 'undefined' || pos >= this.cm.config.length) {
55654             pos = this.cm.config.length; //this.cm.config.push(cfg);
55655         } 
55656         pos = Math.max(0,pos);
55657         ar.unshift(0);
55658         ar.unshift(pos);
55659         this.cm.config.splice.apply(this.cm.config, ar);
55660         
55661         
55662         
55663         this.view.generateRules(this.cm);
55664         this.view.refresh(true);
55665         
55666     },
55667     
55668     
55669     
55670     
55671     // private
55672     onKeyDown : function(e){
55673         this.fireEvent("keydown", e);
55674     },
55675
55676     /**
55677      * Destroy this grid.
55678      * @param {Boolean} removeEl True to remove the element
55679      */
55680     destroy : function(removeEl, keepListeners){
55681         if(this.loadMask){
55682             this.loadMask.destroy();
55683         }
55684         var c = this.container;
55685         c.removeAllListeners();
55686         this.view.destroy();
55687         this.colModel.purgeListeners();
55688         if(!keepListeners){
55689             this.purgeListeners();
55690         }
55691         c.update("");
55692         if(removeEl === true){
55693             c.remove();
55694         }
55695     },
55696
55697     // private
55698     processEvent : function(name, e){
55699         // does this fire select???
55700         //Roo.log('grid:processEvent '  + name);
55701         
55702         if (name != 'touchstart' ) {
55703             this.fireEvent(name, e);    
55704         }
55705         
55706         var t = e.getTarget();
55707         var v = this.view;
55708         var header = v.findHeaderIndex(t);
55709         if(header !== false){
55710             var ename = name == 'touchstart' ? 'click' : name;
55711              
55712             this.fireEvent("header" + ename, this, header, e);
55713         }else{
55714             var row = v.findRowIndex(t);
55715             var cell = v.findCellIndex(t);
55716             if (name == 'touchstart') {
55717                 // first touch is always a click.
55718                 // hopefull this happens after selection is updated.?
55719                 name = false;
55720                 
55721                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
55722                     var cs = this.selModel.getSelectedCell();
55723                     if (row == cs[0] && cell == cs[1]){
55724                         name = 'dblclick';
55725                     }
55726                 }
55727                 if (typeof(this.selModel.getSelections) != 'undefined') {
55728                     var cs = this.selModel.getSelections();
55729                     var ds = this.dataSource;
55730                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
55731                         name = 'dblclick';
55732                     }
55733                 }
55734                 if (!name) {
55735                     return;
55736                 }
55737             }
55738             
55739             
55740             if(row !== false){
55741                 this.fireEvent("row" + name, this, row, e);
55742                 if(cell !== false){
55743                     this.fireEvent("cell" + name, this, row, cell, e);
55744                 }
55745             }
55746         }
55747     },
55748
55749     // private
55750     onClick : function(e){
55751         this.processEvent("click", e);
55752     },
55753    // private
55754     onTouchStart : function(e){
55755         this.processEvent("touchstart", e);
55756     },
55757
55758     // private
55759     onContextMenu : function(e, t){
55760         this.processEvent("contextmenu", e);
55761     },
55762
55763     // private
55764     onDblClick : function(e){
55765         this.processEvent("dblclick", e);
55766     },
55767
55768     // private
55769     walkCells : function(row, col, step, fn, scope){
55770         var cm = this.colModel, clen = cm.getColumnCount();
55771         var ds = this.dataSource, rlen = ds.getCount(), first = true;
55772         if(step < 0){
55773             if(col < 0){
55774                 row--;
55775                 first = false;
55776             }
55777             while(row >= 0){
55778                 if(!first){
55779                     col = clen-1;
55780                 }
55781                 first = false;
55782                 while(col >= 0){
55783                     if(fn.call(scope || this, row, col, cm) === true){
55784                         return [row, col];
55785                     }
55786                     col--;
55787                 }
55788                 row--;
55789             }
55790         } else {
55791             if(col >= clen){
55792                 row++;
55793                 first = false;
55794             }
55795             while(row < rlen){
55796                 if(!first){
55797                     col = 0;
55798                 }
55799                 first = false;
55800                 while(col < clen){
55801                     if(fn.call(scope || this, row, col, cm) === true){
55802                         return [row, col];
55803                     }
55804                     col++;
55805                 }
55806                 row++;
55807             }
55808         }
55809         return null;
55810     },
55811
55812     // private
55813     getSelections : function(){
55814         return this.selModel.getSelections();
55815     },
55816
55817     /**
55818      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
55819      * but if manual update is required this method will initiate it.
55820      */
55821     autoSize : function(){
55822         if(this.rendered){
55823             this.view.layout();
55824             if(this.view.adjustForScroll){
55825                 this.view.adjustForScroll();
55826             }
55827         }
55828     },
55829
55830     /**
55831      * Returns the grid's underlying element.
55832      * @return {Element} The element
55833      */
55834     getGridEl : function(){
55835         return this.container;
55836     },
55837
55838     // private for compatibility, overridden by editor grid
55839     stopEditing : function(){},
55840
55841     /**
55842      * Returns the grid's SelectionModel.
55843      * @return {SelectionModel}
55844      */
55845     getSelectionModel : function(){
55846         if(!this.selModel){
55847             this.selModel = new Roo.grid.RowSelectionModel();
55848         }
55849         return this.selModel;
55850     },
55851
55852     /**
55853      * Returns the grid's DataSource.
55854      * @return {DataSource}
55855      */
55856     getDataSource : function(){
55857         return this.dataSource;
55858     },
55859
55860     /**
55861      * Returns the grid's ColumnModel.
55862      * @return {ColumnModel}
55863      */
55864     getColumnModel : function(){
55865         return this.colModel;
55866     },
55867
55868     /**
55869      * Returns the grid's GridView object.
55870      * @return {GridView}
55871      */
55872     getView : function(){
55873         if(!this.view){
55874             this.view = new Roo.grid.GridView(this.viewConfig);
55875             this.relayEvents(this.view, [
55876                 "beforerowremoved", "beforerowsinserted",
55877                 "beforerefresh", "rowremoved",
55878                 "rowsinserted", "rowupdated" ,"refresh"
55879             ]);
55880         }
55881         return this.view;
55882     },
55883     /**
55884      * Called to get grid's drag proxy text, by default returns this.ddText.
55885      * Override this to put something different in the dragged text.
55886      * @return {String}
55887      */
55888     getDragDropText : function(){
55889         var count = this.selModel.getCount();
55890         return String.format(this.ddText, count, count == 1 ? '' : 's');
55891     }
55892 });
55893 /*
55894  * Based on:
55895  * Ext JS Library 1.1.1
55896  * Copyright(c) 2006-2007, Ext JS, LLC.
55897  *
55898  * Originally Released Under LGPL - original licence link has changed is not relivant.
55899  *
55900  * Fork - LGPL
55901  * <script type="text/javascript">
55902  */
55903  
55904 Roo.grid.AbstractGridView = function(){
55905         this.grid = null;
55906         
55907         this.events = {
55908             "beforerowremoved" : true,
55909             "beforerowsinserted" : true,
55910             "beforerefresh" : true,
55911             "rowremoved" : true,
55912             "rowsinserted" : true,
55913             "rowupdated" : true,
55914             "refresh" : true
55915         };
55916     Roo.grid.AbstractGridView.superclass.constructor.call(this);
55917 };
55918
55919 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
55920     rowClass : "x-grid-row",
55921     cellClass : "x-grid-cell",
55922     tdClass : "x-grid-td",
55923     hdClass : "x-grid-hd",
55924     splitClass : "x-grid-hd-split",
55925     
55926     init: function(grid){
55927         this.grid = grid;
55928                 var cid = this.grid.getGridEl().id;
55929         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
55930         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
55931         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
55932         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
55933         },
55934         
55935     getColumnRenderers : function(){
55936         var renderers = [];
55937         var cm = this.grid.colModel;
55938         var colCount = cm.getColumnCount();
55939         for(var i = 0; i < colCount; i++){
55940             renderers[i] = cm.getRenderer(i);
55941         }
55942         return renderers;
55943     },
55944     
55945     getColumnIds : function(){
55946         var ids = [];
55947         var cm = this.grid.colModel;
55948         var colCount = cm.getColumnCount();
55949         for(var i = 0; i < colCount; i++){
55950             ids[i] = cm.getColumnId(i);
55951         }
55952         return ids;
55953     },
55954     
55955     getDataIndexes : function(){
55956         if(!this.indexMap){
55957             this.indexMap = this.buildIndexMap();
55958         }
55959         return this.indexMap.colToData;
55960     },
55961     
55962     getColumnIndexByDataIndex : function(dataIndex){
55963         if(!this.indexMap){
55964             this.indexMap = this.buildIndexMap();
55965         }
55966         return this.indexMap.dataToCol[dataIndex];
55967     },
55968     
55969     /**
55970      * Set a css style for a column dynamically. 
55971      * @param {Number} colIndex The index of the column
55972      * @param {String} name The css property name
55973      * @param {String} value The css value
55974      */
55975     setCSSStyle : function(colIndex, name, value){
55976         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
55977         Roo.util.CSS.updateRule(selector, name, value);
55978     },
55979     
55980     generateRules : function(cm){
55981         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
55982         Roo.util.CSS.removeStyleSheet(rulesId);
55983         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
55984             var cid = cm.getColumnId(i);
55985             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
55986                          this.tdSelector, cid, " {\n}\n",
55987                          this.hdSelector, cid, " {\n}\n",
55988                          this.splitSelector, cid, " {\n}\n");
55989         }
55990         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
55991     }
55992 });/*
55993  * Based on:
55994  * Ext JS Library 1.1.1
55995  * Copyright(c) 2006-2007, Ext JS, LLC.
55996  *
55997  * Originally Released Under LGPL - original licence link has changed is not relivant.
55998  *
55999  * Fork - LGPL
56000  * <script type="text/javascript">
56001  */
56002
56003 // private
56004 // This is a support class used internally by the Grid components
56005 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
56006     this.grid = grid;
56007     this.view = grid.getView();
56008     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
56009     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
56010     if(hd2){
56011         this.setHandleElId(Roo.id(hd));
56012         this.setOuterHandleElId(Roo.id(hd2));
56013     }
56014     this.scroll = false;
56015 };
56016 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
56017     maxDragWidth: 120,
56018     getDragData : function(e){
56019         var t = Roo.lib.Event.getTarget(e);
56020         var h = this.view.findHeaderCell(t);
56021         if(h){
56022             return {ddel: h.firstChild, header:h};
56023         }
56024         return false;
56025     },
56026
56027     onInitDrag : function(e){
56028         this.view.headersDisabled = true;
56029         var clone = this.dragData.ddel.cloneNode(true);
56030         clone.id = Roo.id();
56031         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
56032         this.proxy.update(clone);
56033         return true;
56034     },
56035
56036     afterValidDrop : function(){
56037         var v = this.view;
56038         setTimeout(function(){
56039             v.headersDisabled = false;
56040         }, 50);
56041     },
56042
56043     afterInvalidDrop : function(){
56044         var v = this.view;
56045         setTimeout(function(){
56046             v.headersDisabled = false;
56047         }, 50);
56048     }
56049 });
56050 /*
56051  * Based on:
56052  * Ext JS Library 1.1.1
56053  * Copyright(c) 2006-2007, Ext JS, LLC.
56054  *
56055  * Originally Released Under LGPL - original licence link has changed is not relivant.
56056  *
56057  * Fork - LGPL
56058  * <script type="text/javascript">
56059  */
56060 // private
56061 // This is a support class used internally by the Grid components
56062 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
56063     this.grid = grid;
56064     this.view = grid.getView();
56065     // split the proxies so they don't interfere with mouse events
56066     this.proxyTop = Roo.DomHelper.append(document.body, {
56067         cls:"col-move-top", html:"&#160;"
56068     }, true);
56069     this.proxyBottom = Roo.DomHelper.append(document.body, {
56070         cls:"col-move-bottom", html:"&#160;"
56071     }, true);
56072     this.proxyTop.hide = this.proxyBottom.hide = function(){
56073         this.setLeftTop(-100,-100);
56074         this.setStyle("visibility", "hidden");
56075     };
56076     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
56077     // temporarily disabled
56078     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
56079     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
56080 };
56081 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
56082     proxyOffsets : [-4, -9],
56083     fly: Roo.Element.fly,
56084
56085     getTargetFromEvent : function(e){
56086         var t = Roo.lib.Event.getTarget(e);
56087         var cindex = this.view.findCellIndex(t);
56088         if(cindex !== false){
56089             return this.view.getHeaderCell(cindex);
56090         }
56091         return null;
56092     },
56093
56094     nextVisible : function(h){
56095         var v = this.view, cm = this.grid.colModel;
56096         h = h.nextSibling;
56097         while(h){
56098             if(!cm.isHidden(v.getCellIndex(h))){
56099                 return h;
56100             }
56101             h = h.nextSibling;
56102         }
56103         return null;
56104     },
56105
56106     prevVisible : function(h){
56107         var v = this.view, cm = this.grid.colModel;
56108         h = h.prevSibling;
56109         while(h){
56110             if(!cm.isHidden(v.getCellIndex(h))){
56111                 return h;
56112             }
56113             h = h.prevSibling;
56114         }
56115         return null;
56116     },
56117
56118     positionIndicator : function(h, n, e){
56119         var x = Roo.lib.Event.getPageX(e);
56120         var r = Roo.lib.Dom.getRegion(n.firstChild);
56121         var px, pt, py = r.top + this.proxyOffsets[1];
56122         if((r.right - x) <= (r.right-r.left)/2){
56123             px = r.right+this.view.borderWidth;
56124             pt = "after";
56125         }else{
56126             px = r.left;
56127             pt = "before";
56128         }
56129         var oldIndex = this.view.getCellIndex(h);
56130         var newIndex = this.view.getCellIndex(n);
56131
56132         if(this.grid.colModel.isFixed(newIndex)){
56133             return false;
56134         }
56135
56136         var locked = this.grid.colModel.isLocked(newIndex);
56137
56138         if(pt == "after"){
56139             newIndex++;
56140         }
56141         if(oldIndex < newIndex){
56142             newIndex--;
56143         }
56144         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
56145             return false;
56146         }
56147         px +=  this.proxyOffsets[0];
56148         this.proxyTop.setLeftTop(px, py);
56149         this.proxyTop.show();
56150         if(!this.bottomOffset){
56151             this.bottomOffset = this.view.mainHd.getHeight();
56152         }
56153         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
56154         this.proxyBottom.show();
56155         return pt;
56156     },
56157
56158     onNodeEnter : function(n, dd, e, data){
56159         if(data.header != n){
56160             this.positionIndicator(data.header, n, e);
56161         }
56162     },
56163
56164     onNodeOver : function(n, dd, e, data){
56165         var result = false;
56166         if(data.header != n){
56167             result = this.positionIndicator(data.header, n, e);
56168         }
56169         if(!result){
56170             this.proxyTop.hide();
56171             this.proxyBottom.hide();
56172         }
56173         return result ? this.dropAllowed : this.dropNotAllowed;
56174     },
56175
56176     onNodeOut : function(n, dd, e, data){
56177         this.proxyTop.hide();
56178         this.proxyBottom.hide();
56179     },
56180
56181     onNodeDrop : function(n, dd, e, data){
56182         var h = data.header;
56183         if(h != n){
56184             var cm = this.grid.colModel;
56185             var x = Roo.lib.Event.getPageX(e);
56186             var r = Roo.lib.Dom.getRegion(n.firstChild);
56187             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
56188             var oldIndex = this.view.getCellIndex(h);
56189             var newIndex = this.view.getCellIndex(n);
56190             var locked = cm.isLocked(newIndex);
56191             if(pt == "after"){
56192                 newIndex++;
56193             }
56194             if(oldIndex < newIndex){
56195                 newIndex--;
56196             }
56197             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
56198                 return false;
56199             }
56200             cm.setLocked(oldIndex, locked, true);
56201             cm.moveColumn(oldIndex, newIndex);
56202             this.grid.fireEvent("columnmove", oldIndex, newIndex);
56203             return true;
56204         }
56205         return false;
56206     }
56207 });
56208 /*
56209  * Based on:
56210  * Ext JS Library 1.1.1
56211  * Copyright(c) 2006-2007, Ext JS, LLC.
56212  *
56213  * Originally Released Under LGPL - original licence link has changed is not relivant.
56214  *
56215  * Fork - LGPL
56216  * <script type="text/javascript">
56217  */
56218   
56219 /**
56220  * @class Roo.grid.GridView
56221  * @extends Roo.util.Observable
56222  *
56223  * @constructor
56224  * @param {Object} config
56225  */
56226 Roo.grid.GridView = function(config){
56227     Roo.grid.GridView.superclass.constructor.call(this);
56228     this.el = null;
56229
56230     Roo.apply(this, config);
56231 };
56232
56233 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
56234
56235     unselectable :  'unselectable="on"',
56236     unselectableCls :  'x-unselectable',
56237     
56238     
56239     rowClass : "x-grid-row",
56240
56241     cellClass : "x-grid-col",
56242
56243     tdClass : "x-grid-td",
56244
56245     hdClass : "x-grid-hd",
56246
56247     splitClass : "x-grid-split",
56248
56249     sortClasses : ["sort-asc", "sort-desc"],
56250
56251     enableMoveAnim : false,
56252
56253     hlColor: "C3DAF9",
56254
56255     dh : Roo.DomHelper,
56256
56257     fly : Roo.Element.fly,
56258
56259     css : Roo.util.CSS,
56260
56261     borderWidth: 1,
56262
56263     splitOffset: 3,
56264
56265     scrollIncrement : 22,
56266
56267     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
56268
56269     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
56270
56271     bind : function(ds, cm){
56272         if(this.ds){
56273             this.ds.un("load", this.onLoad, this);
56274             this.ds.un("datachanged", this.onDataChange, this);
56275             this.ds.un("add", this.onAdd, this);
56276             this.ds.un("remove", this.onRemove, this);
56277             this.ds.un("update", this.onUpdate, this);
56278             this.ds.un("clear", this.onClear, this);
56279         }
56280         if(ds){
56281             ds.on("load", this.onLoad, this);
56282             ds.on("datachanged", this.onDataChange, this);
56283             ds.on("add", this.onAdd, this);
56284             ds.on("remove", this.onRemove, this);
56285             ds.on("update", this.onUpdate, this);
56286             ds.on("clear", this.onClear, this);
56287         }
56288         this.ds = ds;
56289
56290         if(this.cm){
56291             this.cm.un("widthchange", this.onColWidthChange, this);
56292             this.cm.un("headerchange", this.onHeaderChange, this);
56293             this.cm.un("hiddenchange", this.onHiddenChange, this);
56294             this.cm.un("columnmoved", this.onColumnMove, this);
56295             this.cm.un("columnlockchange", this.onColumnLock, this);
56296         }
56297         if(cm){
56298             this.generateRules(cm);
56299             cm.on("widthchange", this.onColWidthChange, this);
56300             cm.on("headerchange", this.onHeaderChange, this);
56301             cm.on("hiddenchange", this.onHiddenChange, this);
56302             cm.on("columnmoved", this.onColumnMove, this);
56303             cm.on("columnlockchange", this.onColumnLock, this);
56304         }
56305         this.cm = cm;
56306     },
56307
56308     init: function(grid){
56309         Roo.grid.GridView.superclass.init.call(this, grid);
56310
56311         this.bind(grid.dataSource, grid.colModel);
56312
56313         grid.on("headerclick", this.handleHeaderClick, this);
56314
56315         if(grid.trackMouseOver){
56316             grid.on("mouseover", this.onRowOver, this);
56317             grid.on("mouseout", this.onRowOut, this);
56318         }
56319         grid.cancelTextSelection = function(){};
56320         this.gridId = grid.id;
56321
56322         var tpls = this.templates || {};
56323
56324         if(!tpls.master){
56325             tpls.master = new Roo.Template(
56326                '<div class="x-grid" hidefocus="true">',
56327                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
56328                   '<div class="x-grid-topbar"></div>',
56329                   '<div class="x-grid-scroller"><div></div></div>',
56330                   '<div class="x-grid-locked">',
56331                       '<div class="x-grid-header">{lockedHeader}</div>',
56332                       '<div class="x-grid-body">{lockedBody}</div>',
56333                   "</div>",
56334                   '<div class="x-grid-viewport">',
56335                       '<div class="x-grid-header">{header}</div>',
56336                       '<div class="x-grid-body">{body}</div>',
56337                   "</div>",
56338                   '<div class="x-grid-bottombar"></div>',
56339                  
56340                   '<div class="x-grid-resize-proxy">&#160;</div>',
56341                "</div>"
56342             );
56343             tpls.master.disableformats = true;
56344         }
56345
56346         if(!tpls.header){
56347             tpls.header = new Roo.Template(
56348                '<table border="0" cellspacing="0" cellpadding="0">',
56349                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
56350                "</table>{splits}"
56351             );
56352             tpls.header.disableformats = true;
56353         }
56354         tpls.header.compile();
56355
56356         if(!tpls.hcell){
56357             tpls.hcell = new Roo.Template(
56358                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
56359                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
56360                 "</div></td>"
56361              );
56362              tpls.hcell.disableFormats = true;
56363         }
56364         tpls.hcell.compile();
56365
56366         if(!tpls.hsplit){
56367             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
56368                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
56369             tpls.hsplit.disableFormats = true;
56370         }
56371         tpls.hsplit.compile();
56372
56373         if(!tpls.body){
56374             tpls.body = new Roo.Template(
56375                '<table border="0" cellspacing="0" cellpadding="0">',
56376                "<tbody>{rows}</tbody>",
56377                "</table>"
56378             );
56379             tpls.body.disableFormats = true;
56380         }
56381         tpls.body.compile();
56382
56383         if(!tpls.row){
56384             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
56385             tpls.row.disableFormats = true;
56386         }
56387         tpls.row.compile();
56388
56389         if(!tpls.cell){
56390             tpls.cell = new Roo.Template(
56391                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
56392                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
56393                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
56394                 "</td>"
56395             );
56396             tpls.cell.disableFormats = true;
56397         }
56398         tpls.cell.compile();
56399
56400         this.templates = tpls;
56401     },
56402
56403     // remap these for backwards compat
56404     onColWidthChange : function(){
56405         this.updateColumns.apply(this, arguments);
56406     },
56407     onHeaderChange : function(){
56408         this.updateHeaders.apply(this, arguments);
56409     }, 
56410     onHiddenChange : function(){
56411         this.handleHiddenChange.apply(this, arguments);
56412     },
56413     onColumnMove : function(){
56414         this.handleColumnMove.apply(this, arguments);
56415     },
56416     onColumnLock : function(){
56417         this.handleLockChange.apply(this, arguments);
56418     },
56419
56420     onDataChange : function(){
56421         this.refresh();
56422         this.updateHeaderSortState();
56423     },
56424
56425     onClear : function(){
56426         this.refresh();
56427     },
56428
56429     onUpdate : function(ds, record){
56430         this.refreshRow(record);
56431     },
56432
56433     refreshRow : function(record){
56434         var ds = this.ds, index;
56435         if(typeof record == 'number'){
56436             index = record;
56437             record = ds.getAt(index);
56438         }else{
56439             index = ds.indexOf(record);
56440         }
56441         this.insertRows(ds, index, index, true);
56442         this.onRemove(ds, record, index+1, true);
56443         this.syncRowHeights(index, index);
56444         this.layout();
56445         this.fireEvent("rowupdated", this, index, record);
56446     },
56447
56448     onAdd : function(ds, records, index){
56449         this.insertRows(ds, index, index + (records.length-1));
56450     },
56451
56452     onRemove : function(ds, record, index, isUpdate){
56453         if(isUpdate !== true){
56454             this.fireEvent("beforerowremoved", this, index, record);
56455         }
56456         var bt = this.getBodyTable(), lt = this.getLockedTable();
56457         if(bt.rows[index]){
56458             bt.firstChild.removeChild(bt.rows[index]);
56459         }
56460         if(lt.rows[index]){
56461             lt.firstChild.removeChild(lt.rows[index]);
56462         }
56463         if(isUpdate !== true){
56464             this.stripeRows(index);
56465             this.syncRowHeights(index, index);
56466             this.layout();
56467             this.fireEvent("rowremoved", this, index, record);
56468         }
56469     },
56470
56471     onLoad : function(){
56472         this.scrollToTop();
56473     },
56474
56475     /**
56476      * Scrolls the grid to the top
56477      */
56478     scrollToTop : function(){
56479         if(this.scroller){
56480             this.scroller.dom.scrollTop = 0;
56481             this.syncScroll();
56482         }
56483     },
56484
56485     /**
56486      * Gets a panel in the header of the grid that can be used for toolbars etc.
56487      * After modifying the contents of this panel a call to grid.autoSize() may be
56488      * required to register any changes in size.
56489      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
56490      * @return Roo.Element
56491      */
56492     getHeaderPanel : function(doShow){
56493         if(doShow){
56494             this.headerPanel.show();
56495         }
56496         return this.headerPanel;
56497     },
56498
56499     /**
56500      * Gets a panel in the footer of the grid that can be used for toolbars etc.
56501      * After modifying the contents of this panel a call to grid.autoSize() may be
56502      * required to register any changes in size.
56503      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
56504      * @return Roo.Element
56505      */
56506     getFooterPanel : function(doShow){
56507         if(doShow){
56508             this.footerPanel.show();
56509         }
56510         return this.footerPanel;
56511     },
56512
56513     initElements : function(){
56514         var E = Roo.Element;
56515         var el = this.grid.getGridEl().dom.firstChild;
56516         var cs = el.childNodes;
56517
56518         this.el = new E(el);
56519         
56520          this.focusEl = new E(el.firstChild);
56521         this.focusEl.swallowEvent("click", true);
56522         
56523         this.headerPanel = new E(cs[1]);
56524         this.headerPanel.enableDisplayMode("block");
56525
56526         this.scroller = new E(cs[2]);
56527         this.scrollSizer = new E(this.scroller.dom.firstChild);
56528
56529         this.lockedWrap = new E(cs[3]);
56530         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
56531         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
56532
56533         this.mainWrap = new E(cs[4]);
56534         this.mainHd = new E(this.mainWrap.dom.firstChild);
56535         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
56536
56537         this.footerPanel = new E(cs[5]);
56538         this.footerPanel.enableDisplayMode("block");
56539
56540         this.resizeProxy = new E(cs[6]);
56541
56542         this.headerSelector = String.format(
56543            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
56544            this.lockedHd.id, this.mainHd.id
56545         );
56546
56547         this.splitterSelector = String.format(
56548            '#{0} div.x-grid-split, #{1} div.x-grid-split',
56549            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
56550         );
56551     },
56552     idToCssName : function(s)
56553     {
56554         return s.replace(/[^a-z0-9]+/ig, '-');
56555     },
56556
56557     getHeaderCell : function(index){
56558         return Roo.DomQuery.select(this.headerSelector)[index];
56559     },
56560
56561     getHeaderCellMeasure : function(index){
56562         return this.getHeaderCell(index).firstChild;
56563     },
56564
56565     getHeaderCellText : function(index){
56566         return this.getHeaderCell(index).firstChild.firstChild;
56567     },
56568
56569     getLockedTable : function(){
56570         return this.lockedBody.dom.firstChild;
56571     },
56572
56573     getBodyTable : function(){
56574         return this.mainBody.dom.firstChild;
56575     },
56576
56577     getLockedRow : function(index){
56578         return this.getLockedTable().rows[index];
56579     },
56580
56581     getRow : function(index){
56582         return this.getBodyTable().rows[index];
56583     },
56584
56585     getRowComposite : function(index){
56586         if(!this.rowEl){
56587             this.rowEl = new Roo.CompositeElementLite();
56588         }
56589         var els = [], lrow, mrow;
56590         if(lrow = this.getLockedRow(index)){
56591             els.push(lrow);
56592         }
56593         if(mrow = this.getRow(index)){
56594             els.push(mrow);
56595         }
56596         this.rowEl.elements = els;
56597         return this.rowEl;
56598     },
56599     /**
56600      * Gets the 'td' of the cell
56601      * 
56602      * @param {Integer} rowIndex row to select
56603      * @param {Integer} colIndex column to select
56604      * 
56605      * @return {Object} 
56606      */
56607     getCell : function(rowIndex, colIndex){
56608         var locked = this.cm.getLockedCount();
56609         var source;
56610         if(colIndex < locked){
56611             source = this.lockedBody.dom.firstChild;
56612         }else{
56613             source = this.mainBody.dom.firstChild;
56614             colIndex -= locked;
56615         }
56616         return source.rows[rowIndex].childNodes[colIndex];
56617     },
56618
56619     getCellText : function(rowIndex, colIndex){
56620         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
56621     },
56622
56623     getCellBox : function(cell){
56624         var b = this.fly(cell).getBox();
56625         if(Roo.isOpera){ // opera fails to report the Y
56626             b.y = cell.offsetTop + this.mainBody.getY();
56627         }
56628         return b;
56629     },
56630
56631     getCellIndex : function(cell){
56632         var id = String(cell.className).match(this.cellRE);
56633         if(id){
56634             return parseInt(id[1], 10);
56635         }
56636         return 0;
56637     },
56638
56639     findHeaderIndex : function(n){
56640         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
56641         return r ? this.getCellIndex(r) : false;
56642     },
56643
56644     findHeaderCell : function(n){
56645         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
56646         return r ? r : false;
56647     },
56648
56649     findRowIndex : function(n){
56650         if(!n){
56651             return false;
56652         }
56653         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
56654         return r ? r.rowIndex : false;
56655     },
56656
56657     findCellIndex : function(node){
56658         var stop = this.el.dom;
56659         while(node && node != stop){
56660             if(this.findRE.test(node.className)){
56661                 return this.getCellIndex(node);
56662             }
56663             node = node.parentNode;
56664         }
56665         return false;
56666     },
56667
56668     getColumnId : function(index){
56669         return this.cm.getColumnId(index);
56670     },
56671
56672     getSplitters : function()
56673     {
56674         if(this.splitterSelector){
56675            return Roo.DomQuery.select(this.splitterSelector);
56676         }else{
56677             return null;
56678       }
56679     },
56680
56681     getSplitter : function(index){
56682         return this.getSplitters()[index];
56683     },
56684
56685     onRowOver : function(e, t){
56686         var row;
56687         if((row = this.findRowIndex(t)) !== false){
56688             this.getRowComposite(row).addClass("x-grid-row-over");
56689         }
56690     },
56691
56692     onRowOut : function(e, t){
56693         var row;
56694         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
56695             this.getRowComposite(row).removeClass("x-grid-row-over");
56696         }
56697     },
56698
56699     renderHeaders : function(){
56700         var cm = this.cm;
56701         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
56702         var cb = [], lb = [], sb = [], lsb = [], p = {};
56703         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
56704             p.cellId = "x-grid-hd-0-" + i;
56705             p.splitId = "x-grid-csplit-0-" + i;
56706             p.id = cm.getColumnId(i);
56707             p.value = cm.getColumnHeader(i) || "";
56708             p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</)  ? '' :  p.value  || "";
56709             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
56710             if(!cm.isLocked(i)){
56711                 cb[cb.length] = ct.apply(p);
56712                 sb[sb.length] = st.apply(p);
56713             }else{
56714                 lb[lb.length] = ct.apply(p);
56715                 lsb[lsb.length] = st.apply(p);
56716             }
56717         }
56718         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
56719                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
56720     },
56721
56722     updateHeaders : function(){
56723         var html = this.renderHeaders();
56724         this.lockedHd.update(html[0]);
56725         this.mainHd.update(html[1]);
56726     },
56727
56728     /**
56729      * Focuses the specified row.
56730      * @param {Number} row The row index
56731      */
56732     focusRow : function(row)
56733     {
56734         //Roo.log('GridView.focusRow');
56735         var x = this.scroller.dom.scrollLeft;
56736         this.focusCell(row, 0, false);
56737         this.scroller.dom.scrollLeft = x;
56738     },
56739
56740     /**
56741      * Focuses the specified cell.
56742      * @param {Number} row The row index
56743      * @param {Number} col The column index
56744      * @param {Boolean} hscroll false to disable horizontal scrolling
56745      */
56746     focusCell : function(row, col, hscroll)
56747     {
56748         //Roo.log('GridView.focusCell');
56749         var el = this.ensureVisible(row, col, hscroll);
56750         this.focusEl.alignTo(el, "tl-tl");
56751         if(Roo.isGecko){
56752             this.focusEl.focus();
56753         }else{
56754             this.focusEl.focus.defer(1, this.focusEl);
56755         }
56756     },
56757
56758     /**
56759      * Scrolls the specified cell into view
56760      * @param {Number} row The row index
56761      * @param {Number} col The column index
56762      * @param {Boolean} hscroll false to disable horizontal scrolling
56763      */
56764     ensureVisible : function(row, col, hscroll)
56765     {
56766         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
56767         //return null; //disable for testing.
56768         if(typeof row != "number"){
56769             row = row.rowIndex;
56770         }
56771         if(row < 0 && row >= this.ds.getCount()){
56772             return  null;
56773         }
56774         col = (col !== undefined ? col : 0);
56775         var cm = this.grid.colModel;
56776         while(cm.isHidden(col)){
56777             col++;
56778         }
56779
56780         var el = this.getCell(row, col);
56781         if(!el){
56782             return null;
56783         }
56784         var c = this.scroller.dom;
56785
56786         var ctop = parseInt(el.offsetTop, 10);
56787         var cleft = parseInt(el.offsetLeft, 10);
56788         var cbot = ctop + el.offsetHeight;
56789         var cright = cleft + el.offsetWidth;
56790         
56791         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
56792         var stop = parseInt(c.scrollTop, 10);
56793         var sleft = parseInt(c.scrollLeft, 10);
56794         var sbot = stop + ch;
56795         var sright = sleft + c.clientWidth;
56796         /*
56797         Roo.log('GridView.ensureVisible:' +
56798                 ' ctop:' + ctop +
56799                 ' c.clientHeight:' + c.clientHeight +
56800                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
56801                 ' stop:' + stop +
56802                 ' cbot:' + cbot +
56803                 ' sbot:' + sbot +
56804                 ' ch:' + ch  
56805                 );
56806         */
56807         if(ctop < stop){
56808             c.scrollTop = ctop;
56809             //Roo.log("set scrolltop to ctop DISABLE?");
56810         }else if(cbot > sbot){
56811             //Roo.log("set scrolltop to cbot-ch");
56812             c.scrollTop = cbot-ch;
56813         }
56814         
56815         if(hscroll !== false){
56816             if(cleft < sleft){
56817                 c.scrollLeft = cleft;
56818             }else if(cright > sright){
56819                 c.scrollLeft = cright-c.clientWidth;
56820             }
56821         }
56822          
56823         return el;
56824     },
56825
56826     updateColumns : function(){
56827         this.grid.stopEditing();
56828         var cm = this.grid.colModel, colIds = this.getColumnIds();
56829         //var totalWidth = cm.getTotalWidth();
56830         var pos = 0;
56831         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
56832             //if(cm.isHidden(i)) continue;
56833             var w = cm.getColumnWidth(i);
56834             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
56835             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
56836         }
56837         this.updateSplitters();
56838     },
56839
56840     generateRules : function(cm){
56841         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
56842         Roo.util.CSS.removeStyleSheet(rulesId);
56843         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
56844             var cid = cm.getColumnId(i);
56845             var align = '';
56846             if(cm.config[i].align){
56847                 align = 'text-align:'+cm.config[i].align+';';
56848             }
56849             var hidden = '';
56850             if(cm.isHidden(i)){
56851                 hidden = 'display:none;';
56852             }
56853             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
56854             ruleBuf.push(
56855                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
56856                     this.hdSelector, cid, " {\n", align, width, "}\n",
56857                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
56858                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
56859         }
56860         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
56861     },
56862
56863     updateSplitters : function(){
56864         var cm = this.cm, s = this.getSplitters();
56865         if(s){ // splitters not created yet
56866             var pos = 0, locked = true;
56867             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
56868                 if(cm.isHidden(i)) {
56869                     continue;
56870                 }
56871                 var w = cm.getColumnWidth(i); // make sure it's a number
56872                 if(!cm.isLocked(i) && locked){
56873                     pos = 0;
56874                     locked = false;
56875                 }
56876                 pos += w;
56877                 s[i].style.left = (pos-this.splitOffset) + "px";
56878             }
56879         }
56880     },
56881
56882     handleHiddenChange : function(colModel, colIndex, hidden){
56883         if(hidden){
56884             this.hideColumn(colIndex);
56885         }else{
56886             this.unhideColumn(colIndex);
56887         }
56888     },
56889
56890     hideColumn : function(colIndex){
56891         var cid = this.getColumnId(colIndex);
56892         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
56893         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
56894         if(Roo.isSafari){
56895             this.updateHeaders();
56896         }
56897         this.updateSplitters();
56898         this.layout();
56899     },
56900
56901     unhideColumn : function(colIndex){
56902         var cid = this.getColumnId(colIndex);
56903         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
56904         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
56905
56906         if(Roo.isSafari){
56907             this.updateHeaders();
56908         }
56909         this.updateSplitters();
56910         this.layout();
56911     },
56912
56913     insertRows : function(dm, firstRow, lastRow, isUpdate){
56914         if(firstRow == 0 && lastRow == dm.getCount()-1){
56915             this.refresh();
56916         }else{
56917             if(!isUpdate){
56918                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
56919             }
56920             var s = this.getScrollState();
56921             var markup = this.renderRows(firstRow, lastRow);
56922             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
56923             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
56924             this.restoreScroll(s);
56925             if(!isUpdate){
56926                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
56927                 this.syncRowHeights(firstRow, lastRow);
56928                 this.stripeRows(firstRow);
56929                 this.layout();
56930             }
56931         }
56932     },
56933
56934     bufferRows : function(markup, target, index){
56935         var before = null, trows = target.rows, tbody = target.tBodies[0];
56936         if(index < trows.length){
56937             before = trows[index];
56938         }
56939         var b = document.createElement("div");
56940         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
56941         var rows = b.firstChild.rows;
56942         for(var i = 0, len = rows.length; i < len; i++){
56943             if(before){
56944                 tbody.insertBefore(rows[0], before);
56945             }else{
56946                 tbody.appendChild(rows[0]);
56947             }
56948         }
56949         b.innerHTML = "";
56950         b = null;
56951     },
56952
56953     deleteRows : function(dm, firstRow, lastRow){
56954         if(dm.getRowCount()<1){
56955             this.fireEvent("beforerefresh", this);
56956             this.mainBody.update("");
56957             this.lockedBody.update("");
56958             this.fireEvent("refresh", this);
56959         }else{
56960             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
56961             var bt = this.getBodyTable();
56962             var tbody = bt.firstChild;
56963             var rows = bt.rows;
56964             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
56965                 tbody.removeChild(rows[firstRow]);
56966             }
56967             this.stripeRows(firstRow);
56968             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
56969         }
56970     },
56971
56972     updateRows : function(dataSource, firstRow, lastRow){
56973         var s = this.getScrollState();
56974         this.refresh();
56975         this.restoreScroll(s);
56976     },
56977
56978     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
56979         if(!noRefresh){
56980            this.refresh();
56981         }
56982         this.updateHeaderSortState();
56983     },
56984
56985     getScrollState : function(){
56986         
56987         var sb = this.scroller.dom;
56988         return {left: sb.scrollLeft, top: sb.scrollTop};
56989     },
56990
56991     stripeRows : function(startRow){
56992         if(!this.grid.stripeRows || this.ds.getCount() < 1){
56993             return;
56994         }
56995         startRow = startRow || 0;
56996         var rows = this.getBodyTable().rows;
56997         var lrows = this.getLockedTable().rows;
56998         var cls = ' x-grid-row-alt ';
56999         for(var i = startRow, len = rows.length; i < len; i++){
57000             var row = rows[i], lrow = lrows[i];
57001             var isAlt = ((i+1) % 2 == 0);
57002             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
57003             if(isAlt == hasAlt){
57004                 continue;
57005             }
57006             if(isAlt){
57007                 row.className += " x-grid-row-alt";
57008             }else{
57009                 row.className = row.className.replace("x-grid-row-alt", "");
57010             }
57011             if(lrow){
57012                 lrow.className = row.className;
57013             }
57014         }
57015     },
57016
57017     restoreScroll : function(state){
57018         //Roo.log('GridView.restoreScroll');
57019         var sb = this.scroller.dom;
57020         sb.scrollLeft = state.left;
57021         sb.scrollTop = state.top;
57022         this.syncScroll();
57023     },
57024
57025     syncScroll : function(){
57026         //Roo.log('GridView.syncScroll');
57027         var sb = this.scroller.dom;
57028         var sh = this.mainHd.dom;
57029         var bs = this.mainBody.dom;
57030         var lv = this.lockedBody.dom;
57031         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
57032         lv.scrollTop = bs.scrollTop = sb.scrollTop;
57033     },
57034
57035     handleScroll : function(e){
57036         this.syncScroll();
57037         var sb = this.scroller.dom;
57038         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
57039         e.stopEvent();
57040     },
57041
57042     handleWheel : function(e){
57043         var d = e.getWheelDelta();
57044         this.scroller.dom.scrollTop -= d*22;
57045         // set this here to prevent jumpy scrolling on large tables
57046         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
57047         e.stopEvent();
57048     },
57049
57050     renderRows : function(startRow, endRow){
57051         // pull in all the crap needed to render rows
57052         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
57053         var colCount = cm.getColumnCount();
57054
57055         if(ds.getCount() < 1){
57056             return ["", ""];
57057         }
57058
57059         // build a map for all the columns
57060         var cs = [];
57061         for(var i = 0; i < colCount; i++){
57062             var name = cm.getDataIndex(i);
57063             cs[i] = {
57064                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
57065                 renderer : cm.getRenderer(i),
57066                 id : cm.getColumnId(i),
57067                 locked : cm.isLocked(i),
57068                 has_editor : cm.isCellEditable(i)
57069             };
57070         }
57071
57072         startRow = startRow || 0;
57073         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
57074
57075         // records to render
57076         var rs = ds.getRange(startRow, endRow);
57077
57078         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
57079     },
57080
57081     // As much as I hate to duplicate code, this was branched because FireFox really hates
57082     // [].join("") on strings. The performance difference was substantial enough to
57083     // branch this function
57084     doRender : Roo.isGecko ?
57085             function(cs, rs, ds, startRow, colCount, stripe){
57086                 var ts = this.templates, ct = ts.cell, rt = ts.row;
57087                 // buffers
57088                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
57089                 
57090                 var hasListener = this.grid.hasListener('rowclass');
57091                 var rowcfg = {};
57092                 for(var j = 0, len = rs.length; j < len; j++){
57093                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
57094                     for(var i = 0; i < colCount; i++){
57095                         c = cs[i];
57096                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
57097                         p.id = c.id;
57098                         p.css = p.attr = "";
57099                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
57100                         if(p.value == undefined || p.value === "") {
57101                             p.value = "&#160;";
57102                         }
57103                         if(c.has_editor){
57104                             p.css += ' x-grid-editable-cell';
57105                         }
57106                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
57107                             p.css +=  ' x-grid-dirty-cell';
57108                         }
57109                         var markup = ct.apply(p);
57110                         if(!c.locked){
57111                             cb+= markup;
57112                         }else{
57113                             lcb+= markup;
57114                         }
57115                     }
57116                     var alt = [];
57117                     if(stripe && ((rowIndex+1) % 2 == 0)){
57118                         alt.push("x-grid-row-alt")
57119                     }
57120                     if(r.dirty){
57121                         alt.push(  " x-grid-dirty-row");
57122                     }
57123                     rp.cells = lcb;
57124                     if(this.getRowClass){
57125                         alt.push(this.getRowClass(r, rowIndex));
57126                     }
57127                     if (hasListener) {
57128                         rowcfg = {
57129                              
57130                             record: r,
57131                             rowIndex : rowIndex,
57132                             rowClass : ''
57133                         };
57134                         this.grid.fireEvent('rowclass', this, rowcfg);
57135                         alt.push(rowcfg.rowClass);
57136                     }
57137                     rp.alt = alt.join(" ");
57138                     lbuf+= rt.apply(rp);
57139                     rp.cells = cb;
57140                     buf+=  rt.apply(rp);
57141                 }
57142                 return [lbuf, buf];
57143             } :
57144             function(cs, rs, ds, startRow, colCount, stripe){
57145                 var ts = this.templates, ct = ts.cell, rt = ts.row;
57146                 // buffers
57147                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
57148                 var hasListener = this.grid.hasListener('rowclass');
57149  
57150                 var rowcfg = {};
57151                 for(var j = 0, len = rs.length; j < len; j++){
57152                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
57153                     for(var i = 0; i < colCount; i++){
57154                         c = cs[i];
57155                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
57156                         p.id = c.id;
57157                         p.css = p.attr = "";
57158                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
57159                         if(p.value == undefined || p.value === "") {
57160                             p.value = "&#160;";
57161                         }
57162                         //Roo.log(c);
57163                          if(c.has_editor){
57164                             p.css += ' x-grid-editable-cell';
57165                         }
57166                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
57167                             p.css += ' x-grid-dirty-cell' 
57168                         }
57169                         
57170                         var markup = ct.apply(p);
57171                         if(!c.locked){
57172                             cb[cb.length] = markup;
57173                         }else{
57174                             lcb[lcb.length] = markup;
57175                         }
57176                     }
57177                     var alt = [];
57178                     if(stripe && ((rowIndex+1) % 2 == 0)){
57179                         alt.push( "x-grid-row-alt");
57180                     }
57181                     if(r.dirty){
57182                         alt.push(" x-grid-dirty-row");
57183                     }
57184                     rp.cells = lcb;
57185                     if(this.getRowClass){
57186                         alt.push( this.getRowClass(r, rowIndex));
57187                     }
57188                     if (hasListener) {
57189                         rowcfg = {
57190                              
57191                             record: r,
57192                             rowIndex : rowIndex,
57193                             rowClass : ''
57194                         };
57195                         this.grid.fireEvent('rowclass', this, rowcfg);
57196                         alt.push(rowcfg.rowClass);
57197                     }
57198                     
57199                     rp.alt = alt.join(" ");
57200                     rp.cells = lcb.join("");
57201                     lbuf[lbuf.length] = rt.apply(rp);
57202                     rp.cells = cb.join("");
57203                     buf[buf.length] =  rt.apply(rp);
57204                 }
57205                 return [lbuf.join(""), buf.join("")];
57206             },
57207
57208     renderBody : function(){
57209         var markup = this.renderRows();
57210         var bt = this.templates.body;
57211         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
57212     },
57213
57214     /**
57215      * Refreshes the grid
57216      * @param {Boolean} headersToo
57217      */
57218     refresh : function(headersToo){
57219         this.fireEvent("beforerefresh", this);
57220         this.grid.stopEditing();
57221         var result = this.renderBody();
57222         this.lockedBody.update(result[0]);
57223         this.mainBody.update(result[1]);
57224         if(headersToo === true){
57225             this.updateHeaders();
57226             this.updateColumns();
57227             this.updateSplitters();
57228             this.updateHeaderSortState();
57229         }
57230         this.syncRowHeights();
57231         this.layout();
57232         this.fireEvent("refresh", this);
57233     },
57234
57235     handleColumnMove : function(cm, oldIndex, newIndex){
57236         this.indexMap = null;
57237         var s = this.getScrollState();
57238         this.refresh(true);
57239         this.restoreScroll(s);
57240         this.afterMove(newIndex);
57241     },
57242
57243     afterMove : function(colIndex){
57244         if(this.enableMoveAnim && Roo.enableFx){
57245             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
57246         }
57247         // if multisort - fix sortOrder, and reload..
57248         if (this.grid.dataSource.multiSort) {
57249             // the we can call sort again..
57250             var dm = this.grid.dataSource;
57251             var cm = this.grid.colModel;
57252             var so = [];
57253             for(var i = 0; i < cm.config.length; i++ ) {
57254                 
57255                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
57256                     continue; // dont' bother, it's not in sort list or being set.
57257                 }
57258                 
57259                 so.push(cm.config[i].dataIndex);
57260             };
57261             dm.sortOrder = so;
57262             dm.load(dm.lastOptions);
57263             
57264             
57265         }
57266         
57267     },
57268
57269     updateCell : function(dm, rowIndex, dataIndex){
57270         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
57271         if(typeof colIndex == "undefined"){ // not present in grid
57272             return;
57273         }
57274         var cm = this.grid.colModel;
57275         var cell = this.getCell(rowIndex, colIndex);
57276         var cellText = this.getCellText(rowIndex, colIndex);
57277
57278         var p = {
57279             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
57280             id : cm.getColumnId(colIndex),
57281             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
57282         };
57283         var renderer = cm.getRenderer(colIndex);
57284         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
57285         if(typeof val == "undefined" || val === "") {
57286             val = "&#160;";
57287         }
57288         cellText.innerHTML = val;
57289         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
57290         this.syncRowHeights(rowIndex, rowIndex);
57291     },
57292
57293     calcColumnWidth : function(colIndex, maxRowsToMeasure){
57294         var maxWidth = 0;
57295         if(this.grid.autoSizeHeaders){
57296             var h = this.getHeaderCellMeasure(colIndex);
57297             maxWidth = Math.max(maxWidth, h.scrollWidth);
57298         }
57299         var tb, index;
57300         if(this.cm.isLocked(colIndex)){
57301             tb = this.getLockedTable();
57302             index = colIndex;
57303         }else{
57304             tb = this.getBodyTable();
57305             index = colIndex - this.cm.getLockedCount();
57306         }
57307         if(tb && tb.rows){
57308             var rows = tb.rows;
57309             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
57310             for(var i = 0; i < stopIndex; i++){
57311                 var cell = rows[i].childNodes[index].firstChild;
57312                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
57313             }
57314         }
57315         return maxWidth + /*margin for error in IE*/ 5;
57316     },
57317     /**
57318      * Autofit a column to its content.
57319      * @param {Number} colIndex
57320      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
57321      */
57322      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
57323          if(this.cm.isHidden(colIndex)){
57324              return; // can't calc a hidden column
57325          }
57326         if(forceMinSize){
57327             var cid = this.cm.getColumnId(colIndex);
57328             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
57329            if(this.grid.autoSizeHeaders){
57330                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
57331            }
57332         }
57333         var newWidth = this.calcColumnWidth(colIndex);
57334         this.cm.setColumnWidth(colIndex,
57335             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
57336         if(!suppressEvent){
57337             this.grid.fireEvent("columnresize", colIndex, newWidth);
57338         }
57339     },
57340
57341     /**
57342      * Autofits all columns to their content and then expands to fit any extra space in the grid
57343      */
57344      autoSizeColumns : function(){
57345         var cm = this.grid.colModel;
57346         var colCount = cm.getColumnCount();
57347         for(var i = 0; i < colCount; i++){
57348             this.autoSizeColumn(i, true, true);
57349         }
57350         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
57351             this.fitColumns();
57352         }else{
57353             this.updateColumns();
57354             this.layout();
57355         }
57356     },
57357
57358     /**
57359      * Autofits all columns to the grid's width proportionate with their current size
57360      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
57361      */
57362     fitColumns : function(reserveScrollSpace){
57363         var cm = this.grid.colModel;
57364         var colCount = cm.getColumnCount();
57365         var cols = [];
57366         var width = 0;
57367         var i, w;
57368         for (i = 0; i < colCount; i++){
57369             if(!cm.isHidden(i) && !cm.isFixed(i)){
57370                 w = cm.getColumnWidth(i);
57371                 cols.push(i);
57372                 cols.push(w);
57373                 width += w;
57374             }
57375         }
57376         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
57377         if(reserveScrollSpace){
57378             avail -= 17;
57379         }
57380         var frac = (avail - cm.getTotalWidth())/width;
57381         while (cols.length){
57382             w = cols.pop();
57383             i = cols.pop();
57384             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
57385         }
57386         this.updateColumns();
57387         this.layout();
57388     },
57389
57390     onRowSelect : function(rowIndex){
57391         var row = this.getRowComposite(rowIndex);
57392         row.addClass("x-grid-row-selected");
57393     },
57394
57395     onRowDeselect : function(rowIndex){
57396         var row = this.getRowComposite(rowIndex);
57397         row.removeClass("x-grid-row-selected");
57398     },
57399
57400     onCellSelect : function(row, col){
57401         var cell = this.getCell(row, col);
57402         if(cell){
57403             Roo.fly(cell).addClass("x-grid-cell-selected");
57404         }
57405     },
57406
57407     onCellDeselect : function(row, col){
57408         var cell = this.getCell(row, col);
57409         if(cell){
57410             Roo.fly(cell).removeClass("x-grid-cell-selected");
57411         }
57412     },
57413
57414     updateHeaderSortState : function(){
57415         
57416         // sort state can be single { field: xxx, direction : yyy}
57417         // or   { xxx=>ASC , yyy : DESC ..... }
57418         
57419         var mstate = {};
57420         if (!this.ds.multiSort) { 
57421             var state = this.ds.getSortState();
57422             if(!state){
57423                 return;
57424             }
57425             mstate[state.field] = state.direction;
57426             // FIXME... - this is not used here.. but might be elsewhere..
57427             this.sortState = state;
57428             
57429         } else {
57430             mstate = this.ds.sortToggle;
57431         }
57432         //remove existing sort classes..
57433         
57434         var sc = this.sortClasses;
57435         var hds = this.el.select(this.headerSelector).removeClass(sc);
57436         
57437         for(var f in mstate) {
57438         
57439             var sortColumn = this.cm.findColumnIndex(f);
57440             
57441             if(sortColumn != -1){
57442                 var sortDir = mstate[f];        
57443                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
57444             }
57445         }
57446         
57447          
57448         
57449     },
57450
57451
57452     handleHeaderClick : function(g, index,e){
57453         
57454         Roo.log("header click");
57455         
57456         if (Roo.isTouch) {
57457             // touch events on header are handled by context
57458             this.handleHdCtx(g,index,e);
57459             return;
57460         }
57461         
57462         
57463         if(this.headersDisabled){
57464             return;
57465         }
57466         var dm = g.dataSource, cm = g.colModel;
57467         if(!cm.isSortable(index)){
57468             return;
57469         }
57470         g.stopEditing();
57471         
57472         if (dm.multiSort) {
57473             // update the sortOrder
57474             var so = [];
57475             for(var i = 0; i < cm.config.length; i++ ) {
57476                 
57477                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
57478                     continue; // dont' bother, it's not in sort list or being set.
57479                 }
57480                 
57481                 so.push(cm.config[i].dataIndex);
57482             };
57483             dm.sortOrder = so;
57484         }
57485         
57486         
57487         dm.sort(cm.getDataIndex(index));
57488     },
57489
57490
57491     destroy : function(){
57492         if(this.colMenu){
57493             this.colMenu.removeAll();
57494             Roo.menu.MenuMgr.unregister(this.colMenu);
57495             this.colMenu.getEl().remove();
57496             delete this.colMenu;
57497         }
57498         if(this.hmenu){
57499             this.hmenu.removeAll();
57500             Roo.menu.MenuMgr.unregister(this.hmenu);
57501             this.hmenu.getEl().remove();
57502             delete this.hmenu;
57503         }
57504         if(this.grid.enableColumnMove){
57505             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
57506             if(dds){
57507                 for(var dd in dds){
57508                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
57509                         var elid = dds[dd].dragElId;
57510                         dds[dd].unreg();
57511                         Roo.get(elid).remove();
57512                     } else if(dds[dd].config.isTarget){
57513                         dds[dd].proxyTop.remove();
57514                         dds[dd].proxyBottom.remove();
57515                         dds[dd].unreg();
57516                     }
57517                     if(Roo.dd.DDM.locationCache[dd]){
57518                         delete Roo.dd.DDM.locationCache[dd];
57519                     }
57520                 }
57521                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
57522             }
57523         }
57524         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
57525         this.bind(null, null);
57526         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
57527     },
57528
57529     handleLockChange : function(){
57530         this.refresh(true);
57531     },
57532
57533     onDenyColumnLock : function(){
57534
57535     },
57536
57537     onDenyColumnHide : function(){
57538
57539     },
57540
57541     handleHdMenuClick : function(item){
57542         var index = this.hdCtxIndex;
57543         var cm = this.cm, ds = this.ds;
57544         switch(item.id){
57545             case "asc":
57546                 ds.sort(cm.getDataIndex(index), "ASC");
57547                 break;
57548             case "desc":
57549                 ds.sort(cm.getDataIndex(index), "DESC");
57550                 break;
57551             case "lock":
57552                 var lc = cm.getLockedCount();
57553                 if(cm.getColumnCount(true) <= lc+1){
57554                     this.onDenyColumnLock();
57555                     return;
57556                 }
57557                 if(lc != index){
57558                     cm.setLocked(index, true, true);
57559                     cm.moveColumn(index, lc);
57560                     this.grid.fireEvent("columnmove", index, lc);
57561                 }else{
57562                     cm.setLocked(index, true);
57563                 }
57564             break;
57565             case "unlock":
57566                 var lc = cm.getLockedCount();
57567                 if((lc-1) != index){
57568                     cm.setLocked(index, false, true);
57569                     cm.moveColumn(index, lc-1);
57570                     this.grid.fireEvent("columnmove", index, lc-1);
57571                 }else{
57572                     cm.setLocked(index, false);
57573                 }
57574             break;
57575             case 'wider': // used to expand cols on touch..
57576             case 'narrow':
57577                 var cw = cm.getColumnWidth(index);
57578                 cw += (item.id == 'wider' ? 1 : -1) * 50;
57579                 cw = Math.max(0, cw);
57580                 cw = Math.min(cw,4000);
57581                 cm.setColumnWidth(index, cw);
57582                 break;
57583                 
57584             default:
57585                 index = cm.getIndexById(item.id.substr(4));
57586                 if(index != -1){
57587                     if(item.checked && cm.getColumnCount(true) <= 1){
57588                         this.onDenyColumnHide();
57589                         return false;
57590                     }
57591                     cm.setHidden(index, item.checked);
57592                 }
57593         }
57594         return true;
57595     },
57596
57597     beforeColMenuShow : function(){
57598         var cm = this.cm,  colCount = cm.getColumnCount();
57599         this.colMenu.removeAll();
57600         for(var i = 0; i < colCount; i++){
57601             this.colMenu.add(new Roo.menu.CheckItem({
57602                 id: "col-"+cm.getColumnId(i),
57603                 text: cm.getColumnHeader(i),
57604                 checked: !cm.isHidden(i),
57605                 hideOnClick:false
57606             }));
57607         }
57608     },
57609
57610     handleHdCtx : function(g, index, e){
57611         e.stopEvent();
57612         var hd = this.getHeaderCell(index);
57613         this.hdCtxIndex = index;
57614         var ms = this.hmenu.items, cm = this.cm;
57615         ms.get("asc").setDisabled(!cm.isSortable(index));
57616         ms.get("desc").setDisabled(!cm.isSortable(index));
57617         if(this.grid.enableColLock !== false){
57618             ms.get("lock").setDisabled(cm.isLocked(index));
57619             ms.get("unlock").setDisabled(!cm.isLocked(index));
57620         }
57621         this.hmenu.show(hd, "tl-bl");
57622     },
57623
57624     handleHdOver : function(e){
57625         var hd = this.findHeaderCell(e.getTarget());
57626         if(hd && !this.headersDisabled){
57627             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
57628                this.fly(hd).addClass("x-grid-hd-over");
57629             }
57630         }
57631     },
57632
57633     handleHdOut : function(e){
57634         var hd = this.findHeaderCell(e.getTarget());
57635         if(hd){
57636             this.fly(hd).removeClass("x-grid-hd-over");
57637         }
57638     },
57639
57640     handleSplitDblClick : function(e, t){
57641         var i = this.getCellIndex(t);
57642         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
57643             this.autoSizeColumn(i, true);
57644             this.layout();
57645         }
57646     },
57647
57648     render : function(){
57649
57650         var cm = this.cm;
57651         var colCount = cm.getColumnCount();
57652
57653         if(this.grid.monitorWindowResize === true){
57654             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
57655         }
57656         var header = this.renderHeaders();
57657         var body = this.templates.body.apply({rows:""});
57658         var html = this.templates.master.apply({
57659             lockedBody: body,
57660             body: body,
57661             lockedHeader: header[0],
57662             header: header[1]
57663         });
57664
57665         //this.updateColumns();
57666
57667         this.grid.getGridEl().dom.innerHTML = html;
57668
57669         this.initElements();
57670         
57671         // a kludge to fix the random scolling effect in webkit
57672         this.el.on("scroll", function() {
57673             this.el.dom.scrollTop=0; // hopefully not recursive..
57674         },this);
57675
57676         this.scroller.on("scroll", this.handleScroll, this);
57677         this.lockedBody.on("mousewheel", this.handleWheel, this);
57678         this.mainBody.on("mousewheel", this.handleWheel, this);
57679
57680         this.mainHd.on("mouseover", this.handleHdOver, this);
57681         this.mainHd.on("mouseout", this.handleHdOut, this);
57682         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
57683                 {delegate: "."+this.splitClass});
57684
57685         this.lockedHd.on("mouseover", this.handleHdOver, this);
57686         this.lockedHd.on("mouseout", this.handleHdOut, this);
57687         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
57688                 {delegate: "."+this.splitClass});
57689
57690         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
57691             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
57692         }
57693
57694         this.updateSplitters();
57695
57696         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
57697             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
57698             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
57699         }
57700
57701         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
57702             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
57703             this.hmenu.add(
57704                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
57705                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
57706             );
57707             if(this.grid.enableColLock !== false){
57708                 this.hmenu.add('-',
57709                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
57710                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
57711                 );
57712             }
57713             if (Roo.isTouch) {
57714                  this.hmenu.add('-',
57715                     {id:"wider", text: this.columnsWiderText},
57716                     {id:"narrow", text: this.columnsNarrowText }
57717                 );
57718                 
57719                  
57720             }
57721             
57722             if(this.grid.enableColumnHide !== false){
57723
57724                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
57725                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
57726                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
57727
57728                 this.hmenu.add('-',
57729                     {id:"columns", text: this.columnsText, menu: this.colMenu}
57730                 );
57731             }
57732             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
57733
57734             this.grid.on("headercontextmenu", this.handleHdCtx, this);
57735         }
57736
57737         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
57738             this.dd = new Roo.grid.GridDragZone(this.grid, {
57739                 ddGroup : this.grid.ddGroup || 'GridDD'
57740             });
57741             
57742         }
57743
57744         /*
57745         for(var i = 0; i < colCount; i++){
57746             if(cm.isHidden(i)){
57747                 this.hideColumn(i);
57748             }
57749             if(cm.config[i].align){
57750                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
57751                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
57752             }
57753         }*/
57754         
57755         this.updateHeaderSortState();
57756
57757         this.beforeInitialResize();
57758         this.layout(true);
57759
57760         // two part rendering gives faster view to the user
57761         this.renderPhase2.defer(1, this);
57762     },
57763
57764     renderPhase2 : function(){
57765         // render the rows now
57766         this.refresh();
57767         if(this.grid.autoSizeColumns){
57768             this.autoSizeColumns();
57769         }
57770     },
57771
57772     beforeInitialResize : function(){
57773
57774     },
57775
57776     onColumnSplitterMoved : function(i, w){
57777         this.userResized = true;
57778         var cm = this.grid.colModel;
57779         cm.setColumnWidth(i, w, true);
57780         var cid = cm.getColumnId(i);
57781         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
57782         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
57783         this.updateSplitters();
57784         this.layout();
57785         this.grid.fireEvent("columnresize", i, w);
57786     },
57787
57788     syncRowHeights : function(startIndex, endIndex){
57789         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
57790             startIndex = startIndex || 0;
57791             var mrows = this.getBodyTable().rows;
57792             var lrows = this.getLockedTable().rows;
57793             var len = mrows.length-1;
57794             endIndex = Math.min(endIndex || len, len);
57795             for(var i = startIndex; i <= endIndex; i++){
57796                 var m = mrows[i], l = lrows[i];
57797                 var h = Math.max(m.offsetHeight, l.offsetHeight);
57798                 m.style.height = l.style.height = h + "px";
57799             }
57800         }
57801     },
57802
57803     layout : function(initialRender, is2ndPass)
57804     {
57805         var g = this.grid;
57806         var auto = g.autoHeight;
57807         var scrollOffset = 16;
57808         var c = g.getGridEl(), cm = this.cm,
57809                 expandCol = g.autoExpandColumn,
57810                 gv = this;
57811         //c.beginMeasure();
57812
57813         if(!c.dom.offsetWidth){ // display:none?
57814             if(initialRender){
57815                 this.lockedWrap.show();
57816                 this.mainWrap.show();
57817             }
57818             return;
57819         }
57820
57821         var hasLock = this.cm.isLocked(0);
57822
57823         var tbh = this.headerPanel.getHeight();
57824         var bbh = this.footerPanel.getHeight();
57825
57826         if(auto){
57827             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
57828             var newHeight = ch + c.getBorderWidth("tb");
57829             if(g.maxHeight){
57830                 newHeight = Math.min(g.maxHeight, newHeight);
57831             }
57832             c.setHeight(newHeight);
57833         }
57834
57835         if(g.autoWidth){
57836             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
57837         }
57838
57839         var s = this.scroller;
57840
57841         var csize = c.getSize(true);
57842
57843         this.el.setSize(csize.width, csize.height);
57844
57845         this.headerPanel.setWidth(csize.width);
57846         this.footerPanel.setWidth(csize.width);
57847
57848         var hdHeight = this.mainHd.getHeight();
57849         var vw = csize.width;
57850         var vh = csize.height - (tbh + bbh);
57851
57852         s.setSize(vw, vh);
57853
57854         var bt = this.getBodyTable();
57855         
57856         if(cm.getLockedCount() == cm.config.length){
57857             bt = this.getLockedTable();
57858         }
57859         
57860         var ltWidth = hasLock ?
57861                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
57862
57863         var scrollHeight = bt.offsetHeight;
57864         var scrollWidth = ltWidth + bt.offsetWidth;
57865         var vscroll = false, hscroll = false;
57866
57867         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
57868
57869         var lw = this.lockedWrap, mw = this.mainWrap;
57870         var lb = this.lockedBody, mb = this.mainBody;
57871
57872         setTimeout(function(){
57873             var t = s.dom.offsetTop;
57874             var w = s.dom.clientWidth,
57875                 h = s.dom.clientHeight;
57876
57877             lw.setTop(t);
57878             lw.setSize(ltWidth, h);
57879
57880             mw.setLeftTop(ltWidth, t);
57881             mw.setSize(w-ltWidth, h);
57882
57883             lb.setHeight(h-hdHeight);
57884             mb.setHeight(h-hdHeight);
57885
57886             if(is2ndPass !== true && !gv.userResized && expandCol){
57887                 // high speed resize without full column calculation
57888                 
57889                 var ci = cm.getIndexById(expandCol);
57890                 if (ci < 0) {
57891                     ci = cm.findColumnIndex(expandCol);
57892                 }
57893                 ci = Math.max(0, ci); // make sure it's got at least the first col.
57894                 var expandId = cm.getColumnId(ci);
57895                 var  tw = cm.getTotalWidth(false);
57896                 var currentWidth = cm.getColumnWidth(ci);
57897                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
57898                 if(currentWidth != cw){
57899                     cm.setColumnWidth(ci, cw, true);
57900                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
57901                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
57902                     gv.updateSplitters();
57903                     gv.layout(false, true);
57904                 }
57905             }
57906
57907             if(initialRender){
57908                 lw.show();
57909                 mw.show();
57910             }
57911             //c.endMeasure();
57912         }, 10);
57913     },
57914
57915     onWindowResize : function(){
57916         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
57917             return;
57918         }
57919         this.layout();
57920     },
57921
57922     appendFooter : function(parentEl){
57923         return null;
57924     },
57925
57926     sortAscText : "Sort Ascending",
57927     sortDescText : "Sort Descending",
57928     lockText : "Lock Column",
57929     unlockText : "Unlock Column",
57930     columnsText : "Columns",
57931  
57932     columnsWiderText : "Wider",
57933     columnsNarrowText : "Thinner"
57934 });
57935
57936
57937 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
57938     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
57939     this.proxy.el.addClass('x-grid3-col-dd');
57940 };
57941
57942 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
57943     handleMouseDown : function(e){
57944
57945     },
57946
57947     callHandleMouseDown : function(e){
57948         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
57949     }
57950 });
57951 /*
57952  * Based on:
57953  * Ext JS Library 1.1.1
57954  * Copyright(c) 2006-2007, Ext JS, LLC.
57955  *
57956  * Originally Released Under LGPL - original licence link has changed is not relivant.
57957  *
57958  * Fork - LGPL
57959  * <script type="text/javascript">
57960  */
57961  
57962 // private
57963 // This is a support class used internally by the Grid components
57964 Roo.grid.SplitDragZone = function(grid, hd, hd2){
57965     this.grid = grid;
57966     this.view = grid.getView();
57967     this.proxy = this.view.resizeProxy;
57968     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
57969         "gridSplitters" + this.grid.getGridEl().id, {
57970         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
57971     });
57972     this.setHandleElId(Roo.id(hd));
57973     this.setOuterHandleElId(Roo.id(hd2));
57974     this.scroll = false;
57975 };
57976 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
57977     fly: Roo.Element.fly,
57978
57979     b4StartDrag : function(x, y){
57980         this.view.headersDisabled = true;
57981         this.proxy.setHeight(this.view.mainWrap.getHeight());
57982         var w = this.cm.getColumnWidth(this.cellIndex);
57983         var minw = Math.max(w-this.grid.minColumnWidth, 0);
57984         this.resetConstraints();
57985         this.setXConstraint(minw, 1000);
57986         this.setYConstraint(0, 0);
57987         this.minX = x - minw;
57988         this.maxX = x + 1000;
57989         this.startPos = x;
57990         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
57991     },
57992
57993
57994     handleMouseDown : function(e){
57995         ev = Roo.EventObject.setEvent(e);
57996         var t = this.fly(ev.getTarget());
57997         if(t.hasClass("x-grid-split")){
57998             this.cellIndex = this.view.getCellIndex(t.dom);
57999             this.split = t.dom;
58000             this.cm = this.grid.colModel;
58001             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
58002                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
58003             }
58004         }
58005     },
58006
58007     endDrag : function(e){
58008         this.view.headersDisabled = false;
58009         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
58010         var diff = endX - this.startPos;
58011         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
58012     },
58013
58014     autoOffset : function(){
58015         this.setDelta(0,0);
58016     }
58017 });/*
58018  * Based on:
58019  * Ext JS Library 1.1.1
58020  * Copyright(c) 2006-2007, Ext JS, LLC.
58021  *
58022  * Originally Released Under LGPL - original licence link has changed is not relivant.
58023  *
58024  * Fork - LGPL
58025  * <script type="text/javascript">
58026  */
58027  
58028 // private
58029 // This is a support class used internally by the Grid components
58030 Roo.grid.GridDragZone = function(grid, config){
58031     this.view = grid.getView();
58032     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
58033     if(this.view.lockedBody){
58034         this.setHandleElId(Roo.id(this.view.mainBody.dom));
58035         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
58036     }
58037     this.scroll = false;
58038     this.grid = grid;
58039     this.ddel = document.createElement('div');
58040     this.ddel.className = 'x-grid-dd-wrap';
58041 };
58042
58043 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
58044     ddGroup : "GridDD",
58045
58046     getDragData : function(e){
58047         var t = Roo.lib.Event.getTarget(e);
58048         var rowIndex = this.view.findRowIndex(t);
58049         var sm = this.grid.selModel;
58050             
58051         //Roo.log(rowIndex);
58052         
58053         if (sm.getSelectedCell) {
58054             // cell selection..
58055             if (!sm.getSelectedCell()) {
58056                 return false;
58057             }
58058             if (rowIndex != sm.getSelectedCell()[0]) {
58059                 return false;
58060             }
58061         
58062         }
58063         if (sm.getSelections && sm.getSelections().length < 1) {
58064             return false;
58065         }
58066         
58067         
58068         // before it used to all dragging of unseleted... - now we dont do that.
58069         if(rowIndex !== false){
58070             
58071             // if editorgrid.. 
58072             
58073             
58074             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
58075                
58076             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
58077               //  
58078             //}
58079             if (e.hasModifier()){
58080                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
58081             }
58082             
58083             Roo.log("getDragData");
58084             
58085             return {
58086                 grid: this.grid,
58087                 ddel: this.ddel,
58088                 rowIndex: rowIndex,
58089                 selections: sm.getSelections ? sm.getSelections() : (
58090                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : [])
58091             };
58092         }
58093         return false;
58094     },
58095     
58096     
58097     onInitDrag : function(e){
58098         var data = this.dragData;
58099         this.ddel.innerHTML = this.grid.getDragDropText();
58100         this.proxy.update(this.ddel);
58101         // fire start drag?
58102     },
58103
58104     afterRepair : function(){
58105         this.dragging = false;
58106     },
58107
58108     getRepairXY : function(e, data){
58109         return false;
58110     },
58111
58112     onEndDrag : function(data, e){
58113         // fire end drag?
58114     },
58115
58116     onValidDrop : function(dd, e, id){
58117         // fire drag drop?
58118         this.hideProxy();
58119     },
58120
58121     beforeInvalidDrop : function(e, id){
58122
58123     }
58124 });/*
58125  * Based on:
58126  * Ext JS Library 1.1.1
58127  * Copyright(c) 2006-2007, Ext JS, LLC.
58128  *
58129  * Originally Released Under LGPL - original licence link has changed is not relivant.
58130  *
58131  * Fork - LGPL
58132  * <script type="text/javascript">
58133  */
58134  
58135
58136 /**
58137  * @class Roo.grid.ColumnModel
58138  * @extends Roo.util.Observable
58139  * This is the default implementation of a ColumnModel used by the Grid. It defines
58140  * the columns in the grid.
58141  * <br>Usage:<br>
58142  <pre><code>
58143  var colModel = new Roo.grid.ColumnModel([
58144         {header: "Ticker", width: 60, sortable: true, locked: true},
58145         {header: "Company Name", width: 150, sortable: true},
58146         {header: "Market Cap.", width: 100, sortable: true},
58147         {header: "$ Sales", width: 100, sortable: true, renderer: money},
58148         {header: "Employees", width: 100, sortable: true, resizable: false}
58149  ]);
58150  </code></pre>
58151  * <p>
58152  
58153  * The config options listed for this class are options which may appear in each
58154  * individual column definition.
58155  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
58156  * @constructor
58157  * @param {Object} config An Array of column config objects. See this class's
58158  * config objects for details.
58159 */
58160 Roo.grid.ColumnModel = function(config){
58161         /**
58162      * The config passed into the constructor
58163      */
58164     this.config = config;
58165     this.lookup = {};
58166
58167     // if no id, create one
58168     // if the column does not have a dataIndex mapping,
58169     // map it to the order it is in the config
58170     for(var i = 0, len = config.length; i < len; i++){
58171         var c = config[i];
58172         if(typeof c.dataIndex == "undefined"){
58173             c.dataIndex = i;
58174         }
58175         if(typeof c.renderer == "string"){
58176             c.renderer = Roo.util.Format[c.renderer];
58177         }
58178         if(typeof c.id == "undefined"){
58179             c.id = Roo.id();
58180         }
58181         if(c.editor && c.editor.xtype){
58182             c.editor  = Roo.factory(c.editor, Roo.grid);
58183         }
58184         if(c.editor && c.editor.isFormField){
58185             c.editor = new Roo.grid.GridEditor(c.editor);
58186         }
58187         this.lookup[c.id] = c;
58188     }
58189
58190     /**
58191      * The width of columns which have no width specified (defaults to 100)
58192      * @type Number
58193      */
58194     this.defaultWidth = 100;
58195
58196     /**
58197      * Default sortable of columns which have no sortable specified (defaults to false)
58198      * @type Boolean
58199      */
58200     this.defaultSortable = false;
58201
58202     this.addEvents({
58203         /**
58204              * @event widthchange
58205              * Fires when the width of a column changes.
58206              * @param {ColumnModel} this
58207              * @param {Number} columnIndex The column index
58208              * @param {Number} newWidth The new width
58209              */
58210             "widthchange": true,
58211         /**
58212              * @event headerchange
58213              * Fires when the text of a header changes.
58214              * @param {ColumnModel} this
58215              * @param {Number} columnIndex The column index
58216              * @param {Number} newText The new header text
58217              */
58218             "headerchange": true,
58219         /**
58220              * @event hiddenchange
58221              * Fires when a column is hidden or "unhidden".
58222              * @param {ColumnModel} this
58223              * @param {Number} columnIndex The column index
58224              * @param {Boolean} hidden true if hidden, false otherwise
58225              */
58226             "hiddenchange": true,
58227             /**
58228          * @event columnmoved
58229          * Fires when a column is moved.
58230          * @param {ColumnModel} this
58231          * @param {Number} oldIndex
58232          * @param {Number} newIndex
58233          */
58234         "columnmoved" : true,
58235         /**
58236          * @event columlockchange
58237          * Fires when a column's locked state is changed
58238          * @param {ColumnModel} this
58239          * @param {Number} colIndex
58240          * @param {Boolean} locked true if locked
58241          */
58242         "columnlockchange" : true
58243     });
58244     Roo.grid.ColumnModel.superclass.constructor.call(this);
58245 };
58246 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
58247     /**
58248      * @cfg {String} header The header text to display in the Grid view.
58249      */
58250     /**
58251      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
58252      * {@link Roo.data.Record} definition from which to draw the column's value. If not
58253      * specified, the column's index is used as an index into the Record's data Array.
58254      */
58255     /**
58256      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
58257      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
58258      */
58259     /**
58260      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
58261      * Defaults to the value of the {@link #defaultSortable} property.
58262      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
58263      */
58264     /**
58265      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
58266      */
58267     /**
58268      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
58269      */
58270     /**
58271      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
58272      */
58273     /**
58274      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
58275      */
58276     /**
58277      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
58278      * given the cell's data value. See {@link #setRenderer}. If not specified, the
58279      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
58280      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
58281      */
58282        /**
58283      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
58284      */
58285     /**
58286      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
58287      */
58288     /**
58289      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
58290      */
58291     /**
58292      * @cfg {String} cursor (Optional)
58293      */
58294     /**
58295      * @cfg {String} tooltip (Optional)
58296      */
58297     /**
58298      * @cfg {Number} xs (Optional)
58299      */
58300     /**
58301      * @cfg {Number} sm (Optional)
58302      */
58303     /**
58304      * @cfg {Number} md (Optional)
58305      */
58306     /**
58307      * @cfg {Number} lg (Optional)
58308      */
58309     /**
58310      * Returns the id of the column at the specified index.
58311      * @param {Number} index The column index
58312      * @return {String} the id
58313      */
58314     getColumnId : function(index){
58315         return this.config[index].id;
58316     },
58317
58318     /**
58319      * Returns the column for a specified id.
58320      * @param {String} id The column id
58321      * @return {Object} the column
58322      */
58323     getColumnById : function(id){
58324         return this.lookup[id];
58325     },
58326
58327     
58328     /**
58329      * Returns the column for a specified dataIndex.
58330      * @param {String} dataIndex The column dataIndex
58331      * @return {Object|Boolean} the column or false if not found
58332      */
58333     getColumnByDataIndex: function(dataIndex){
58334         var index = this.findColumnIndex(dataIndex);
58335         return index > -1 ? this.config[index] : false;
58336     },
58337     
58338     /**
58339      * Returns the index for a specified column id.
58340      * @param {String} id The column id
58341      * @return {Number} the index, or -1 if not found
58342      */
58343     getIndexById : function(id){
58344         for(var i = 0, len = this.config.length; i < len; i++){
58345             if(this.config[i].id == id){
58346                 return i;
58347             }
58348         }
58349         return -1;
58350     },
58351     
58352     /**
58353      * Returns the index for a specified column dataIndex.
58354      * @param {String} dataIndex The column dataIndex
58355      * @return {Number} the index, or -1 if not found
58356      */
58357     
58358     findColumnIndex : function(dataIndex){
58359         for(var i = 0, len = this.config.length; i < len; i++){
58360             if(this.config[i].dataIndex == dataIndex){
58361                 return i;
58362             }
58363         }
58364         return -1;
58365     },
58366     
58367     
58368     moveColumn : function(oldIndex, newIndex){
58369         var c = this.config[oldIndex];
58370         this.config.splice(oldIndex, 1);
58371         this.config.splice(newIndex, 0, c);
58372         this.dataMap = null;
58373         this.fireEvent("columnmoved", this, oldIndex, newIndex);
58374     },
58375
58376     isLocked : function(colIndex){
58377         return this.config[colIndex].locked === true;
58378     },
58379
58380     setLocked : function(colIndex, value, suppressEvent){
58381         if(this.isLocked(colIndex) == value){
58382             return;
58383         }
58384         this.config[colIndex].locked = value;
58385         if(!suppressEvent){
58386             this.fireEvent("columnlockchange", this, colIndex, value);
58387         }
58388     },
58389
58390     getTotalLockedWidth : function(){
58391         var totalWidth = 0;
58392         for(var i = 0; i < this.config.length; i++){
58393             if(this.isLocked(i) && !this.isHidden(i)){
58394                 this.totalWidth += this.getColumnWidth(i);
58395             }
58396         }
58397         return totalWidth;
58398     },
58399
58400     getLockedCount : function(){
58401         for(var i = 0, len = this.config.length; i < len; i++){
58402             if(!this.isLocked(i)){
58403                 return i;
58404             }
58405         }
58406         
58407         return this.config.length;
58408     },
58409
58410     /**
58411      * Returns the number of columns.
58412      * @return {Number}
58413      */
58414     getColumnCount : function(visibleOnly){
58415         if(visibleOnly === true){
58416             var c = 0;
58417             for(var i = 0, len = this.config.length; i < len; i++){
58418                 if(!this.isHidden(i)){
58419                     c++;
58420                 }
58421             }
58422             return c;
58423         }
58424         return this.config.length;
58425     },
58426
58427     /**
58428      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
58429      * @param {Function} fn
58430      * @param {Object} scope (optional)
58431      * @return {Array} result
58432      */
58433     getColumnsBy : function(fn, scope){
58434         var r = [];
58435         for(var i = 0, len = this.config.length; i < len; i++){
58436             var c = this.config[i];
58437             if(fn.call(scope||this, c, i) === true){
58438                 r[r.length] = c;
58439             }
58440         }
58441         return r;
58442     },
58443
58444     /**
58445      * Returns true if the specified column is sortable.
58446      * @param {Number} col The column index
58447      * @return {Boolean}
58448      */
58449     isSortable : function(col){
58450         if(typeof this.config[col].sortable == "undefined"){
58451             return this.defaultSortable;
58452         }
58453         return this.config[col].sortable;
58454     },
58455
58456     /**
58457      * Returns the rendering (formatting) function defined for the column.
58458      * @param {Number} col The column index.
58459      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
58460      */
58461     getRenderer : function(col){
58462         if(!this.config[col].renderer){
58463             return Roo.grid.ColumnModel.defaultRenderer;
58464         }
58465         return this.config[col].renderer;
58466     },
58467
58468     /**
58469      * Sets the rendering (formatting) function for a column.
58470      * @param {Number} col The column index
58471      * @param {Function} fn The function to use to process the cell's raw data
58472      * to return HTML markup for the grid view. The render function is called with
58473      * the following parameters:<ul>
58474      * <li>Data value.</li>
58475      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
58476      * <li>css A CSS style string to apply to the table cell.</li>
58477      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
58478      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
58479      * <li>Row index</li>
58480      * <li>Column index</li>
58481      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
58482      */
58483     setRenderer : function(col, fn){
58484         this.config[col].renderer = fn;
58485     },
58486
58487     /**
58488      * Returns the width for the specified column.
58489      * @param {Number} col The column index
58490      * @return {Number}
58491      */
58492     getColumnWidth : function(col){
58493         return this.config[col].width * 1 || this.defaultWidth;
58494     },
58495
58496     /**
58497      * Sets the width for a column.
58498      * @param {Number} col The column index
58499      * @param {Number} width The new width
58500      */
58501     setColumnWidth : function(col, width, suppressEvent){
58502         this.config[col].width = width;
58503         this.totalWidth = null;
58504         if(!suppressEvent){
58505              this.fireEvent("widthchange", this, col, width);
58506         }
58507     },
58508
58509     /**
58510      * Returns the total width of all columns.
58511      * @param {Boolean} includeHidden True to include hidden column widths
58512      * @return {Number}
58513      */
58514     getTotalWidth : function(includeHidden){
58515         if(!this.totalWidth){
58516             this.totalWidth = 0;
58517             for(var i = 0, len = this.config.length; i < len; i++){
58518                 if(includeHidden || !this.isHidden(i)){
58519                     this.totalWidth += this.getColumnWidth(i);
58520                 }
58521             }
58522         }
58523         return this.totalWidth;
58524     },
58525
58526     /**
58527      * Returns the header for the specified column.
58528      * @param {Number} col The column index
58529      * @return {String}
58530      */
58531     getColumnHeader : function(col){
58532         return this.config[col].header;
58533     },
58534
58535     /**
58536      * Sets the header for a column.
58537      * @param {Number} col The column index
58538      * @param {String} header The new header
58539      */
58540     setColumnHeader : function(col, header){
58541         this.config[col].header = header;
58542         this.fireEvent("headerchange", this, col, header);
58543     },
58544
58545     /**
58546      * Returns the tooltip for the specified column.
58547      * @param {Number} col The column index
58548      * @return {String}
58549      */
58550     getColumnTooltip : function(col){
58551             return this.config[col].tooltip;
58552     },
58553     /**
58554      * Sets the tooltip for a column.
58555      * @param {Number} col The column index
58556      * @param {String} tooltip The new tooltip
58557      */
58558     setColumnTooltip : function(col, tooltip){
58559             this.config[col].tooltip = tooltip;
58560     },
58561
58562     /**
58563      * Returns the dataIndex for the specified column.
58564      * @param {Number} col The column index
58565      * @return {Number}
58566      */
58567     getDataIndex : function(col){
58568         return this.config[col].dataIndex;
58569     },
58570
58571     /**
58572      * Sets the dataIndex for a column.
58573      * @param {Number} col The column index
58574      * @param {Number} dataIndex The new dataIndex
58575      */
58576     setDataIndex : function(col, dataIndex){
58577         this.config[col].dataIndex = dataIndex;
58578     },
58579
58580     
58581     
58582     /**
58583      * Returns true if the cell is editable.
58584      * @param {Number} colIndex The column index
58585      * @param {Number} rowIndex The row index - this is nto actually used..?
58586      * @return {Boolean}
58587      */
58588     isCellEditable : function(colIndex, rowIndex){
58589         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
58590     },
58591
58592     /**
58593      * Returns the editor defined for the cell/column.
58594      * return false or null to disable editing.
58595      * @param {Number} colIndex The column index
58596      * @param {Number} rowIndex The row index
58597      * @return {Object}
58598      */
58599     getCellEditor : function(colIndex, rowIndex){
58600         return this.config[colIndex].editor;
58601     },
58602
58603     /**
58604      * Sets if a column is editable.
58605      * @param {Number} col The column index
58606      * @param {Boolean} editable True if the column is editable
58607      */
58608     setEditable : function(col, editable){
58609         this.config[col].editable = editable;
58610     },
58611
58612
58613     /**
58614      * Returns true if the column is hidden.
58615      * @param {Number} colIndex The column index
58616      * @return {Boolean}
58617      */
58618     isHidden : function(colIndex){
58619         return this.config[colIndex].hidden;
58620     },
58621
58622
58623     /**
58624      * Returns true if the column width cannot be changed
58625      */
58626     isFixed : function(colIndex){
58627         return this.config[colIndex].fixed;
58628     },
58629
58630     /**
58631      * Returns true if the column can be resized
58632      * @return {Boolean}
58633      */
58634     isResizable : function(colIndex){
58635         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
58636     },
58637     /**
58638      * Sets if a column is hidden.
58639      * @param {Number} colIndex The column index
58640      * @param {Boolean} hidden True if the column is hidden
58641      */
58642     setHidden : function(colIndex, hidden){
58643         this.config[colIndex].hidden = hidden;
58644         this.totalWidth = null;
58645         this.fireEvent("hiddenchange", this, colIndex, hidden);
58646     },
58647
58648     /**
58649      * Sets the editor for a column.
58650      * @param {Number} col The column index
58651      * @param {Object} editor The editor object
58652      */
58653     setEditor : function(col, editor){
58654         this.config[col].editor = editor;
58655     }
58656 });
58657
58658 Roo.grid.ColumnModel.defaultRenderer = function(value)
58659 {
58660     if(typeof value == "object") {
58661         return value;
58662     }
58663         if(typeof value == "string" && value.length < 1){
58664             return "&#160;";
58665         }
58666     
58667         return String.format("{0}", value);
58668 };
58669
58670 // Alias for backwards compatibility
58671 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
58672 /*
58673  * Based on:
58674  * Ext JS Library 1.1.1
58675  * Copyright(c) 2006-2007, Ext JS, LLC.
58676  *
58677  * Originally Released Under LGPL - original licence link has changed is not relivant.
58678  *
58679  * Fork - LGPL
58680  * <script type="text/javascript">
58681  */
58682
58683 /**
58684  * @class Roo.grid.AbstractSelectionModel
58685  * @extends Roo.util.Observable
58686  * Abstract base class for grid SelectionModels.  It provides the interface that should be
58687  * implemented by descendant classes.  This class should not be directly instantiated.
58688  * @constructor
58689  */
58690 Roo.grid.AbstractSelectionModel = function(){
58691     this.locked = false;
58692     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
58693 };
58694
58695 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
58696     /** @ignore Called by the grid automatically. Do not call directly. */
58697     init : function(grid){
58698         this.grid = grid;
58699         this.initEvents();
58700     },
58701
58702     /**
58703      * Locks the selections.
58704      */
58705     lock : function(){
58706         this.locked = true;
58707     },
58708
58709     /**
58710      * Unlocks the selections.
58711      */
58712     unlock : function(){
58713         this.locked = false;
58714     },
58715
58716     /**
58717      * Returns true if the selections are locked.
58718      * @return {Boolean}
58719      */
58720     isLocked : function(){
58721         return this.locked;
58722     }
58723 });/*
58724  * Based on:
58725  * Ext JS Library 1.1.1
58726  * Copyright(c) 2006-2007, Ext JS, LLC.
58727  *
58728  * Originally Released Under LGPL - original licence link has changed is not relivant.
58729  *
58730  * Fork - LGPL
58731  * <script type="text/javascript">
58732  */
58733 /**
58734  * @extends Roo.grid.AbstractSelectionModel
58735  * @class Roo.grid.RowSelectionModel
58736  * The default SelectionModel used by {@link Roo.grid.Grid}.
58737  * It supports multiple selections and keyboard selection/navigation. 
58738  * @constructor
58739  * @param {Object} config
58740  */
58741 Roo.grid.RowSelectionModel = function(config){
58742     Roo.apply(this, config);
58743     this.selections = new Roo.util.MixedCollection(false, function(o){
58744         return o.id;
58745     });
58746
58747     this.last = false;
58748     this.lastActive = false;
58749
58750     this.addEvents({
58751         /**
58752              * @event selectionchange
58753              * Fires when the selection changes
58754              * @param {SelectionModel} this
58755              */
58756             "selectionchange" : true,
58757         /**
58758              * @event afterselectionchange
58759              * Fires after the selection changes (eg. by key press or clicking)
58760              * @param {SelectionModel} this
58761              */
58762             "afterselectionchange" : true,
58763         /**
58764              * @event beforerowselect
58765              * Fires when a row is selected being selected, return false to cancel.
58766              * @param {SelectionModel} this
58767              * @param {Number} rowIndex The selected index
58768              * @param {Boolean} keepExisting False if other selections will be cleared
58769              */
58770             "beforerowselect" : true,
58771         /**
58772              * @event rowselect
58773              * Fires when a row is selected.
58774              * @param {SelectionModel} this
58775              * @param {Number} rowIndex The selected index
58776              * @param {Roo.data.Record} r The record
58777              */
58778             "rowselect" : true,
58779         /**
58780              * @event rowdeselect
58781              * Fires when a row is deselected.
58782              * @param {SelectionModel} this
58783              * @param {Number} rowIndex The selected index
58784              */
58785         "rowdeselect" : true
58786     });
58787     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
58788     this.locked = false;
58789 };
58790
58791 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
58792     /**
58793      * @cfg {Boolean} singleSelect
58794      * True to allow selection of only one row at a time (defaults to false)
58795      */
58796     singleSelect : false,
58797
58798     // private
58799     initEvents : function(){
58800
58801         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
58802             this.grid.on("mousedown", this.handleMouseDown, this);
58803         }else{ // allow click to work like normal
58804             this.grid.on("rowclick", this.handleDragableRowClick, this);
58805         }
58806
58807         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
58808             "up" : function(e){
58809                 if(!e.shiftKey){
58810                     this.selectPrevious(e.shiftKey);
58811                 }else if(this.last !== false && this.lastActive !== false){
58812                     var last = this.last;
58813                     this.selectRange(this.last,  this.lastActive-1);
58814                     this.grid.getView().focusRow(this.lastActive);
58815                     if(last !== false){
58816                         this.last = last;
58817                     }
58818                 }else{
58819                     this.selectFirstRow();
58820                 }
58821                 this.fireEvent("afterselectionchange", this);
58822             },
58823             "down" : function(e){
58824                 if(!e.shiftKey){
58825                     this.selectNext(e.shiftKey);
58826                 }else if(this.last !== false && this.lastActive !== false){
58827                     var last = this.last;
58828                     this.selectRange(this.last,  this.lastActive+1);
58829                     this.grid.getView().focusRow(this.lastActive);
58830                     if(last !== false){
58831                         this.last = last;
58832                     }
58833                 }else{
58834                     this.selectFirstRow();
58835                 }
58836                 this.fireEvent("afterselectionchange", this);
58837             },
58838             scope: this
58839         });
58840
58841         var view = this.grid.view;
58842         view.on("refresh", this.onRefresh, this);
58843         view.on("rowupdated", this.onRowUpdated, this);
58844         view.on("rowremoved", this.onRemove, this);
58845     },
58846
58847     // private
58848     onRefresh : function(){
58849         var ds = this.grid.dataSource, i, v = this.grid.view;
58850         var s = this.selections;
58851         s.each(function(r){
58852             if((i = ds.indexOfId(r.id)) != -1){
58853                 v.onRowSelect(i);
58854                 s.add(ds.getAt(i)); // updating the selection relate data
58855             }else{
58856                 s.remove(r);
58857             }
58858         });
58859     },
58860
58861     // private
58862     onRemove : function(v, index, r){
58863         this.selections.remove(r);
58864     },
58865
58866     // private
58867     onRowUpdated : function(v, index, r){
58868         if(this.isSelected(r)){
58869             v.onRowSelect(index);
58870         }
58871     },
58872
58873     /**
58874      * Select records.
58875      * @param {Array} records The records to select
58876      * @param {Boolean} keepExisting (optional) True to keep existing selections
58877      */
58878     selectRecords : function(records, keepExisting){
58879         if(!keepExisting){
58880             this.clearSelections();
58881         }
58882         var ds = this.grid.dataSource;
58883         for(var i = 0, len = records.length; i < len; i++){
58884             this.selectRow(ds.indexOf(records[i]), true);
58885         }
58886     },
58887
58888     /**
58889      * Gets the number of selected rows.
58890      * @return {Number}
58891      */
58892     getCount : function(){
58893         return this.selections.length;
58894     },
58895
58896     /**
58897      * Selects the first row in the grid.
58898      */
58899     selectFirstRow : function(){
58900         this.selectRow(0);
58901     },
58902
58903     /**
58904      * Select the last row.
58905      * @param {Boolean} keepExisting (optional) True to keep existing selections
58906      */
58907     selectLastRow : function(keepExisting){
58908         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
58909     },
58910
58911     /**
58912      * Selects the row immediately following the last selected row.
58913      * @param {Boolean} keepExisting (optional) True to keep existing selections
58914      */
58915     selectNext : function(keepExisting){
58916         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
58917             this.selectRow(this.last+1, keepExisting);
58918             this.grid.getView().focusRow(this.last);
58919         }
58920     },
58921
58922     /**
58923      * Selects the row that precedes the last selected row.
58924      * @param {Boolean} keepExisting (optional) True to keep existing selections
58925      */
58926     selectPrevious : function(keepExisting){
58927         if(this.last){
58928             this.selectRow(this.last-1, keepExisting);
58929             this.grid.getView().focusRow(this.last);
58930         }
58931     },
58932
58933     /**
58934      * Returns the selected records
58935      * @return {Array} Array of selected records
58936      */
58937     getSelections : function(){
58938         return [].concat(this.selections.items);
58939     },
58940
58941     /**
58942      * Returns the first selected record.
58943      * @return {Record}
58944      */
58945     getSelected : function(){
58946         return this.selections.itemAt(0);
58947     },
58948
58949
58950     /**
58951      * Clears all selections.
58952      */
58953     clearSelections : function(fast){
58954         if(this.locked) {
58955             return;
58956         }
58957         if(fast !== true){
58958             var ds = this.grid.dataSource;
58959             var s = this.selections;
58960             s.each(function(r){
58961                 this.deselectRow(ds.indexOfId(r.id));
58962             }, this);
58963             s.clear();
58964         }else{
58965             this.selections.clear();
58966         }
58967         this.last = false;
58968     },
58969
58970
58971     /**
58972      * Selects all rows.
58973      */
58974     selectAll : function(){
58975         if(this.locked) {
58976             return;
58977         }
58978         this.selections.clear();
58979         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
58980             this.selectRow(i, true);
58981         }
58982     },
58983
58984     /**
58985      * Returns True if there is a selection.
58986      * @return {Boolean}
58987      */
58988     hasSelection : function(){
58989         return this.selections.length > 0;
58990     },
58991
58992     /**
58993      * Returns True if the specified row is selected.
58994      * @param {Number/Record} record The record or index of the record to check
58995      * @return {Boolean}
58996      */
58997     isSelected : function(index){
58998         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
58999         return (r && this.selections.key(r.id) ? true : false);
59000     },
59001
59002     /**
59003      * Returns True if the specified record id is selected.
59004      * @param {String} id The id of record to check
59005      * @return {Boolean}
59006      */
59007     isIdSelected : function(id){
59008         return (this.selections.key(id) ? true : false);
59009     },
59010
59011     // private
59012     handleMouseDown : function(e, t){
59013         var view = this.grid.getView(), rowIndex;
59014         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
59015             return;
59016         };
59017         if(e.shiftKey && this.last !== false){
59018             var last = this.last;
59019             this.selectRange(last, rowIndex, e.ctrlKey);
59020             this.last = last; // reset the last
59021             view.focusRow(rowIndex);
59022         }else{
59023             var isSelected = this.isSelected(rowIndex);
59024             if(e.button !== 0 && isSelected){
59025                 view.focusRow(rowIndex);
59026             }else if(e.ctrlKey && isSelected){
59027                 this.deselectRow(rowIndex);
59028             }else if(!isSelected){
59029                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
59030                 view.focusRow(rowIndex);
59031             }
59032         }
59033         this.fireEvent("afterselectionchange", this);
59034     },
59035     // private
59036     handleDragableRowClick :  function(grid, rowIndex, e) 
59037     {
59038         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
59039             this.selectRow(rowIndex, false);
59040             grid.view.focusRow(rowIndex);
59041              this.fireEvent("afterselectionchange", this);
59042         }
59043     },
59044     
59045     /**
59046      * Selects multiple rows.
59047      * @param {Array} rows Array of the indexes of the row to select
59048      * @param {Boolean} keepExisting (optional) True to keep existing selections
59049      */
59050     selectRows : function(rows, keepExisting){
59051         if(!keepExisting){
59052             this.clearSelections();
59053         }
59054         for(var i = 0, len = rows.length; i < len; i++){
59055             this.selectRow(rows[i], true);
59056         }
59057     },
59058
59059     /**
59060      * Selects a range of rows. All rows in between startRow and endRow are also selected.
59061      * @param {Number} startRow The index of the first row in the range
59062      * @param {Number} endRow The index of the last row in the range
59063      * @param {Boolean} keepExisting (optional) True to retain existing selections
59064      */
59065     selectRange : function(startRow, endRow, keepExisting){
59066         if(this.locked) {
59067             return;
59068         }
59069         if(!keepExisting){
59070             this.clearSelections();
59071         }
59072         if(startRow <= endRow){
59073             for(var i = startRow; i <= endRow; i++){
59074                 this.selectRow(i, true);
59075             }
59076         }else{
59077             for(var i = startRow; i >= endRow; i--){
59078                 this.selectRow(i, true);
59079             }
59080         }
59081     },
59082
59083     /**
59084      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
59085      * @param {Number} startRow The index of the first row in the range
59086      * @param {Number} endRow The index of the last row in the range
59087      */
59088     deselectRange : function(startRow, endRow, preventViewNotify){
59089         if(this.locked) {
59090             return;
59091         }
59092         for(var i = startRow; i <= endRow; i++){
59093             this.deselectRow(i, preventViewNotify);
59094         }
59095     },
59096
59097     /**
59098      * Selects a row.
59099      * @param {Number} row The index of the row to select
59100      * @param {Boolean} keepExisting (optional) True to keep existing selections
59101      */
59102     selectRow : function(index, keepExisting, preventViewNotify){
59103         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
59104             return;
59105         }
59106         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
59107             if(!keepExisting || this.singleSelect){
59108                 this.clearSelections();
59109             }
59110             var r = this.grid.dataSource.getAt(index);
59111             this.selections.add(r);
59112             this.last = this.lastActive = index;
59113             if(!preventViewNotify){
59114                 this.grid.getView().onRowSelect(index);
59115             }
59116             this.fireEvent("rowselect", this, index, r);
59117             this.fireEvent("selectionchange", this);
59118         }
59119     },
59120
59121     /**
59122      * Deselects a row.
59123      * @param {Number} row The index of the row to deselect
59124      */
59125     deselectRow : function(index, preventViewNotify){
59126         if(this.locked) {
59127             return;
59128         }
59129         if(this.last == index){
59130             this.last = false;
59131         }
59132         if(this.lastActive == index){
59133             this.lastActive = false;
59134         }
59135         var r = this.grid.dataSource.getAt(index);
59136         this.selections.remove(r);
59137         if(!preventViewNotify){
59138             this.grid.getView().onRowDeselect(index);
59139         }
59140         this.fireEvent("rowdeselect", this, index);
59141         this.fireEvent("selectionchange", this);
59142     },
59143
59144     // private
59145     restoreLast : function(){
59146         if(this._last){
59147             this.last = this._last;
59148         }
59149     },
59150
59151     // private
59152     acceptsNav : function(row, col, cm){
59153         return !cm.isHidden(col) && cm.isCellEditable(col, row);
59154     },
59155
59156     // private
59157     onEditorKey : function(field, e){
59158         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
59159         if(k == e.TAB){
59160             e.stopEvent();
59161             ed.completeEdit();
59162             if(e.shiftKey){
59163                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
59164             }else{
59165                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
59166             }
59167         }else if(k == e.ENTER && !e.ctrlKey){
59168             e.stopEvent();
59169             ed.completeEdit();
59170             if(e.shiftKey){
59171                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
59172             }else{
59173                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
59174             }
59175         }else if(k == e.ESC){
59176             ed.cancelEdit();
59177         }
59178         if(newCell){
59179             g.startEditing(newCell[0], newCell[1]);
59180         }
59181     }
59182 });/*
59183  * Based on:
59184  * Ext JS Library 1.1.1
59185  * Copyright(c) 2006-2007, Ext JS, LLC.
59186  *
59187  * Originally Released Under LGPL - original licence link has changed is not relivant.
59188  *
59189  * Fork - LGPL
59190  * <script type="text/javascript">
59191  */
59192 /**
59193  * @class Roo.grid.CellSelectionModel
59194  * @extends Roo.grid.AbstractSelectionModel
59195  * This class provides the basic implementation for cell selection in a grid.
59196  * @constructor
59197  * @param {Object} config The object containing the configuration of this model.
59198  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
59199  */
59200 Roo.grid.CellSelectionModel = function(config){
59201     Roo.apply(this, config);
59202
59203     this.selection = null;
59204
59205     this.addEvents({
59206         /**
59207              * @event beforerowselect
59208              * Fires before a cell is selected.
59209              * @param {SelectionModel} this
59210              * @param {Number} rowIndex The selected row index
59211              * @param {Number} colIndex The selected cell index
59212              */
59213             "beforecellselect" : true,
59214         /**
59215              * @event cellselect
59216              * Fires when a cell is selected.
59217              * @param {SelectionModel} this
59218              * @param {Number} rowIndex The selected row index
59219              * @param {Number} colIndex The selected cell index
59220              */
59221             "cellselect" : true,
59222         /**
59223              * @event selectionchange
59224              * Fires when the active selection changes.
59225              * @param {SelectionModel} this
59226              * @param {Object} selection null for no selection or an object (o) with two properties
59227                 <ul>
59228                 <li>o.record: the record object for the row the selection is in</li>
59229                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
59230                 </ul>
59231              */
59232             "selectionchange" : true,
59233         /**
59234              * @event tabend
59235              * Fires when the tab (or enter) was pressed on the last editable cell
59236              * You can use this to trigger add new row.
59237              * @param {SelectionModel} this
59238              */
59239             "tabend" : true,
59240          /**
59241              * @event beforeeditnext
59242              * Fires before the next editable sell is made active
59243              * You can use this to skip to another cell or fire the tabend
59244              *    if you set cell to false
59245              * @param {Object} eventdata object : { cell : [ row, col ] } 
59246              */
59247             "beforeeditnext" : true
59248     });
59249     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
59250 };
59251
59252 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
59253     
59254     enter_is_tab: false,
59255
59256     /** @ignore */
59257     initEvents : function(){
59258         this.grid.on("mousedown", this.handleMouseDown, this);
59259         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
59260         var view = this.grid.view;
59261         view.on("refresh", this.onViewChange, this);
59262         view.on("rowupdated", this.onRowUpdated, this);
59263         view.on("beforerowremoved", this.clearSelections, this);
59264         view.on("beforerowsinserted", this.clearSelections, this);
59265         if(this.grid.isEditor){
59266             this.grid.on("beforeedit", this.beforeEdit,  this);
59267         }
59268     },
59269
59270         //private
59271     beforeEdit : function(e){
59272         this.select(e.row, e.column, false, true, e.record);
59273     },
59274
59275         //private
59276     onRowUpdated : function(v, index, r){
59277         if(this.selection && this.selection.record == r){
59278             v.onCellSelect(index, this.selection.cell[1]);
59279         }
59280     },
59281
59282         //private
59283     onViewChange : function(){
59284         this.clearSelections(true);
59285     },
59286
59287         /**
59288          * Returns the currently selected cell,.
59289          * @return {Array} The selected cell (row, column) or null if none selected.
59290          */
59291     getSelectedCell : function(){
59292         return this.selection ? this.selection.cell : null;
59293     },
59294
59295     /**
59296      * Clears all selections.
59297      * @param {Boolean} true to prevent the gridview from being notified about the change.
59298      */
59299     clearSelections : function(preventNotify){
59300         var s = this.selection;
59301         if(s){
59302             if(preventNotify !== true){
59303                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
59304             }
59305             this.selection = null;
59306             this.fireEvent("selectionchange", this, null);
59307         }
59308     },
59309
59310     /**
59311      * Returns true if there is a selection.
59312      * @return {Boolean}
59313      */
59314     hasSelection : function(){
59315         return this.selection ? true : false;
59316     },
59317
59318     /** @ignore */
59319     handleMouseDown : function(e, t){
59320         var v = this.grid.getView();
59321         if(this.isLocked()){
59322             return;
59323         };
59324         var row = v.findRowIndex(t);
59325         var cell = v.findCellIndex(t);
59326         if(row !== false && cell !== false){
59327             this.select(row, cell);
59328         }
59329     },
59330
59331     /**
59332      * Selects a cell.
59333      * @param {Number} rowIndex
59334      * @param {Number} collIndex
59335      */
59336     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
59337         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
59338             this.clearSelections();
59339             r = r || this.grid.dataSource.getAt(rowIndex);
59340             this.selection = {
59341                 record : r,
59342                 cell : [rowIndex, colIndex]
59343             };
59344             if(!preventViewNotify){
59345                 var v = this.grid.getView();
59346                 v.onCellSelect(rowIndex, colIndex);
59347                 if(preventFocus !== true){
59348                     v.focusCell(rowIndex, colIndex);
59349                 }
59350             }
59351             this.fireEvent("cellselect", this, rowIndex, colIndex);
59352             this.fireEvent("selectionchange", this, this.selection);
59353         }
59354     },
59355
59356         //private
59357     isSelectable : function(rowIndex, colIndex, cm){
59358         return !cm.isHidden(colIndex);
59359     },
59360
59361     /** @ignore */
59362     handleKeyDown : function(e){
59363         //Roo.log('Cell Sel Model handleKeyDown');
59364         if(!e.isNavKeyPress()){
59365             return;
59366         }
59367         var g = this.grid, s = this.selection;
59368         if(!s){
59369             e.stopEvent();
59370             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
59371             if(cell){
59372                 this.select(cell[0], cell[1]);
59373             }
59374             return;
59375         }
59376         var sm = this;
59377         var walk = function(row, col, step){
59378             return g.walkCells(row, col, step, sm.isSelectable,  sm);
59379         };
59380         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
59381         var newCell;
59382
59383       
59384
59385         switch(k){
59386             case e.TAB:
59387                 // handled by onEditorKey
59388                 if (g.isEditor && g.editing) {
59389                     return;
59390                 }
59391                 if(e.shiftKey) {
59392                     newCell = walk(r, c-1, -1);
59393                 } else {
59394                     newCell = walk(r, c+1, 1);
59395                 }
59396                 break;
59397             
59398             case e.DOWN:
59399                newCell = walk(r+1, c, 1);
59400                 break;
59401             
59402             case e.UP:
59403                 newCell = walk(r-1, c, -1);
59404                 break;
59405             
59406             case e.RIGHT:
59407                 newCell = walk(r, c+1, 1);
59408                 break;
59409             
59410             case e.LEFT:
59411                 newCell = walk(r, c-1, -1);
59412                 break;
59413             
59414             case e.ENTER:
59415                 
59416                 if(g.isEditor && !g.editing){
59417                    g.startEditing(r, c);
59418                    e.stopEvent();
59419                    return;
59420                 }
59421                 
59422                 
59423              break;
59424         };
59425         if(newCell){
59426             this.select(newCell[0], newCell[1]);
59427             e.stopEvent();
59428             
59429         }
59430     },
59431
59432     acceptsNav : function(row, col, cm){
59433         return !cm.isHidden(col) && cm.isCellEditable(col, row);
59434     },
59435     /**
59436      * Selects a cell.
59437      * @param {Number} field (not used) - as it's normally used as a listener
59438      * @param {Number} e - event - fake it by using
59439      *
59440      * var e = Roo.EventObjectImpl.prototype;
59441      * e.keyCode = e.TAB
59442      *
59443      * 
59444      */
59445     onEditorKey : function(field, e){
59446         
59447         var k = e.getKey(),
59448             newCell,
59449             g = this.grid,
59450             ed = g.activeEditor,
59451             forward = false;
59452         ///Roo.log('onEditorKey' + k);
59453         
59454         
59455         if (this.enter_is_tab && k == e.ENTER) {
59456             k = e.TAB;
59457         }
59458         
59459         if(k == e.TAB){
59460             if(e.shiftKey){
59461                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
59462             }else{
59463                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
59464                 forward = true;
59465             }
59466             
59467             e.stopEvent();
59468             
59469         } else if(k == e.ENTER &&  !e.ctrlKey){
59470             ed.completeEdit();
59471             e.stopEvent();
59472             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
59473         
59474                 } else if(k == e.ESC){
59475             ed.cancelEdit();
59476         }
59477                 
59478         if (newCell) {
59479             var ecall = { cell : newCell, forward : forward };
59480             this.fireEvent('beforeeditnext', ecall );
59481             newCell = ecall.cell;
59482                         forward = ecall.forward;
59483         }
59484                 
59485         if(newCell){
59486             //Roo.log('next cell after edit');
59487             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
59488         } else if (forward) {
59489             // tabbed past last
59490             this.fireEvent.defer(100, this, ['tabend',this]);
59491         }
59492     }
59493 });/*
59494  * Based on:
59495  * Ext JS Library 1.1.1
59496  * Copyright(c) 2006-2007, Ext JS, LLC.
59497  *
59498  * Originally Released Under LGPL - original licence link has changed is not relivant.
59499  *
59500  * Fork - LGPL
59501  * <script type="text/javascript">
59502  */
59503  
59504 /**
59505  * @class Roo.grid.EditorGrid
59506  * @extends Roo.grid.Grid
59507  * Class for creating and editable grid.
59508  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
59509  * The container MUST have some type of size defined for the grid to fill. The container will be 
59510  * automatically set to position relative if it isn't already.
59511  * @param {Object} dataSource The data model to bind to
59512  * @param {Object} colModel The column model with info about this grid's columns
59513  */
59514 Roo.grid.EditorGrid = function(container, config){
59515     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
59516     this.getGridEl().addClass("xedit-grid");
59517
59518     if(!this.selModel){
59519         this.selModel = new Roo.grid.CellSelectionModel();
59520     }
59521
59522     this.activeEditor = null;
59523
59524         this.addEvents({
59525             /**
59526              * @event beforeedit
59527              * Fires before cell editing is triggered. The edit event object has the following properties <br />
59528              * <ul style="padding:5px;padding-left:16px;">
59529              * <li>grid - This grid</li>
59530              * <li>record - The record being edited</li>
59531              * <li>field - The field name being edited</li>
59532              * <li>value - The value for the field being edited.</li>
59533              * <li>row - The grid row index</li>
59534              * <li>column - The grid column index</li>
59535              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
59536              * </ul>
59537              * @param {Object} e An edit event (see above for description)
59538              */
59539             "beforeedit" : true,
59540             /**
59541              * @event afteredit
59542              * Fires after a cell is edited. <br />
59543              * <ul style="padding:5px;padding-left:16px;">
59544              * <li>grid - This grid</li>
59545              * <li>record - The record being edited</li>
59546              * <li>field - The field name being edited</li>
59547              * <li>value - The value being set</li>
59548              * <li>originalValue - The original value for the field, before the edit.</li>
59549              * <li>row - The grid row index</li>
59550              * <li>column - The grid column index</li>
59551              * </ul>
59552              * @param {Object} e An edit event (see above for description)
59553              */
59554             "afteredit" : true,
59555             /**
59556              * @event validateedit
59557              * Fires after a cell is edited, but before the value is set in the record. 
59558          * You can use this to modify the value being set in the field, Return false
59559              * to cancel the change. The edit event object has the following properties <br />
59560              * <ul style="padding:5px;padding-left:16px;">
59561          * <li>editor - This editor</li>
59562              * <li>grid - This grid</li>
59563              * <li>record - The record being edited</li>
59564              * <li>field - The field name being edited</li>
59565              * <li>value - The value being set</li>
59566              * <li>originalValue - The original value for the field, before the edit.</li>
59567              * <li>row - The grid row index</li>
59568              * <li>column - The grid column index</li>
59569              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
59570              * </ul>
59571              * @param {Object} e An edit event (see above for description)
59572              */
59573             "validateedit" : true
59574         });
59575     this.on("bodyscroll", this.stopEditing,  this);
59576     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
59577 };
59578
59579 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
59580     /**
59581      * @cfg {Number} clicksToEdit
59582      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
59583      */
59584     clicksToEdit: 2,
59585
59586     // private
59587     isEditor : true,
59588     // private
59589     trackMouseOver: false, // causes very odd FF errors
59590
59591     onCellDblClick : function(g, row, col){
59592         this.startEditing(row, col);
59593     },
59594
59595     onEditComplete : function(ed, value, startValue){
59596         this.editing = false;
59597         this.activeEditor = null;
59598         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
59599         var r = ed.record;
59600         var field = this.colModel.getDataIndex(ed.col);
59601         var e = {
59602             grid: this,
59603             record: r,
59604             field: field,
59605             originalValue: startValue,
59606             value: value,
59607             row: ed.row,
59608             column: ed.col,
59609             cancel:false,
59610             editor: ed
59611         };
59612         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
59613         cell.show();
59614           
59615         if(String(value) !== String(startValue)){
59616             
59617             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
59618                 r.set(field, e.value);
59619                 // if we are dealing with a combo box..
59620                 // then we also set the 'name' colum to be the displayField
59621                 if (ed.field.displayField && ed.field.name) {
59622                     r.set(ed.field.name, ed.field.el.dom.value);
59623                 }
59624                 
59625                 delete e.cancel; //?? why!!!
59626                 this.fireEvent("afteredit", e);
59627             }
59628         } else {
59629             this.fireEvent("afteredit", e); // always fire it!
59630         }
59631         this.view.focusCell(ed.row, ed.col);
59632     },
59633
59634     /**
59635      * Starts editing the specified for the specified row/column
59636      * @param {Number} rowIndex
59637      * @param {Number} colIndex
59638      */
59639     startEditing : function(row, col){
59640         this.stopEditing();
59641         if(this.colModel.isCellEditable(col, row)){
59642             this.view.ensureVisible(row, col, true);
59643           
59644             var r = this.dataSource.getAt(row);
59645             var field = this.colModel.getDataIndex(col);
59646             var cell = Roo.get(this.view.getCell(row,col));
59647             var e = {
59648                 grid: this,
59649                 record: r,
59650                 field: field,
59651                 value: r.data[field],
59652                 row: row,
59653                 column: col,
59654                 cancel:false 
59655             };
59656             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
59657                 this.editing = true;
59658                 var ed = this.colModel.getCellEditor(col, row);
59659                 
59660                 if (!ed) {
59661                     return;
59662                 }
59663                 if(!ed.rendered){
59664                     ed.render(ed.parentEl || document.body);
59665                 }
59666                 ed.field.reset();
59667                
59668                 cell.hide();
59669                 
59670                 (function(){ // complex but required for focus issues in safari, ie and opera
59671                     ed.row = row;
59672                     ed.col = col;
59673                     ed.record = r;
59674                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
59675                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
59676                     this.activeEditor = ed;
59677                     var v = r.data[field];
59678                     ed.startEdit(this.view.getCell(row, col), v);
59679                     // combo's with 'displayField and name set
59680                     if (ed.field.displayField && ed.field.name) {
59681                         ed.field.el.dom.value = r.data[ed.field.name];
59682                     }
59683                     
59684                     
59685                 }).defer(50, this);
59686             }
59687         }
59688     },
59689         
59690     /**
59691      * Stops any active editing
59692      */
59693     stopEditing : function(){
59694         if(this.activeEditor){
59695             this.activeEditor.completeEdit();
59696         }
59697         this.activeEditor = null;
59698     },
59699         
59700          /**
59701      * Called to get grid's drag proxy text, by default returns this.ddText.
59702      * @return {String}
59703      */
59704     getDragDropText : function(){
59705         var count = this.selModel.getSelectedCell() ? 1 : 0;
59706         return String.format(this.ddText, count, count == 1 ? '' : 's');
59707     }
59708         
59709 });/*
59710  * Based on:
59711  * Ext JS Library 1.1.1
59712  * Copyright(c) 2006-2007, Ext JS, LLC.
59713  *
59714  * Originally Released Under LGPL - original licence link has changed is not relivant.
59715  *
59716  * Fork - LGPL
59717  * <script type="text/javascript">
59718  */
59719
59720 // private - not really -- you end up using it !
59721 // This is a support class used internally by the Grid components
59722
59723 /**
59724  * @class Roo.grid.GridEditor
59725  * @extends Roo.Editor
59726  * Class for creating and editable grid elements.
59727  * @param {Object} config any settings (must include field)
59728  */
59729 Roo.grid.GridEditor = function(field, config){
59730     if (!config && field.field) {
59731         config = field;
59732         field = Roo.factory(config.field, Roo.form);
59733     }
59734     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
59735     field.monitorTab = false;
59736 };
59737
59738 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
59739     
59740     /**
59741      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
59742      */
59743     
59744     alignment: "tl-tl",
59745     autoSize: "width",
59746     hideEl : false,
59747     cls: "x-small-editor x-grid-editor",
59748     shim:false,
59749     shadow:"frame"
59750 });/*
59751  * Based on:
59752  * Ext JS Library 1.1.1
59753  * Copyright(c) 2006-2007, Ext JS, LLC.
59754  *
59755  * Originally Released Under LGPL - original licence link has changed is not relivant.
59756  *
59757  * Fork - LGPL
59758  * <script type="text/javascript">
59759  */
59760   
59761
59762   
59763 Roo.grid.PropertyRecord = Roo.data.Record.create([
59764     {name:'name',type:'string'},  'value'
59765 ]);
59766
59767
59768 Roo.grid.PropertyStore = function(grid, source){
59769     this.grid = grid;
59770     this.store = new Roo.data.Store({
59771         recordType : Roo.grid.PropertyRecord
59772     });
59773     this.store.on('update', this.onUpdate,  this);
59774     if(source){
59775         this.setSource(source);
59776     }
59777     Roo.grid.PropertyStore.superclass.constructor.call(this);
59778 };
59779
59780
59781
59782 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
59783     setSource : function(o){
59784         this.source = o;
59785         this.store.removeAll();
59786         var data = [];
59787         for(var k in o){
59788             if(this.isEditableValue(o[k])){
59789                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
59790             }
59791         }
59792         this.store.loadRecords({records: data}, {}, true);
59793     },
59794
59795     onUpdate : function(ds, record, type){
59796         if(type == Roo.data.Record.EDIT){
59797             var v = record.data['value'];
59798             var oldValue = record.modified['value'];
59799             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
59800                 this.source[record.id] = v;
59801                 record.commit();
59802                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
59803             }else{
59804                 record.reject();
59805             }
59806         }
59807     },
59808
59809     getProperty : function(row){
59810        return this.store.getAt(row);
59811     },
59812
59813     isEditableValue: function(val){
59814         if(val && val instanceof Date){
59815             return true;
59816         }else if(typeof val == 'object' || typeof val == 'function'){
59817             return false;
59818         }
59819         return true;
59820     },
59821
59822     setValue : function(prop, value){
59823         this.source[prop] = value;
59824         this.store.getById(prop).set('value', value);
59825     },
59826
59827     getSource : function(){
59828         return this.source;
59829     }
59830 });
59831
59832 Roo.grid.PropertyColumnModel = function(grid, store){
59833     this.grid = grid;
59834     var g = Roo.grid;
59835     g.PropertyColumnModel.superclass.constructor.call(this, [
59836         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
59837         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
59838     ]);
59839     this.store = store;
59840     this.bselect = Roo.DomHelper.append(document.body, {
59841         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
59842             {tag: 'option', value: 'true', html: 'true'},
59843             {tag: 'option', value: 'false', html: 'false'}
59844         ]
59845     });
59846     Roo.id(this.bselect);
59847     var f = Roo.form;
59848     this.editors = {
59849         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
59850         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
59851         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
59852         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
59853         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
59854     };
59855     this.renderCellDelegate = this.renderCell.createDelegate(this);
59856     this.renderPropDelegate = this.renderProp.createDelegate(this);
59857 };
59858
59859 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
59860     
59861     
59862     nameText : 'Name',
59863     valueText : 'Value',
59864     
59865     dateFormat : 'm/j/Y',
59866     
59867     
59868     renderDate : function(dateVal){
59869         return dateVal.dateFormat(this.dateFormat);
59870     },
59871
59872     renderBool : function(bVal){
59873         return bVal ? 'true' : 'false';
59874     },
59875
59876     isCellEditable : function(colIndex, rowIndex){
59877         return colIndex == 1;
59878     },
59879
59880     getRenderer : function(col){
59881         return col == 1 ?
59882             this.renderCellDelegate : this.renderPropDelegate;
59883     },
59884
59885     renderProp : function(v){
59886         return this.getPropertyName(v);
59887     },
59888
59889     renderCell : function(val){
59890         var rv = val;
59891         if(val instanceof Date){
59892             rv = this.renderDate(val);
59893         }else if(typeof val == 'boolean'){
59894             rv = this.renderBool(val);
59895         }
59896         return Roo.util.Format.htmlEncode(rv);
59897     },
59898
59899     getPropertyName : function(name){
59900         var pn = this.grid.propertyNames;
59901         return pn && pn[name] ? pn[name] : name;
59902     },
59903
59904     getCellEditor : function(colIndex, rowIndex){
59905         var p = this.store.getProperty(rowIndex);
59906         var n = p.data['name'], val = p.data['value'];
59907         
59908         if(typeof(this.grid.customEditors[n]) == 'string'){
59909             return this.editors[this.grid.customEditors[n]];
59910         }
59911         if(typeof(this.grid.customEditors[n]) != 'undefined'){
59912             return this.grid.customEditors[n];
59913         }
59914         if(val instanceof Date){
59915             return this.editors['date'];
59916         }else if(typeof val == 'number'){
59917             return this.editors['number'];
59918         }else if(typeof val == 'boolean'){
59919             return this.editors['boolean'];
59920         }else{
59921             return this.editors['string'];
59922         }
59923     }
59924 });
59925
59926 /**
59927  * @class Roo.grid.PropertyGrid
59928  * @extends Roo.grid.EditorGrid
59929  * This class represents the  interface of a component based property grid control.
59930  * <br><br>Usage:<pre><code>
59931  var grid = new Roo.grid.PropertyGrid("my-container-id", {
59932       
59933  });
59934  // set any options
59935  grid.render();
59936  * </code></pre>
59937   
59938  * @constructor
59939  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
59940  * The container MUST have some type of size defined for the grid to fill. The container will be
59941  * automatically set to position relative if it isn't already.
59942  * @param {Object} config A config object that sets properties on this grid.
59943  */
59944 Roo.grid.PropertyGrid = function(container, config){
59945     config = config || {};
59946     var store = new Roo.grid.PropertyStore(this);
59947     this.store = store;
59948     var cm = new Roo.grid.PropertyColumnModel(this, store);
59949     store.store.sort('name', 'ASC');
59950     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
59951         ds: store.store,
59952         cm: cm,
59953         enableColLock:false,
59954         enableColumnMove:false,
59955         stripeRows:false,
59956         trackMouseOver: false,
59957         clicksToEdit:1
59958     }, config));
59959     this.getGridEl().addClass('x-props-grid');
59960     this.lastEditRow = null;
59961     this.on('columnresize', this.onColumnResize, this);
59962     this.addEvents({
59963          /**
59964              * @event beforepropertychange
59965              * Fires before a property changes (return false to stop?)
59966              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
59967              * @param {String} id Record Id
59968              * @param {String} newval New Value
59969          * @param {String} oldval Old Value
59970              */
59971         "beforepropertychange": true,
59972         /**
59973              * @event propertychange
59974              * Fires after a property changes
59975              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
59976              * @param {String} id Record Id
59977              * @param {String} newval New Value
59978          * @param {String} oldval Old Value
59979              */
59980         "propertychange": true
59981     });
59982     this.customEditors = this.customEditors || {};
59983 };
59984 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
59985     
59986      /**
59987      * @cfg {Object} customEditors map of colnames=> custom editors.
59988      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
59989      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
59990      * false disables editing of the field.
59991          */
59992     
59993       /**
59994      * @cfg {Object} propertyNames map of property Names to their displayed value
59995          */
59996     
59997     render : function(){
59998         Roo.grid.PropertyGrid.superclass.render.call(this);
59999         this.autoSize.defer(100, this);
60000     },
60001
60002     autoSize : function(){
60003         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
60004         if(this.view){
60005             this.view.fitColumns();
60006         }
60007     },
60008
60009     onColumnResize : function(){
60010         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
60011         this.autoSize();
60012     },
60013     /**
60014      * Sets the data for the Grid
60015      * accepts a Key => Value object of all the elements avaiable.
60016      * @param {Object} data  to appear in grid.
60017      */
60018     setSource : function(source){
60019         this.store.setSource(source);
60020         //this.autoSize();
60021     },
60022     /**
60023      * Gets all the data from the grid.
60024      * @return {Object} data  data stored in grid
60025      */
60026     getSource : function(){
60027         return this.store.getSource();
60028     }
60029 });/*
60030   
60031  * Licence LGPL
60032  
60033  */
60034  
60035 /**
60036  * @class Roo.grid.Calendar
60037  * @extends Roo.util.Grid
60038  * This class extends the Grid to provide a calendar widget
60039  * <br><br>Usage:<pre><code>
60040  var grid = new Roo.grid.Calendar("my-container-id", {
60041      ds: myDataStore,
60042      cm: myColModel,
60043      selModel: mySelectionModel,
60044      autoSizeColumns: true,
60045      monitorWindowResize: false,
60046      trackMouseOver: true
60047      eventstore : real data store..
60048  });
60049  // set any options
60050  grid.render();
60051   
60052   * @constructor
60053  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
60054  * The container MUST have some type of size defined for the grid to fill. The container will be
60055  * automatically set to position relative if it isn't already.
60056  * @param {Object} config A config object that sets properties on this grid.
60057  */
60058 Roo.grid.Calendar = function(container, config){
60059         // initialize the container
60060         this.container = Roo.get(container);
60061         this.container.update("");
60062         this.container.setStyle("overflow", "hidden");
60063     this.container.addClass('x-grid-container');
60064
60065     this.id = this.container.id;
60066
60067     Roo.apply(this, config);
60068     // check and correct shorthanded configs
60069     
60070     var rows = [];
60071     var d =1;
60072     for (var r = 0;r < 6;r++) {
60073         
60074         rows[r]=[];
60075         for (var c =0;c < 7;c++) {
60076             rows[r][c]= '';
60077         }
60078     }
60079     if (this.eventStore) {
60080         this.eventStore= Roo.factory(this.eventStore, Roo.data);
60081         this.eventStore.on('load',this.onLoad, this);
60082         this.eventStore.on('beforeload',this.clearEvents, this);
60083          
60084     }
60085     
60086     this.dataSource = new Roo.data.Store({
60087             proxy: new Roo.data.MemoryProxy(rows),
60088             reader: new Roo.data.ArrayReader({}, [
60089                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
60090     });
60091
60092     this.dataSource.load();
60093     this.ds = this.dataSource;
60094     this.ds.xmodule = this.xmodule || false;
60095     
60096     
60097     var cellRender = function(v,x,r)
60098     {
60099         return String.format(
60100             '<div class="fc-day  fc-widget-content"><div>' +
60101                 '<div class="fc-event-container"></div>' +
60102                 '<div class="fc-day-number">{0}</div>'+
60103                 
60104                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
60105             '</div></div>', v);
60106     
60107     }
60108     
60109     
60110     this.colModel = new Roo.grid.ColumnModel( [
60111         {
60112             xtype: 'ColumnModel',
60113             xns: Roo.grid,
60114             dataIndex : 'weekday0',
60115             header : 'Sunday',
60116             renderer : cellRender
60117         },
60118         {
60119             xtype: 'ColumnModel',
60120             xns: Roo.grid,
60121             dataIndex : 'weekday1',
60122             header : 'Monday',
60123             renderer : cellRender
60124         },
60125         {
60126             xtype: 'ColumnModel',
60127             xns: Roo.grid,
60128             dataIndex : 'weekday2',
60129             header : 'Tuesday',
60130             renderer : cellRender
60131         },
60132         {
60133             xtype: 'ColumnModel',
60134             xns: Roo.grid,
60135             dataIndex : 'weekday3',
60136             header : 'Wednesday',
60137             renderer : cellRender
60138         },
60139         {
60140             xtype: 'ColumnModel',
60141             xns: Roo.grid,
60142             dataIndex : 'weekday4',
60143             header : 'Thursday',
60144             renderer : cellRender
60145         },
60146         {
60147             xtype: 'ColumnModel',
60148             xns: Roo.grid,
60149             dataIndex : 'weekday5',
60150             header : 'Friday',
60151             renderer : cellRender
60152         },
60153         {
60154             xtype: 'ColumnModel',
60155             xns: Roo.grid,
60156             dataIndex : 'weekday6',
60157             header : 'Saturday',
60158             renderer : cellRender
60159         }
60160     ]);
60161     this.cm = this.colModel;
60162     this.cm.xmodule = this.xmodule || false;
60163  
60164         
60165           
60166     //this.selModel = new Roo.grid.CellSelectionModel();
60167     //this.sm = this.selModel;
60168     //this.selModel.init(this);
60169     
60170     
60171     if(this.width){
60172         this.container.setWidth(this.width);
60173     }
60174
60175     if(this.height){
60176         this.container.setHeight(this.height);
60177     }
60178     /** @private */
60179         this.addEvents({
60180         // raw events
60181         /**
60182          * @event click
60183          * The raw click event for the entire grid.
60184          * @param {Roo.EventObject} e
60185          */
60186         "click" : true,
60187         /**
60188          * @event dblclick
60189          * The raw dblclick event for the entire grid.
60190          * @param {Roo.EventObject} e
60191          */
60192         "dblclick" : true,
60193         /**
60194          * @event contextmenu
60195          * The raw contextmenu event for the entire grid.
60196          * @param {Roo.EventObject} e
60197          */
60198         "contextmenu" : true,
60199         /**
60200          * @event mousedown
60201          * The raw mousedown event for the entire grid.
60202          * @param {Roo.EventObject} e
60203          */
60204         "mousedown" : true,
60205         /**
60206          * @event mouseup
60207          * The raw mouseup event for the entire grid.
60208          * @param {Roo.EventObject} e
60209          */
60210         "mouseup" : true,
60211         /**
60212          * @event mouseover
60213          * The raw mouseover event for the entire grid.
60214          * @param {Roo.EventObject} e
60215          */
60216         "mouseover" : true,
60217         /**
60218          * @event mouseout
60219          * The raw mouseout event for the entire grid.
60220          * @param {Roo.EventObject} e
60221          */
60222         "mouseout" : true,
60223         /**
60224          * @event keypress
60225          * The raw keypress event for the entire grid.
60226          * @param {Roo.EventObject} e
60227          */
60228         "keypress" : true,
60229         /**
60230          * @event keydown
60231          * The raw keydown event for the entire grid.
60232          * @param {Roo.EventObject} e
60233          */
60234         "keydown" : true,
60235
60236         // custom events
60237
60238         /**
60239          * @event cellclick
60240          * Fires when a cell is clicked
60241          * @param {Grid} this
60242          * @param {Number} rowIndex
60243          * @param {Number} columnIndex
60244          * @param {Roo.EventObject} e
60245          */
60246         "cellclick" : true,
60247         /**
60248          * @event celldblclick
60249          * Fires when a cell is double clicked
60250          * @param {Grid} this
60251          * @param {Number} rowIndex
60252          * @param {Number} columnIndex
60253          * @param {Roo.EventObject} e
60254          */
60255         "celldblclick" : true,
60256         /**
60257          * @event rowclick
60258          * Fires when a row is clicked
60259          * @param {Grid} this
60260          * @param {Number} rowIndex
60261          * @param {Roo.EventObject} e
60262          */
60263         "rowclick" : true,
60264         /**
60265          * @event rowdblclick
60266          * Fires when a row is double clicked
60267          * @param {Grid} this
60268          * @param {Number} rowIndex
60269          * @param {Roo.EventObject} e
60270          */
60271         "rowdblclick" : true,
60272         /**
60273          * @event headerclick
60274          * Fires when a header is clicked
60275          * @param {Grid} this
60276          * @param {Number} columnIndex
60277          * @param {Roo.EventObject} e
60278          */
60279         "headerclick" : true,
60280         /**
60281          * @event headerdblclick
60282          * Fires when a header cell is double clicked
60283          * @param {Grid} this
60284          * @param {Number} columnIndex
60285          * @param {Roo.EventObject} e
60286          */
60287         "headerdblclick" : true,
60288         /**
60289          * @event rowcontextmenu
60290          * Fires when a row is right clicked
60291          * @param {Grid} this
60292          * @param {Number} rowIndex
60293          * @param {Roo.EventObject} e
60294          */
60295         "rowcontextmenu" : true,
60296         /**
60297          * @event cellcontextmenu
60298          * Fires when a cell is right clicked
60299          * @param {Grid} this
60300          * @param {Number} rowIndex
60301          * @param {Number} cellIndex
60302          * @param {Roo.EventObject} e
60303          */
60304          "cellcontextmenu" : true,
60305         /**
60306          * @event headercontextmenu
60307          * Fires when a header is right clicked
60308          * @param {Grid} this
60309          * @param {Number} columnIndex
60310          * @param {Roo.EventObject} e
60311          */
60312         "headercontextmenu" : true,
60313         /**
60314          * @event bodyscroll
60315          * Fires when the body element is scrolled
60316          * @param {Number} scrollLeft
60317          * @param {Number} scrollTop
60318          */
60319         "bodyscroll" : true,
60320         /**
60321          * @event columnresize
60322          * Fires when the user resizes a column
60323          * @param {Number} columnIndex
60324          * @param {Number} newSize
60325          */
60326         "columnresize" : true,
60327         /**
60328          * @event columnmove
60329          * Fires when the user moves a column
60330          * @param {Number} oldIndex
60331          * @param {Number} newIndex
60332          */
60333         "columnmove" : true,
60334         /**
60335          * @event startdrag
60336          * Fires when row(s) start being dragged
60337          * @param {Grid} this
60338          * @param {Roo.GridDD} dd The drag drop object
60339          * @param {event} e The raw browser event
60340          */
60341         "startdrag" : true,
60342         /**
60343          * @event enddrag
60344          * Fires when a drag operation is complete
60345          * @param {Grid} this
60346          * @param {Roo.GridDD} dd The drag drop object
60347          * @param {event} e The raw browser event
60348          */
60349         "enddrag" : true,
60350         /**
60351          * @event dragdrop
60352          * Fires when dragged row(s) are dropped on a valid DD target
60353          * @param {Grid} this
60354          * @param {Roo.GridDD} dd The drag drop object
60355          * @param {String} targetId The target drag drop object
60356          * @param {event} e The raw browser event
60357          */
60358         "dragdrop" : true,
60359         /**
60360          * @event dragover
60361          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
60362          * @param {Grid} this
60363          * @param {Roo.GridDD} dd The drag drop object
60364          * @param {String} targetId The target drag drop object
60365          * @param {event} e The raw browser event
60366          */
60367         "dragover" : true,
60368         /**
60369          * @event dragenter
60370          *  Fires when the dragged row(s) first cross another DD target while being dragged
60371          * @param {Grid} this
60372          * @param {Roo.GridDD} dd The drag drop object
60373          * @param {String} targetId The target drag drop object
60374          * @param {event} e The raw browser event
60375          */
60376         "dragenter" : true,
60377         /**
60378          * @event dragout
60379          * Fires when the dragged row(s) leave another DD target while being dragged
60380          * @param {Grid} this
60381          * @param {Roo.GridDD} dd The drag drop object
60382          * @param {String} targetId The target drag drop object
60383          * @param {event} e The raw browser event
60384          */
60385         "dragout" : true,
60386         /**
60387          * @event rowclass
60388          * Fires when a row is rendered, so you can change add a style to it.
60389          * @param {GridView} gridview   The grid view
60390          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
60391          */
60392         'rowclass' : true,
60393
60394         /**
60395          * @event render
60396          * Fires when the grid is rendered
60397          * @param {Grid} grid
60398          */
60399         'render' : true,
60400             /**
60401              * @event select
60402              * Fires when a date is selected
60403              * @param {DatePicker} this
60404              * @param {Date} date The selected date
60405              */
60406         'select': true,
60407         /**
60408              * @event monthchange
60409              * Fires when the displayed month changes 
60410              * @param {DatePicker} this
60411              * @param {Date} date The selected month
60412              */
60413         'monthchange': true,
60414         /**
60415              * @event evententer
60416              * Fires when mouse over an event
60417              * @param {Calendar} this
60418              * @param {event} Event
60419              */
60420         'evententer': true,
60421         /**
60422              * @event eventleave
60423              * Fires when the mouse leaves an
60424              * @param {Calendar} this
60425              * @param {event}
60426              */
60427         'eventleave': true,
60428         /**
60429              * @event eventclick
60430              * Fires when the mouse click an
60431              * @param {Calendar} this
60432              * @param {event}
60433              */
60434         'eventclick': true,
60435         /**
60436              * @event eventrender
60437              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
60438              * @param {Calendar} this
60439              * @param {data} data to be modified
60440              */
60441         'eventrender': true
60442         
60443     });
60444
60445     Roo.grid.Grid.superclass.constructor.call(this);
60446     this.on('render', function() {
60447         this.view.el.addClass('x-grid-cal'); 
60448         
60449         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
60450
60451     },this);
60452     
60453     if (!Roo.grid.Calendar.style) {
60454         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
60455             
60456             
60457             '.x-grid-cal .x-grid-col' :  {
60458                 height: 'auto !important',
60459                 'vertical-align': 'top'
60460             },
60461             '.x-grid-cal  .fc-event-hori' : {
60462                 height: '14px'
60463             }
60464              
60465             
60466         }, Roo.id());
60467     }
60468
60469     
60470     
60471 };
60472 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
60473     /**
60474      * @cfg {Store} eventStore The store that loads events.
60475      */
60476     eventStore : 25,
60477
60478      
60479     activeDate : false,
60480     startDay : 0,
60481     autoWidth : true,
60482     monitorWindowResize : false,
60483
60484     
60485     resizeColumns : function() {
60486         var col = (this.view.el.getWidth() / 7) - 3;
60487         // loop through cols, and setWidth
60488         for(var i =0 ; i < 7 ; i++){
60489             this.cm.setColumnWidth(i, col);
60490         }
60491     },
60492      setDate :function(date) {
60493         
60494         Roo.log('setDate?');
60495         
60496         this.resizeColumns();
60497         var vd = this.activeDate;
60498         this.activeDate = date;
60499 //        if(vd && this.el){
60500 //            var t = date.getTime();
60501 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
60502 //                Roo.log('using add remove');
60503 //                
60504 //                this.fireEvent('monthchange', this, date);
60505 //                
60506 //                this.cells.removeClass("fc-state-highlight");
60507 //                this.cells.each(function(c){
60508 //                   if(c.dateValue == t){
60509 //                       c.addClass("fc-state-highlight");
60510 //                       setTimeout(function(){
60511 //                            try{c.dom.firstChild.focus();}catch(e){}
60512 //                       }, 50);
60513 //                       return false;
60514 //                   }
60515 //                   return true;
60516 //                });
60517 //                return;
60518 //            }
60519 //        }
60520         
60521         var days = date.getDaysInMonth();
60522         
60523         var firstOfMonth = date.getFirstDateOfMonth();
60524         var startingPos = firstOfMonth.getDay()-this.startDay;
60525         
60526         if(startingPos < this.startDay){
60527             startingPos += 7;
60528         }
60529         
60530         var pm = date.add(Date.MONTH, -1);
60531         var prevStart = pm.getDaysInMonth()-startingPos;
60532 //        
60533         
60534         
60535         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
60536         
60537         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
60538         //this.cells.addClassOnOver('fc-state-hover');
60539         
60540         var cells = this.cells.elements;
60541         var textEls = this.textNodes;
60542         
60543         //Roo.each(cells, function(cell){
60544         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
60545         //});
60546         
60547         days += startingPos;
60548
60549         // convert everything to numbers so it's fast
60550         var day = 86400000;
60551         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
60552         //Roo.log(d);
60553         //Roo.log(pm);
60554         //Roo.log(prevStart);
60555         
60556         var today = new Date().clearTime().getTime();
60557         var sel = date.clearTime().getTime();
60558         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
60559         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
60560         var ddMatch = this.disabledDatesRE;
60561         var ddText = this.disabledDatesText;
60562         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
60563         var ddaysText = this.disabledDaysText;
60564         var format = this.format;
60565         
60566         var setCellClass = function(cal, cell){
60567             
60568             //Roo.log('set Cell Class');
60569             cell.title = "";
60570             var t = d.getTime();
60571             
60572             //Roo.log(d);
60573             
60574             
60575             cell.dateValue = t;
60576             if(t == today){
60577                 cell.className += " fc-today";
60578                 cell.className += " fc-state-highlight";
60579                 cell.title = cal.todayText;
60580             }
60581             if(t == sel){
60582                 // disable highlight in other month..
60583                 cell.className += " fc-state-highlight";
60584                 
60585             }
60586             // disabling
60587             if(t < min) {
60588                 //cell.className = " fc-state-disabled";
60589                 cell.title = cal.minText;
60590                 return;
60591             }
60592             if(t > max) {
60593                 //cell.className = " fc-state-disabled";
60594                 cell.title = cal.maxText;
60595                 return;
60596             }
60597             if(ddays){
60598                 if(ddays.indexOf(d.getDay()) != -1){
60599                     // cell.title = ddaysText;
60600                    // cell.className = " fc-state-disabled";
60601                 }
60602             }
60603             if(ddMatch && format){
60604                 var fvalue = d.dateFormat(format);
60605                 if(ddMatch.test(fvalue)){
60606                     cell.title = ddText.replace("%0", fvalue);
60607                    cell.className = " fc-state-disabled";
60608                 }
60609             }
60610             
60611             if (!cell.initialClassName) {
60612                 cell.initialClassName = cell.dom.className;
60613             }
60614             
60615             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
60616         };
60617
60618         var i = 0;
60619         
60620         for(; i < startingPos; i++) {
60621             cells[i].dayName =  (++prevStart);
60622             Roo.log(textEls[i]);
60623             d.setDate(d.getDate()+1);
60624             
60625             //cells[i].className = "fc-past fc-other-month";
60626             setCellClass(this, cells[i]);
60627         }
60628         
60629         var intDay = 0;
60630         
60631         for(; i < days; i++){
60632             intDay = i - startingPos + 1;
60633             cells[i].dayName =  (intDay);
60634             d.setDate(d.getDate()+1);
60635             
60636             cells[i].className = ''; // "x-date-active";
60637             setCellClass(this, cells[i]);
60638         }
60639         var extraDays = 0;
60640         
60641         for(; i < 42; i++) {
60642             //textEls[i].innerHTML = (++extraDays);
60643             
60644             d.setDate(d.getDate()+1);
60645             cells[i].dayName = (++extraDays);
60646             cells[i].className = "fc-future fc-other-month";
60647             setCellClass(this, cells[i]);
60648         }
60649         
60650         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
60651         
60652         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
60653         
60654         // this will cause all the cells to mis
60655         var rows= [];
60656         var i =0;
60657         for (var r = 0;r < 6;r++) {
60658             for (var c =0;c < 7;c++) {
60659                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
60660             }    
60661         }
60662         
60663         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
60664         for(i=0;i<cells.length;i++) {
60665             
60666             this.cells.elements[i].dayName = cells[i].dayName ;
60667             this.cells.elements[i].className = cells[i].className;
60668             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
60669             this.cells.elements[i].title = cells[i].title ;
60670             this.cells.elements[i].dateValue = cells[i].dateValue ;
60671         }
60672         
60673         
60674         
60675         
60676         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
60677         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
60678         
60679         ////if(totalRows != 6){
60680             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
60681            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
60682        // }
60683         
60684         this.fireEvent('monthchange', this, date);
60685         
60686         
60687     },
60688  /**
60689      * Returns the grid's SelectionModel.
60690      * @return {SelectionModel}
60691      */
60692     getSelectionModel : function(){
60693         if(!this.selModel){
60694             this.selModel = new Roo.grid.CellSelectionModel();
60695         }
60696         return this.selModel;
60697     },
60698
60699     load: function() {
60700         this.eventStore.load()
60701         
60702         
60703         
60704     },
60705     
60706     findCell : function(dt) {
60707         dt = dt.clearTime().getTime();
60708         var ret = false;
60709         this.cells.each(function(c){
60710             //Roo.log("check " +c.dateValue + '?=' + dt);
60711             if(c.dateValue == dt){
60712                 ret = c;
60713                 return false;
60714             }
60715             return true;
60716         });
60717         
60718         return ret;
60719     },
60720     
60721     findCells : function(rec) {
60722         var s = rec.data.start_dt.clone().clearTime().getTime();
60723        // Roo.log(s);
60724         var e= rec.data.end_dt.clone().clearTime().getTime();
60725        // Roo.log(e);
60726         var ret = [];
60727         this.cells.each(function(c){
60728              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
60729             
60730             if(c.dateValue > e){
60731                 return ;
60732             }
60733             if(c.dateValue < s){
60734                 return ;
60735             }
60736             ret.push(c);
60737         });
60738         
60739         return ret;    
60740     },
60741     
60742     findBestRow: function(cells)
60743     {
60744         var ret = 0;
60745         
60746         for (var i =0 ; i < cells.length;i++) {
60747             ret  = Math.max(cells[i].rows || 0,ret);
60748         }
60749         return ret;
60750         
60751     },
60752     
60753     
60754     addItem : function(rec)
60755     {
60756         // look for vertical location slot in
60757         var cells = this.findCells(rec);
60758         
60759         rec.row = this.findBestRow(cells);
60760         
60761         // work out the location.
60762         
60763         var crow = false;
60764         var rows = [];
60765         for(var i =0; i < cells.length; i++) {
60766             if (!crow) {
60767                 crow = {
60768                     start : cells[i],
60769                     end :  cells[i]
60770                 };
60771                 continue;
60772             }
60773             if (crow.start.getY() == cells[i].getY()) {
60774                 // on same row.
60775                 crow.end = cells[i];
60776                 continue;
60777             }
60778             // different row.
60779             rows.push(crow);
60780             crow = {
60781                 start: cells[i],
60782                 end : cells[i]
60783             };
60784             
60785         }
60786         
60787         rows.push(crow);
60788         rec.els = [];
60789         rec.rows = rows;
60790         rec.cells = cells;
60791         for (var i = 0; i < cells.length;i++) {
60792             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
60793             
60794         }
60795         
60796         
60797     },
60798     
60799     clearEvents: function() {
60800         
60801         if (!this.eventStore.getCount()) {
60802             return;
60803         }
60804         // reset number of rows in cells.
60805         Roo.each(this.cells.elements, function(c){
60806             c.rows = 0;
60807         });
60808         
60809         this.eventStore.each(function(e) {
60810             this.clearEvent(e);
60811         },this);
60812         
60813     },
60814     
60815     clearEvent : function(ev)
60816     {
60817         if (ev.els) {
60818             Roo.each(ev.els, function(el) {
60819                 el.un('mouseenter' ,this.onEventEnter, this);
60820                 el.un('mouseleave' ,this.onEventLeave, this);
60821                 el.remove();
60822             },this);
60823             ev.els = [];
60824         }
60825     },
60826     
60827     
60828     renderEvent : function(ev,ctr) {
60829         if (!ctr) {
60830              ctr = this.view.el.select('.fc-event-container',true).first();
60831         }
60832         
60833          
60834         this.clearEvent(ev);
60835             //code
60836        
60837         
60838         
60839         ev.els = [];
60840         var cells = ev.cells;
60841         var rows = ev.rows;
60842         this.fireEvent('eventrender', this, ev);
60843         
60844         for(var i =0; i < rows.length; i++) {
60845             
60846             cls = '';
60847             if (i == 0) {
60848                 cls += ' fc-event-start';
60849             }
60850             if ((i+1) == rows.length) {
60851                 cls += ' fc-event-end';
60852             }
60853             
60854             //Roo.log(ev.data);
60855             // how many rows should it span..
60856             var cg = this.eventTmpl.append(ctr,Roo.apply({
60857                 fccls : cls
60858                 
60859             }, ev.data) , true);
60860             
60861             
60862             cg.on('mouseenter' ,this.onEventEnter, this, ev);
60863             cg.on('mouseleave' ,this.onEventLeave, this, ev);
60864             cg.on('click', this.onEventClick, this, ev);
60865             
60866             ev.els.push(cg);
60867             
60868             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
60869             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
60870             //Roo.log(cg);
60871              
60872             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
60873             cg.setWidth(ebox.right - sbox.x -2);
60874         }
60875     },
60876     
60877     renderEvents: function()
60878     {   
60879         // first make sure there is enough space..
60880         
60881         if (!this.eventTmpl) {
60882             this.eventTmpl = new Roo.Template(
60883                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
60884                     '<div class="fc-event-inner">' +
60885                         '<span class="fc-event-time">{time}</span>' +
60886                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
60887                     '</div>' +
60888                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
60889                 '</div>'
60890             );
60891                 
60892         }
60893                
60894         
60895         
60896         this.cells.each(function(c) {
60897             //Roo.log(c.select('.fc-day-content div',true).first());
60898             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
60899         });
60900         
60901         var ctr = this.view.el.select('.fc-event-container',true).first();
60902         
60903         var cls;
60904         this.eventStore.each(function(ev){
60905             
60906             this.renderEvent(ev);
60907              
60908              
60909         }, this);
60910         this.view.layout();
60911         
60912     },
60913     
60914     onEventEnter: function (e, el,event,d) {
60915         this.fireEvent('evententer', this, el, event);
60916     },
60917     
60918     onEventLeave: function (e, el,event,d) {
60919         this.fireEvent('eventleave', this, el, event);
60920     },
60921     
60922     onEventClick: function (e, el,event,d) {
60923         this.fireEvent('eventclick', this, el, event);
60924     },
60925     
60926     onMonthChange: function () {
60927         this.store.load();
60928     },
60929     
60930     onLoad: function () {
60931         
60932         //Roo.log('calendar onload');
60933 //         
60934         if(this.eventStore.getCount() > 0){
60935             
60936            
60937             
60938             this.eventStore.each(function(d){
60939                 
60940                 
60941                 // FIXME..
60942                 var add =   d.data;
60943                 if (typeof(add.end_dt) == 'undefined')  {
60944                     Roo.log("Missing End time in calendar data: ");
60945                     Roo.log(d);
60946                     return;
60947                 }
60948                 if (typeof(add.start_dt) == 'undefined')  {
60949                     Roo.log("Missing Start time in calendar data: ");
60950                     Roo.log(d);
60951                     return;
60952                 }
60953                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
60954                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
60955                 add.id = add.id || d.id;
60956                 add.title = add.title || '??';
60957                 
60958                 this.addItem(d);
60959                 
60960              
60961             },this);
60962         }
60963         
60964         this.renderEvents();
60965     }
60966     
60967
60968 });
60969 /*
60970  grid : {
60971                 xtype: 'Grid',
60972                 xns: Roo.grid,
60973                 listeners : {
60974                     render : function ()
60975                     {
60976                         _this.grid = this;
60977                         
60978                         if (!this.view.el.hasClass('course-timesheet')) {
60979                             this.view.el.addClass('course-timesheet');
60980                         }
60981                         if (this.tsStyle) {
60982                             this.ds.load({});
60983                             return; 
60984                         }
60985                         Roo.log('width');
60986                         Roo.log(_this.grid.view.el.getWidth());
60987                         
60988                         
60989                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
60990                             '.course-timesheet .x-grid-row' : {
60991                                 height: '80px'
60992                             },
60993                             '.x-grid-row td' : {
60994                                 'vertical-align' : 0
60995                             },
60996                             '.course-edit-link' : {
60997                                 'color' : 'blue',
60998                                 'text-overflow' : 'ellipsis',
60999                                 'overflow' : 'hidden',
61000                                 'white-space' : 'nowrap',
61001                                 'cursor' : 'pointer'
61002                             },
61003                             '.sub-link' : {
61004                                 'color' : 'green'
61005                             },
61006                             '.de-act-sup-link' : {
61007                                 'color' : 'purple',
61008                                 'text-decoration' : 'line-through'
61009                             },
61010                             '.de-act-link' : {
61011                                 'color' : 'red',
61012                                 'text-decoration' : 'line-through'
61013                             },
61014                             '.course-timesheet .course-highlight' : {
61015                                 'border-top-style': 'dashed !important',
61016                                 'border-bottom-bottom': 'dashed !important'
61017                             },
61018                             '.course-timesheet .course-item' : {
61019                                 'font-family'   : 'tahoma, arial, helvetica',
61020                                 'font-size'     : '11px',
61021                                 'overflow'      : 'hidden',
61022                                 'padding-left'  : '10px',
61023                                 'padding-right' : '10px',
61024                                 'padding-top' : '10px' 
61025                             }
61026                             
61027                         }, Roo.id());
61028                                 this.ds.load({});
61029                     }
61030                 },
61031                 autoWidth : true,
61032                 monitorWindowResize : false,
61033                 cellrenderer : function(v,x,r)
61034                 {
61035                     return v;
61036                 },
61037                 sm : {
61038                     xtype: 'CellSelectionModel',
61039                     xns: Roo.grid
61040                 },
61041                 dataSource : {
61042                     xtype: 'Store',
61043                     xns: Roo.data,
61044                     listeners : {
61045                         beforeload : function (_self, options)
61046                         {
61047                             options.params = options.params || {};
61048                             options.params._month = _this.monthField.getValue();
61049                             options.params.limit = 9999;
61050                             options.params['sort'] = 'when_dt';    
61051                             options.params['dir'] = 'ASC';    
61052                             this.proxy.loadResponse = this.loadResponse;
61053                             Roo.log("load?");
61054                             //this.addColumns();
61055                         },
61056                         load : function (_self, records, options)
61057                         {
61058                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
61059                                 // if you click on the translation.. you can edit it...
61060                                 var el = Roo.get(this);
61061                                 var id = el.dom.getAttribute('data-id');
61062                                 var d = el.dom.getAttribute('data-date');
61063                                 var t = el.dom.getAttribute('data-time');
61064                                 //var id = this.child('span').dom.textContent;
61065                                 
61066                                 //Roo.log(this);
61067                                 Pman.Dialog.CourseCalendar.show({
61068                                     id : id,
61069                                     when_d : d,
61070                                     when_t : t,
61071                                     productitem_active : id ? 1 : 0
61072                                 }, function() {
61073                                     _this.grid.ds.load({});
61074                                 });
61075                            
61076                            });
61077                            
61078                            _this.panel.fireEvent('resize', [ '', '' ]);
61079                         }
61080                     },
61081                     loadResponse : function(o, success, response){
61082                             // this is overridden on before load..
61083                             
61084                             Roo.log("our code?");       
61085                             //Roo.log(success);
61086                             //Roo.log(response)
61087                             delete this.activeRequest;
61088                             if(!success){
61089                                 this.fireEvent("loadexception", this, o, response);
61090                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
61091                                 return;
61092                             }
61093                             var result;
61094                             try {
61095                                 result = o.reader.read(response);
61096                             }catch(e){
61097                                 Roo.log("load exception?");
61098                                 this.fireEvent("loadexception", this, o, response, e);
61099                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
61100                                 return;
61101                             }
61102                             Roo.log("ready...");        
61103                             // loop through result.records;
61104                             // and set this.tdate[date] = [] << array of records..
61105                             _this.tdata  = {};
61106                             Roo.each(result.records, function(r){
61107                                 //Roo.log(r.data);
61108                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
61109                                     _this.tdata[r.data.when_dt.format('j')] = [];
61110                                 }
61111                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
61112                             });
61113                             
61114                             //Roo.log(_this.tdata);
61115                             
61116                             result.records = [];
61117                             result.totalRecords = 6;
61118                     
61119                             // let's generate some duumy records for the rows.
61120                             //var st = _this.dateField.getValue();
61121                             
61122                             // work out monday..
61123                             //st = st.add(Date.DAY, -1 * st.format('w'));
61124                             
61125                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
61126                             
61127                             var firstOfMonth = date.getFirstDayOfMonth();
61128                             var days = date.getDaysInMonth();
61129                             var d = 1;
61130                             var firstAdded = false;
61131                             for (var i = 0; i < result.totalRecords ; i++) {
61132                                 //var d= st.add(Date.DAY, i);
61133                                 var row = {};
61134                                 var added = 0;
61135                                 for(var w = 0 ; w < 7 ; w++){
61136                                     if(!firstAdded && firstOfMonth != w){
61137                                         continue;
61138                                     }
61139                                     if(d > days){
61140                                         continue;
61141                                     }
61142                                     firstAdded = true;
61143                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
61144                                     row['weekday'+w] = String.format(
61145                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
61146                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
61147                                                     d,
61148                                                     date.format('Y-m-')+dd
61149                                                 );
61150                                     added++;
61151                                     if(typeof(_this.tdata[d]) != 'undefined'){
61152                                         Roo.each(_this.tdata[d], function(r){
61153                                             var is_sub = '';
61154                                             var deactive = '';
61155                                             var id = r.id;
61156                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
61157                                             if(r.parent_id*1>0){
61158                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
61159                                                 id = r.parent_id;
61160                                             }
61161                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
61162                                                 deactive = 'de-act-link';
61163                                             }
61164                                             
61165                                             row['weekday'+w] += String.format(
61166                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
61167                                                     id, //0
61168                                                     r.product_id_name, //1
61169                                                     r.when_dt.format('h:ia'), //2
61170                                                     is_sub, //3
61171                                                     deactive, //4
61172                                                     desc // 5
61173                                             );
61174                                         });
61175                                     }
61176                                     d++;
61177                                 }
61178                                 
61179                                 // only do this if something added..
61180                                 if(added > 0){ 
61181                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
61182                                 }
61183                                 
61184                                 
61185                                 // push it twice. (second one with an hour..
61186                                 
61187                             }
61188                             //Roo.log(result);
61189                             this.fireEvent("load", this, o, o.request.arg);
61190                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
61191                         },
61192                     sortInfo : {field: 'when_dt', direction : 'ASC' },
61193                     proxy : {
61194                         xtype: 'HttpProxy',
61195                         xns: Roo.data,
61196                         method : 'GET',
61197                         url : baseURL + '/Roo/Shop_course.php'
61198                     },
61199                     reader : {
61200                         xtype: 'JsonReader',
61201                         xns: Roo.data,
61202                         id : 'id',
61203                         fields : [
61204                             {
61205                                 'name': 'id',
61206                                 'type': 'int'
61207                             },
61208                             {
61209                                 'name': 'when_dt',
61210                                 'type': 'string'
61211                             },
61212                             {
61213                                 'name': 'end_dt',
61214                                 'type': 'string'
61215                             },
61216                             {
61217                                 'name': 'parent_id',
61218                                 'type': 'int'
61219                             },
61220                             {
61221                                 'name': 'product_id',
61222                                 'type': 'int'
61223                             },
61224                             {
61225                                 'name': 'productitem_id',
61226                                 'type': 'int'
61227                             },
61228                             {
61229                                 'name': 'guid',
61230                                 'type': 'int'
61231                             }
61232                         ]
61233                     }
61234                 },
61235                 toolbar : {
61236                     xtype: 'Toolbar',
61237                     xns: Roo,
61238                     items : [
61239                         {
61240                             xtype: 'Button',
61241                             xns: Roo.Toolbar,
61242                             listeners : {
61243                                 click : function (_self, e)
61244                                 {
61245                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
61246                                     sd.setMonth(sd.getMonth()-1);
61247                                     _this.monthField.setValue(sd.format('Y-m-d'));
61248                                     _this.grid.ds.load({});
61249                                 }
61250                             },
61251                             text : "Back"
61252                         },
61253                         {
61254                             xtype: 'Separator',
61255                             xns: Roo.Toolbar
61256                         },
61257                         {
61258                             xtype: 'MonthField',
61259                             xns: Roo.form,
61260                             listeners : {
61261                                 render : function (_self)
61262                                 {
61263                                     _this.monthField = _self;
61264                                    // _this.monthField.set  today
61265                                 },
61266                                 select : function (combo, date)
61267                                 {
61268                                     _this.grid.ds.load({});
61269                                 }
61270                             },
61271                             value : (function() { return new Date(); })()
61272                         },
61273                         {
61274                             xtype: 'Separator',
61275                             xns: Roo.Toolbar
61276                         },
61277                         {
61278                             xtype: 'TextItem',
61279                             xns: Roo.Toolbar,
61280                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
61281                         },
61282                         {
61283                             xtype: 'Fill',
61284                             xns: Roo.Toolbar
61285                         },
61286                         {
61287                             xtype: 'Button',
61288                             xns: Roo.Toolbar,
61289                             listeners : {
61290                                 click : function (_self, e)
61291                                 {
61292                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
61293                                     sd.setMonth(sd.getMonth()+1);
61294                                     _this.monthField.setValue(sd.format('Y-m-d'));
61295                                     _this.grid.ds.load({});
61296                                 }
61297                             },
61298                             text : "Next"
61299                         }
61300                     ]
61301                 },
61302                  
61303             }
61304         };
61305         
61306         *//*
61307  * Based on:
61308  * Ext JS Library 1.1.1
61309  * Copyright(c) 2006-2007, Ext JS, LLC.
61310  *
61311  * Originally Released Under LGPL - original licence link has changed is not relivant.
61312  *
61313  * Fork - LGPL
61314  * <script type="text/javascript">
61315  */
61316  
61317 /**
61318  * @class Roo.LoadMask
61319  * A simple utility class for generically masking elements while loading data.  If the element being masked has
61320  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
61321  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
61322  * element's UpdateManager load indicator and will be destroyed after the initial load.
61323  * @constructor
61324  * Create a new LoadMask
61325  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
61326  * @param {Object} config The config object
61327  */
61328 Roo.LoadMask = function(el, config){
61329     this.el = Roo.get(el);
61330     Roo.apply(this, config);
61331     if(this.store){
61332         this.store.on('beforeload', this.onBeforeLoad, this);
61333         this.store.on('load', this.onLoad, this);
61334         this.store.on('loadexception', this.onLoadException, this);
61335         this.removeMask = false;
61336     }else{
61337         var um = this.el.getUpdateManager();
61338         um.showLoadIndicator = false; // disable the default indicator
61339         um.on('beforeupdate', this.onBeforeLoad, this);
61340         um.on('update', this.onLoad, this);
61341         um.on('failure', this.onLoad, this);
61342         this.removeMask = true;
61343     }
61344 };
61345
61346 Roo.LoadMask.prototype = {
61347     /**
61348      * @cfg {Boolean} removeMask
61349      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
61350      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
61351      */
61352     /**
61353      * @cfg {String} msg
61354      * The text to display in a centered loading message box (defaults to 'Loading...')
61355      */
61356     msg : 'Loading...',
61357     /**
61358      * @cfg {String} msgCls
61359      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
61360      */
61361     msgCls : 'x-mask-loading',
61362
61363     /**
61364      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
61365      * @type Boolean
61366      */
61367     disabled: false,
61368
61369     /**
61370      * Disables the mask to prevent it from being displayed
61371      */
61372     disable : function(){
61373        this.disabled = true;
61374     },
61375
61376     /**
61377      * Enables the mask so that it can be displayed
61378      */
61379     enable : function(){
61380         this.disabled = false;
61381     },
61382     
61383     onLoadException : function()
61384     {
61385         Roo.log(arguments);
61386         
61387         if (typeof(arguments[3]) != 'undefined') {
61388             Roo.MessageBox.alert("Error loading",arguments[3]);
61389         } 
61390         /*
61391         try {
61392             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
61393                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
61394             }   
61395         } catch(e) {
61396             
61397         }
61398         */
61399     
61400         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
61401     },
61402     // private
61403     onLoad : function()
61404     {
61405         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
61406     },
61407
61408     // private
61409     onBeforeLoad : function(){
61410         if(!this.disabled){
61411             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
61412         }
61413     },
61414
61415     // private
61416     destroy : function(){
61417         if(this.store){
61418             this.store.un('beforeload', this.onBeforeLoad, this);
61419             this.store.un('load', this.onLoad, this);
61420             this.store.un('loadexception', this.onLoadException, this);
61421         }else{
61422             var um = this.el.getUpdateManager();
61423             um.un('beforeupdate', this.onBeforeLoad, this);
61424             um.un('update', this.onLoad, this);
61425             um.un('failure', this.onLoad, this);
61426         }
61427     }
61428 };/*
61429  * Based on:
61430  * Ext JS Library 1.1.1
61431  * Copyright(c) 2006-2007, Ext JS, LLC.
61432  *
61433  * Originally Released Under LGPL - original licence link has changed is not relivant.
61434  *
61435  * Fork - LGPL
61436  * <script type="text/javascript">
61437  */
61438
61439
61440 /**
61441  * @class Roo.XTemplate
61442  * @extends Roo.Template
61443  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
61444 <pre><code>
61445 var t = new Roo.XTemplate(
61446         '&lt;select name="{name}"&gt;',
61447                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
61448         '&lt;/select&gt;'
61449 );
61450  
61451 // then append, applying the master template values
61452  </code></pre>
61453  *
61454  * Supported features:
61455  *
61456  *  Tags:
61457
61458 <pre><code>
61459       {a_variable} - output encoded.
61460       {a_variable.format:("Y-m-d")} - call a method on the variable
61461       {a_variable:raw} - unencoded output
61462       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
61463       {a_variable:this.method_on_template(...)} - call a method on the template object.
61464  
61465 </code></pre>
61466  *  The tpl tag:
61467 <pre><code>
61468         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
61469         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
61470         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
61471         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
61472   
61473         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
61474         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
61475 </code></pre>
61476  *      
61477  */
61478 Roo.XTemplate = function()
61479 {
61480     Roo.XTemplate.superclass.constructor.apply(this, arguments);
61481     if (this.html) {
61482         this.compile();
61483     }
61484 };
61485
61486
61487 Roo.extend(Roo.XTemplate, Roo.Template, {
61488
61489     /**
61490      * The various sub templates
61491      */
61492     tpls : false,
61493     /**
61494      *
61495      * basic tag replacing syntax
61496      * WORD:WORD()
61497      *
61498      * // you can fake an object call by doing this
61499      *  x.t:(test,tesT) 
61500      * 
61501      */
61502     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
61503
61504     /**
61505      * compile the template
61506      *
61507      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
61508      *
61509      */
61510     compile: function()
61511     {
61512         var s = this.html;
61513      
61514         s = ['<tpl>', s, '</tpl>'].join('');
61515     
61516         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
61517             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
61518             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
61519             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
61520             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
61521             m,
61522             id     = 0,
61523             tpls   = [];
61524     
61525         while(true == !!(m = s.match(re))){
61526             var forMatch   = m[0].match(nameRe),
61527                 ifMatch   = m[0].match(ifRe),
61528                 execMatch   = m[0].match(execRe),
61529                 namedMatch   = m[0].match(namedRe),
61530                 
61531                 exp  = null, 
61532                 fn   = null,
61533                 exec = null,
61534                 name = forMatch && forMatch[1] ? forMatch[1] : '';
61535                 
61536             if (ifMatch) {
61537                 // if - puts fn into test..
61538                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
61539                 if(exp){
61540                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
61541                 }
61542             }
61543             
61544             if (execMatch) {
61545                 // exec - calls a function... returns empty if true is  returned.
61546                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
61547                 if(exp){
61548                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
61549                 }
61550             }
61551             
61552             
61553             if (name) {
61554                 // for = 
61555                 switch(name){
61556                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
61557                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
61558                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
61559                 }
61560             }
61561             var uid = namedMatch ? namedMatch[1] : id;
61562             
61563             
61564             tpls.push({
61565                 id:     namedMatch ? namedMatch[1] : id,
61566                 target: name,
61567                 exec:   exec,
61568                 test:   fn,
61569                 body:   m[1] || ''
61570             });
61571             if (namedMatch) {
61572                 s = s.replace(m[0], '');
61573             } else { 
61574                 s = s.replace(m[0], '{xtpl'+ id + '}');
61575             }
61576             ++id;
61577         }
61578         this.tpls = [];
61579         for(var i = tpls.length-1; i >= 0; --i){
61580             this.compileTpl(tpls[i]);
61581             this.tpls[tpls[i].id] = tpls[i];
61582         }
61583         this.master = tpls[tpls.length-1];
61584         return this;
61585     },
61586     /**
61587      * same as applyTemplate, except it's done to one of the subTemplates
61588      * when using named templates, you can do:
61589      *
61590      * var str = pl.applySubTemplate('your-name', values);
61591      *
61592      * 
61593      * @param {Number} id of the template
61594      * @param {Object} values to apply to template
61595      * @param {Object} parent (normaly the instance of this object)
61596      */
61597     applySubTemplate : function(id, values, parent)
61598     {
61599         
61600         
61601         var t = this.tpls[id];
61602         
61603         
61604         try { 
61605             if(t.test && !t.test.call(this, values, parent)){
61606                 return '';
61607             }
61608         } catch(e) {
61609             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
61610             Roo.log(e.toString());
61611             Roo.log(t.test);
61612             return ''
61613         }
61614         try { 
61615             
61616             if(t.exec && t.exec.call(this, values, parent)){
61617                 return '';
61618             }
61619         } catch(e) {
61620             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
61621             Roo.log(e.toString());
61622             Roo.log(t.exec);
61623             return ''
61624         }
61625         try {
61626             var vs = t.target ? t.target.call(this, values, parent) : values;
61627             parent = t.target ? values : parent;
61628             if(t.target && vs instanceof Array){
61629                 var buf = [];
61630                 for(var i = 0, len = vs.length; i < len; i++){
61631                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
61632                 }
61633                 return buf.join('');
61634             }
61635             return t.compiled.call(this, vs, parent);
61636         } catch (e) {
61637             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
61638             Roo.log(e.toString());
61639             Roo.log(t.compiled);
61640             return '';
61641         }
61642     },
61643
61644     compileTpl : function(tpl)
61645     {
61646         var fm = Roo.util.Format;
61647         var useF = this.disableFormats !== true;
61648         var sep = Roo.isGecko ? "+" : ",";
61649         var undef = function(str) {
61650             Roo.log("Property not found :"  + str);
61651             return '';
61652         };
61653         
61654         var fn = function(m, name, format, args)
61655         {
61656             //Roo.log(arguments);
61657             args = args ? args.replace(/\\'/g,"'") : args;
61658             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
61659             if (typeof(format) == 'undefined') {
61660                 format= 'htmlEncode';
61661             }
61662             if (format == 'raw' ) {
61663                 format = false;
61664             }
61665             
61666             if(name.substr(0, 4) == 'xtpl'){
61667                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
61668             }
61669             
61670             // build an array of options to determine if value is undefined..
61671             
61672             // basically get 'xxxx.yyyy' then do
61673             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
61674             //    (function () { Roo.log("Property not found"); return ''; })() :
61675             //    ......
61676             
61677             var udef_ar = [];
61678             var lookfor = '';
61679             Roo.each(name.split('.'), function(st) {
61680                 lookfor += (lookfor.length ? '.': '') + st;
61681                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
61682             });
61683             
61684             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
61685             
61686             
61687             if(format && useF){
61688                 
61689                 args = args ? ',' + args : "";
61690                  
61691                 if(format.substr(0, 5) != "this."){
61692                     format = "fm." + format + '(';
61693                 }else{
61694                     format = 'this.call("'+ format.substr(5) + '", ';
61695                     args = ", values";
61696                 }
61697                 
61698                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
61699             }
61700              
61701             if (args.length) {
61702                 // called with xxyx.yuu:(test,test)
61703                 // change to ()
61704                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
61705             }
61706             // raw.. - :raw modifier..
61707             return "'"+ sep + udef_st  + name + ")"+sep+"'";
61708             
61709         };
61710         var body;
61711         // branched to use + in gecko and [].join() in others
61712         if(Roo.isGecko){
61713             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
61714                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
61715                     "';};};";
61716         }else{
61717             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
61718             body.push(tpl.body.replace(/(\r\n|\n)/g,
61719                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
61720             body.push("'].join('');};};");
61721             body = body.join('');
61722         }
61723         
61724         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
61725        
61726         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
61727         eval(body);
61728         
61729         return this;
61730     },
61731
61732     applyTemplate : function(values){
61733         return this.master.compiled.call(this, values, {});
61734         //var s = this.subs;
61735     },
61736
61737     apply : function(){
61738         return this.applyTemplate.apply(this, arguments);
61739     }
61740
61741  });
61742
61743 Roo.XTemplate.from = function(el){
61744     el = Roo.getDom(el);
61745     return new Roo.XTemplate(el.value || el.innerHTML);
61746 };