add support for onLoad on template
[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      * Returns an HTML fragment of this template with the specified values applied.
4698      * @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'})
4699      * @return {String} The HTML fragment
4700      */
4701     applyTemplate : function(values){
4702         //Roo.log(["applyTemplate", values]);
4703         try {
4704            
4705             if(this.compiled){
4706                 return this.compiled(values);
4707             }
4708             var useF = this.disableFormats !== true;
4709             var fm = Roo.util.Format, tpl = this;
4710             var fn = function(m, name, format, args){
4711                 if(format && useF){
4712                     if(format.substr(0, 5) == "this."){
4713                         return tpl.call(format.substr(5), values[name], values);
4714                     }else{
4715                         if(args){
4716                             // quoted values are required for strings in compiled templates, 
4717                             // but for non compiled we need to strip them
4718                             // quoted reversed for jsmin
4719                             var re = /^\s*['"](.*)["']\s*$/;
4720                             args = args.split(',');
4721                             for(var i = 0, len = args.length; i < len; i++){
4722                                 args[i] = args[i].replace(re, "$1");
4723                             }
4724                             args = [values[name]].concat(args);
4725                         }else{
4726                             args = [values[name]];
4727                         }
4728                         return fm[format].apply(fm, args);
4729                     }
4730                 }else{
4731                     return values[name] !== undefined ? values[name] : "";
4732                 }
4733             };
4734             return this.html.replace(this.re, fn);
4735         } catch (e) {
4736             Roo.log(e);
4737             throw e;
4738         }
4739          
4740     },
4741     
4742     loading : false,
4743       
4744     load : function ()
4745     {
4746          
4747         if (this.loading) {
4748             return;
4749         }
4750         var _t = this;
4751         
4752         this.loading = true;
4753         this.compiled = false;
4754         
4755         var cx = new Roo.data.Connection();
4756         cx.request({
4757             url : this.url,
4758             method : 'GET',
4759             success : function (response) {
4760                 _t.loading = false;
4761                 _t.html = response.responseText;
4762                 _t.url = false;
4763                 _t.compile();
4764                 if (_t.onLoad) {
4765                     _t.onload();
4766                 }
4767              },
4768             failure : function(response) {
4769                 Roo.log("Template failed to load from " + _t.url);
4770                 _t.loading = false;
4771             }
4772         });
4773     },
4774
4775     /**
4776      * Sets the HTML used as the template and optionally compiles it.
4777      * @param {String} html
4778      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
4779      * @return {Roo.Template} this
4780      */
4781     set : function(html, compile){
4782         this.html = html;
4783         this.compiled = null;
4784         if(compile){
4785             this.compile();
4786         }
4787         return this;
4788     },
4789     
4790     /**
4791      * True to disable format functions (defaults to false)
4792      * @type Boolean
4793      */
4794     disableFormats : false,
4795     
4796     /**
4797     * The regular expression used to match template variables 
4798     * @type RegExp
4799     * @property 
4800     */
4801     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
4802     
4803     /**
4804      * Compiles the template into an internal function, eliminating the RegEx overhead.
4805      * @return {Roo.Template} this
4806      */
4807     compile : function(){
4808         var fm = Roo.util.Format;
4809         var useF = this.disableFormats !== true;
4810         var sep = Roo.isGecko ? "+" : ",";
4811         var fn = function(m, name, format, args){
4812             if(format && useF){
4813                 args = args ? ',' + args : "";
4814                 if(format.substr(0, 5) != "this."){
4815                     format = "fm." + format + '(';
4816                 }else{
4817                     format = 'this.call("'+ format.substr(5) + '", ';
4818                     args = ", values";
4819                 }
4820             }else{
4821                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
4822             }
4823             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
4824         };
4825         var body;
4826         // branched to use + in gecko and [].join() in others
4827         if(Roo.isGecko){
4828             body = "this.compiled = function(values){ return '" +
4829                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
4830                     "';};";
4831         }else{
4832             body = ["this.compiled = function(values){ return ['"];
4833             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
4834             body.push("'].join('');};");
4835             body = body.join('');
4836         }
4837         /**
4838          * eval:var:values
4839          * eval:var:fm
4840          */
4841         eval(body);
4842         return this;
4843     },
4844     
4845     // private function used to call members
4846     call : function(fnName, value, allValues){
4847         return this[fnName](value, allValues);
4848     },
4849     
4850     /**
4851      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
4852      * @param {String/HTMLElement/Roo.Element} el The context element
4853      * @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'})
4854      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4855      * @return {HTMLElement/Roo.Element} The new node or Element
4856      */
4857     insertFirst: function(el, values, returnElement){
4858         return this.doInsert('afterBegin', el, values, returnElement);
4859     },
4860
4861     /**
4862      * Applies the supplied values to the template and inserts the new node(s) before el.
4863      * @param {String/HTMLElement/Roo.Element} el The context element
4864      * @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'})
4865      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4866      * @return {HTMLElement/Roo.Element} The new node or Element
4867      */
4868     insertBefore: function(el, values, returnElement){
4869         return this.doInsert('beforeBegin', el, values, returnElement);
4870     },
4871
4872     /**
4873      * Applies the supplied values to the template and inserts the new node(s) after el.
4874      * @param {String/HTMLElement/Roo.Element} el The context element
4875      * @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'})
4876      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4877      * @return {HTMLElement/Roo.Element} The new node or Element
4878      */
4879     insertAfter : function(el, values, returnElement){
4880         return this.doInsert('afterEnd', el, values, returnElement);
4881     },
4882     
4883     /**
4884      * Applies the supplied values to the template and appends the new node(s) to el.
4885      * @param {String/HTMLElement/Roo.Element} el The context element
4886      * @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'})
4887      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4888      * @return {HTMLElement/Roo.Element} The new node or Element
4889      */
4890     append : function(el, values, returnElement){
4891         return this.doInsert('beforeEnd', el, values, returnElement);
4892     },
4893
4894     doInsert : function(where, el, values, returnEl){
4895         el = Roo.getDom(el);
4896         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
4897         return returnEl ? Roo.get(newNode, true) : newNode;
4898     },
4899
4900     /**
4901      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
4902      * @param {String/HTMLElement/Roo.Element} el The context element
4903      * @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'})
4904      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4905      * @return {HTMLElement/Roo.Element} The new node or Element
4906      */
4907     overwrite : function(el, values, returnElement){
4908         el = Roo.getDom(el);
4909         el.innerHTML = this.applyTemplate(values);
4910         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4911     }
4912 };
4913 /**
4914  * Alias for {@link #applyTemplate}
4915  * @method
4916  */
4917 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
4918
4919 // backwards compat
4920 Roo.DomHelper.Template = Roo.Template;
4921
4922 /**
4923  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
4924  * @param {String/HTMLElement} el A DOM element or its id
4925  * @returns {Roo.Template} The created template
4926  * @static
4927  */
4928 Roo.Template.from = function(el){
4929     el = Roo.getDom(el);
4930     return new Roo.Template(el.value || el.innerHTML);
4931 };/*
4932  * Based on:
4933  * Ext JS Library 1.1.1
4934  * Copyright(c) 2006-2007, Ext JS, LLC.
4935  *
4936  * Originally Released Under LGPL - original licence link has changed is not relivant.
4937  *
4938  * Fork - LGPL
4939  * <script type="text/javascript">
4940  */
4941  
4942
4943 /*
4944  * This is code is also distributed under MIT license for use
4945  * with jQuery and prototype JavaScript libraries.
4946  */
4947 /**
4948  * @class Roo.DomQuery
4949 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).
4950 <p>
4951 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>
4952
4953 <p>
4954 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.
4955 </p>
4956 <h4>Element Selectors:</h4>
4957 <ul class="list">
4958     <li> <b>*</b> any element</li>
4959     <li> <b>E</b> an element with the tag E</li>
4960     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
4961     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
4962     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
4963     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
4964 </ul>
4965 <h4>Attribute Selectors:</h4>
4966 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
4967 <ul class="list">
4968     <li> <b>E[foo]</b> has an attribute "foo"</li>
4969     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
4970     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
4971     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
4972     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
4973     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
4974     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
4975 </ul>
4976 <h4>Pseudo Classes:</h4>
4977 <ul class="list">
4978     <li> <b>E:first-child</b> E is the first child of its parent</li>
4979     <li> <b>E:last-child</b> E is the last child of its parent</li>
4980     <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>
4981     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
4982     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
4983     <li> <b>E:only-child</b> E is the only child of its parent</li>
4984     <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>
4985     <li> <b>E:first</b> the first E in the resultset</li>
4986     <li> <b>E:last</b> the last E in the resultset</li>
4987     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
4988     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
4989     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
4990     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
4991     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
4992     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
4993     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
4994     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
4995     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
4996 </ul>
4997 <h4>CSS Value Selectors:</h4>
4998 <ul class="list">
4999     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
5000     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
5001     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
5002     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
5003     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
5004     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
5005 </ul>
5006  * @singleton
5007  */
5008 Roo.DomQuery = function(){
5009     var cache = {}, simpleCache = {}, valueCache = {};
5010     var nonSpace = /\S/;
5011     var trimRe = /^\s+|\s+$/g;
5012     var tplRe = /\{(\d+)\}/g;
5013     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
5014     var tagTokenRe = /^(#)?([\w-\*]+)/;
5015     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
5016
5017     function child(p, index){
5018         var i = 0;
5019         var n = p.firstChild;
5020         while(n){
5021             if(n.nodeType == 1){
5022                if(++i == index){
5023                    return n;
5024                }
5025             }
5026             n = n.nextSibling;
5027         }
5028         return null;
5029     };
5030
5031     function next(n){
5032         while((n = n.nextSibling) && n.nodeType != 1);
5033         return n;
5034     };
5035
5036     function prev(n){
5037         while((n = n.previousSibling) && n.nodeType != 1);
5038         return n;
5039     };
5040
5041     function children(d){
5042         var n = d.firstChild, ni = -1;
5043             while(n){
5044                 var nx = n.nextSibling;
5045                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
5046                     d.removeChild(n);
5047                 }else{
5048                     n.nodeIndex = ++ni;
5049                 }
5050                 n = nx;
5051             }
5052             return this;
5053         };
5054
5055     function byClassName(c, a, v){
5056         if(!v){
5057             return c;
5058         }
5059         var r = [], ri = -1, cn;
5060         for(var i = 0, ci; ci = c[i]; i++){
5061             if((' '+ci.className+' ').indexOf(v) != -1){
5062                 r[++ri] = ci;
5063             }
5064         }
5065         return r;
5066     };
5067
5068     function attrValue(n, attr){
5069         if(!n.tagName && typeof n.length != "undefined"){
5070             n = n[0];
5071         }
5072         if(!n){
5073             return null;
5074         }
5075         if(attr == "for"){
5076             return n.htmlFor;
5077         }
5078         if(attr == "class" || attr == "className"){
5079             return n.className;
5080         }
5081         return n.getAttribute(attr) || n[attr];
5082
5083     };
5084
5085     function getNodes(ns, mode, tagName){
5086         var result = [], ri = -1, cs;
5087         if(!ns){
5088             return result;
5089         }
5090         tagName = tagName || "*";
5091         if(typeof ns.getElementsByTagName != "undefined"){
5092             ns = [ns];
5093         }
5094         if(!mode){
5095             for(var i = 0, ni; ni = ns[i]; i++){
5096                 cs = ni.getElementsByTagName(tagName);
5097                 for(var j = 0, ci; ci = cs[j]; j++){
5098                     result[++ri] = ci;
5099                 }
5100             }
5101         }else if(mode == "/" || mode == ">"){
5102             var utag = tagName.toUpperCase();
5103             for(var i = 0, ni, cn; ni = ns[i]; i++){
5104                 cn = ni.children || ni.childNodes;
5105                 for(var j = 0, cj; cj = cn[j]; j++){
5106                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
5107                         result[++ri] = cj;
5108                     }
5109                 }
5110             }
5111         }else if(mode == "+"){
5112             var utag = tagName.toUpperCase();
5113             for(var i = 0, n; n = ns[i]; i++){
5114                 while((n = n.nextSibling) && n.nodeType != 1);
5115                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
5116                     result[++ri] = n;
5117                 }
5118             }
5119         }else if(mode == "~"){
5120             for(var i = 0, n; n = ns[i]; i++){
5121                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
5122                 if(n){
5123                     result[++ri] = n;
5124                 }
5125             }
5126         }
5127         return result;
5128     };
5129
5130     function concat(a, b){
5131         if(b.slice){
5132             return a.concat(b);
5133         }
5134         for(var i = 0, l = b.length; i < l; i++){
5135             a[a.length] = b[i];
5136         }
5137         return a;
5138     }
5139
5140     function byTag(cs, tagName){
5141         if(cs.tagName || cs == document){
5142             cs = [cs];
5143         }
5144         if(!tagName){
5145             return cs;
5146         }
5147         var r = [], ri = -1;
5148         tagName = tagName.toLowerCase();
5149         for(var i = 0, ci; ci = cs[i]; i++){
5150             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
5151                 r[++ri] = ci;
5152             }
5153         }
5154         return r;
5155     };
5156
5157     function byId(cs, attr, id){
5158         if(cs.tagName || cs == document){
5159             cs = [cs];
5160         }
5161         if(!id){
5162             return cs;
5163         }
5164         var r = [], ri = -1;
5165         for(var i = 0,ci; ci = cs[i]; i++){
5166             if(ci && ci.id == id){
5167                 r[++ri] = ci;
5168                 return r;
5169             }
5170         }
5171         return r;
5172     };
5173
5174     function byAttribute(cs, attr, value, op, custom){
5175         var r = [], ri = -1, st = custom=="{";
5176         var f = Roo.DomQuery.operators[op];
5177         for(var i = 0, ci; ci = cs[i]; i++){
5178             var a;
5179             if(st){
5180                 a = Roo.DomQuery.getStyle(ci, attr);
5181             }
5182             else if(attr == "class" || attr == "className"){
5183                 a = ci.className;
5184             }else if(attr == "for"){
5185                 a = ci.htmlFor;
5186             }else if(attr == "href"){
5187                 a = ci.getAttribute("href", 2);
5188             }else{
5189                 a = ci.getAttribute(attr);
5190             }
5191             if((f && f(a, value)) || (!f && a)){
5192                 r[++ri] = ci;
5193             }
5194         }
5195         return r;
5196     };
5197
5198     function byPseudo(cs, name, value){
5199         return Roo.DomQuery.pseudos[name](cs, value);
5200     };
5201
5202     // This is for IE MSXML which does not support expandos.
5203     // IE runs the same speed using setAttribute, however FF slows way down
5204     // and Safari completely fails so they need to continue to use expandos.
5205     var isIE = window.ActiveXObject ? true : false;
5206
5207     // this eval is stop the compressor from
5208     // renaming the variable to something shorter
5209     
5210     /** eval:var:batch */
5211     var batch = 30803; 
5212
5213     var key = 30803;
5214
5215     function nodupIEXml(cs){
5216         var d = ++key;
5217         cs[0].setAttribute("_nodup", d);
5218         var r = [cs[0]];
5219         for(var i = 1, len = cs.length; i < len; i++){
5220             var c = cs[i];
5221             if(!c.getAttribute("_nodup") != d){
5222                 c.setAttribute("_nodup", d);
5223                 r[r.length] = c;
5224             }
5225         }
5226         for(var i = 0, len = cs.length; i < len; i++){
5227             cs[i].removeAttribute("_nodup");
5228         }
5229         return r;
5230     }
5231
5232     function nodup(cs){
5233         if(!cs){
5234             return [];
5235         }
5236         var len = cs.length, c, i, r = cs, cj, ri = -1;
5237         if(!len || typeof cs.nodeType != "undefined" || len == 1){
5238             return cs;
5239         }
5240         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
5241             return nodupIEXml(cs);
5242         }
5243         var d = ++key;
5244         cs[0]._nodup = d;
5245         for(i = 1; c = cs[i]; i++){
5246             if(c._nodup != d){
5247                 c._nodup = d;
5248             }else{
5249                 r = [];
5250                 for(var j = 0; j < i; j++){
5251                     r[++ri] = cs[j];
5252                 }
5253                 for(j = i+1; cj = cs[j]; j++){
5254                     if(cj._nodup != d){
5255                         cj._nodup = d;
5256                         r[++ri] = cj;
5257                     }
5258                 }
5259                 return r;
5260             }
5261         }
5262         return r;
5263     }
5264
5265     function quickDiffIEXml(c1, c2){
5266         var d = ++key;
5267         for(var i = 0, len = c1.length; i < len; i++){
5268             c1[i].setAttribute("_qdiff", d);
5269         }
5270         var r = [];
5271         for(var i = 0, len = c2.length; i < len; i++){
5272             if(c2[i].getAttribute("_qdiff") != d){
5273                 r[r.length] = c2[i];
5274             }
5275         }
5276         for(var i = 0, len = c1.length; i < len; i++){
5277            c1[i].removeAttribute("_qdiff");
5278         }
5279         return r;
5280     }
5281
5282     function quickDiff(c1, c2){
5283         var len1 = c1.length;
5284         if(!len1){
5285             return c2;
5286         }
5287         if(isIE && c1[0].selectSingleNode){
5288             return quickDiffIEXml(c1, c2);
5289         }
5290         var d = ++key;
5291         for(var i = 0; i < len1; i++){
5292             c1[i]._qdiff = d;
5293         }
5294         var r = [];
5295         for(var i = 0, len = c2.length; i < len; i++){
5296             if(c2[i]._qdiff != d){
5297                 r[r.length] = c2[i];
5298             }
5299         }
5300         return r;
5301     }
5302
5303     function quickId(ns, mode, root, id){
5304         if(ns == root){
5305            var d = root.ownerDocument || root;
5306            return d.getElementById(id);
5307         }
5308         ns = getNodes(ns, mode, "*");
5309         return byId(ns, null, id);
5310     }
5311
5312     return {
5313         getStyle : function(el, name){
5314             return Roo.fly(el).getStyle(name);
5315         },
5316         /**
5317          * Compiles a selector/xpath query into a reusable function. The returned function
5318          * takes one parameter "root" (optional), which is the context node from where the query should start.
5319          * @param {String} selector The selector/xpath query
5320          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
5321          * @return {Function}
5322          */
5323         compile : function(path, type){
5324             type = type || "select";
5325             
5326             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
5327             var q = path, mode, lq;
5328             var tk = Roo.DomQuery.matchers;
5329             var tklen = tk.length;
5330             var mm;
5331
5332             // accept leading mode switch
5333             var lmode = q.match(modeRe);
5334             if(lmode && lmode[1]){
5335                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
5336                 q = q.replace(lmode[1], "");
5337             }
5338             // strip leading slashes
5339             while(path.substr(0, 1)=="/"){
5340                 path = path.substr(1);
5341             }
5342
5343             while(q && lq != q){
5344                 lq = q;
5345                 var tm = q.match(tagTokenRe);
5346                 if(type == "select"){
5347                     if(tm){
5348                         if(tm[1] == "#"){
5349                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
5350                         }else{
5351                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
5352                         }
5353                         q = q.replace(tm[0], "");
5354                     }else if(q.substr(0, 1) != '@'){
5355                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
5356                     }
5357                 }else{
5358                     if(tm){
5359                         if(tm[1] == "#"){
5360                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
5361                         }else{
5362                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
5363                         }
5364                         q = q.replace(tm[0], "");
5365                     }
5366                 }
5367                 while(!(mm = q.match(modeRe))){
5368                     var matched = false;
5369                     for(var j = 0; j < tklen; j++){
5370                         var t = tk[j];
5371                         var m = q.match(t.re);
5372                         if(m){
5373                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
5374                                                     return m[i];
5375                                                 });
5376                             q = q.replace(m[0], "");
5377                             matched = true;
5378                             break;
5379                         }
5380                     }
5381                     // prevent infinite loop on bad selector
5382                     if(!matched){
5383                         throw 'Error parsing selector, parsing failed at "' + q + '"';
5384                     }
5385                 }
5386                 if(mm[1]){
5387                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
5388                     q = q.replace(mm[1], "");
5389                 }
5390             }
5391             fn[fn.length] = "return nodup(n);\n}";
5392             
5393              /** 
5394               * list of variables that need from compression as they are used by eval.
5395              *  eval:var:batch 
5396              *  eval:var:nodup
5397              *  eval:var:byTag
5398              *  eval:var:ById
5399              *  eval:var:getNodes
5400              *  eval:var:quickId
5401              *  eval:var:mode
5402              *  eval:var:root
5403              *  eval:var:n
5404              *  eval:var:byClassName
5405              *  eval:var:byPseudo
5406              *  eval:var:byAttribute
5407              *  eval:var:attrValue
5408              * 
5409              **/ 
5410             eval(fn.join(""));
5411             return f;
5412         },
5413
5414         /**
5415          * Selects a group of elements.
5416          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
5417          * @param {Node} root (optional) The start of the query (defaults to document).
5418          * @return {Array}
5419          */
5420         select : function(path, root, type){
5421             if(!root || root == document){
5422                 root = document;
5423             }
5424             if(typeof root == "string"){
5425                 root = document.getElementById(root);
5426             }
5427             var paths = path.split(",");
5428             var results = [];
5429             for(var i = 0, len = paths.length; i < len; i++){
5430                 var p = paths[i].replace(trimRe, "");
5431                 if(!cache[p]){
5432                     cache[p] = Roo.DomQuery.compile(p);
5433                     if(!cache[p]){
5434                         throw p + " is not a valid selector";
5435                     }
5436                 }
5437                 var result = cache[p](root);
5438                 if(result && result != document){
5439                     results = results.concat(result);
5440                 }
5441             }
5442             if(paths.length > 1){
5443                 return nodup(results);
5444             }
5445             return results;
5446         },
5447
5448         /**
5449          * Selects a single element.
5450          * @param {String} selector The selector/xpath query
5451          * @param {Node} root (optional) The start of the query (defaults to document).
5452          * @return {Element}
5453          */
5454         selectNode : function(path, root){
5455             return Roo.DomQuery.select(path, root)[0];
5456         },
5457
5458         /**
5459          * Selects the value of a node, optionally replacing null with the defaultValue.
5460          * @param {String} selector The selector/xpath query
5461          * @param {Node} root (optional) The start of the query (defaults to document).
5462          * @param {String} defaultValue
5463          */
5464         selectValue : function(path, root, defaultValue){
5465             path = path.replace(trimRe, "");
5466             if(!valueCache[path]){
5467                 valueCache[path] = Roo.DomQuery.compile(path, "select");
5468             }
5469             var n = valueCache[path](root);
5470             n = n[0] ? n[0] : n;
5471             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
5472             return ((v === null||v === undefined||v==='') ? defaultValue : v);
5473         },
5474
5475         /**
5476          * Selects the value of a node, parsing integers and floats.
5477          * @param {String} selector The selector/xpath query
5478          * @param {Node} root (optional) The start of the query (defaults to document).
5479          * @param {Number} defaultValue
5480          * @return {Number}
5481          */
5482         selectNumber : function(path, root, defaultValue){
5483             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
5484             return parseFloat(v);
5485         },
5486
5487         /**
5488          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
5489          * @param {String/HTMLElement/Array} el An element id, element or array of elements
5490          * @param {String} selector The simple selector to test
5491          * @return {Boolean}
5492          */
5493         is : function(el, ss){
5494             if(typeof el == "string"){
5495                 el = document.getElementById(el);
5496             }
5497             var isArray = (el instanceof Array);
5498             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
5499             return isArray ? (result.length == el.length) : (result.length > 0);
5500         },
5501
5502         /**
5503          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
5504          * @param {Array} el An array of elements to filter
5505          * @param {String} selector The simple selector to test
5506          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
5507          * the selector instead of the ones that match
5508          * @return {Array}
5509          */
5510         filter : function(els, ss, nonMatches){
5511             ss = ss.replace(trimRe, "");
5512             if(!simpleCache[ss]){
5513                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
5514             }
5515             var result = simpleCache[ss](els);
5516             return nonMatches ? quickDiff(result, els) : result;
5517         },
5518
5519         /**
5520          * Collection of matching regular expressions and code snippets.
5521          */
5522         matchers : [{
5523                 re: /^\.([\w-]+)/,
5524                 select: 'n = byClassName(n, null, " {1} ");'
5525             }, {
5526                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
5527                 select: 'n = byPseudo(n, "{1}", "{2}");'
5528             },{
5529                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
5530                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
5531             }, {
5532                 re: /^#([\w-]+)/,
5533                 select: 'n = byId(n, null, "{1}");'
5534             },{
5535                 re: /^@([\w-]+)/,
5536                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
5537             }
5538         ],
5539
5540         /**
5541          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
5542          * 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;.
5543          */
5544         operators : {
5545             "=" : function(a, v){
5546                 return a == v;
5547             },
5548             "!=" : function(a, v){
5549                 return a != v;
5550             },
5551             "^=" : function(a, v){
5552                 return a && a.substr(0, v.length) == v;
5553             },
5554             "$=" : function(a, v){
5555                 return a && a.substr(a.length-v.length) == v;
5556             },
5557             "*=" : function(a, v){
5558                 return a && a.indexOf(v) !== -1;
5559             },
5560             "%=" : function(a, v){
5561                 return (a % v) == 0;
5562             },
5563             "|=" : function(a, v){
5564                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
5565             },
5566             "~=" : function(a, v){
5567                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
5568             }
5569         },
5570
5571         /**
5572          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
5573          * and the argument (if any) supplied in the selector.
5574          */
5575         pseudos : {
5576             "first-child" : function(c){
5577                 var r = [], ri = -1, n;
5578                 for(var i = 0, ci; ci = n = c[i]; i++){
5579                     while((n = n.previousSibling) && n.nodeType != 1);
5580                     if(!n){
5581                         r[++ri] = ci;
5582                     }
5583                 }
5584                 return r;
5585             },
5586
5587             "last-child" : function(c){
5588                 var r = [], ri = -1, n;
5589                 for(var i = 0, ci; ci = n = c[i]; i++){
5590                     while((n = n.nextSibling) && n.nodeType != 1);
5591                     if(!n){
5592                         r[++ri] = ci;
5593                     }
5594                 }
5595                 return r;
5596             },
5597
5598             "nth-child" : function(c, a) {
5599                 var r = [], ri = -1;
5600                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
5601                 var f = (m[1] || 1) - 0, l = m[2] - 0;
5602                 for(var i = 0, n; n = c[i]; i++){
5603                     var pn = n.parentNode;
5604                     if (batch != pn._batch) {
5605                         var j = 0;
5606                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
5607                             if(cn.nodeType == 1){
5608                                cn.nodeIndex = ++j;
5609                             }
5610                         }
5611                         pn._batch = batch;
5612                     }
5613                     if (f == 1) {
5614                         if (l == 0 || n.nodeIndex == l){
5615                             r[++ri] = n;
5616                         }
5617                     } else if ((n.nodeIndex + l) % f == 0){
5618                         r[++ri] = n;
5619                     }
5620                 }
5621
5622                 return r;
5623             },
5624
5625             "only-child" : function(c){
5626                 var r = [], ri = -1;;
5627                 for(var i = 0, ci; ci = c[i]; i++){
5628                     if(!prev(ci) && !next(ci)){
5629                         r[++ri] = ci;
5630                     }
5631                 }
5632                 return r;
5633             },
5634
5635             "empty" : function(c){
5636                 var r = [], ri = -1;
5637                 for(var i = 0, ci; ci = c[i]; i++){
5638                     var cns = ci.childNodes, j = 0, cn, empty = true;
5639                     while(cn = cns[j]){
5640                         ++j;
5641                         if(cn.nodeType == 1 || cn.nodeType == 3){
5642                             empty = false;
5643                             break;
5644                         }
5645                     }
5646                     if(empty){
5647                         r[++ri] = ci;
5648                     }
5649                 }
5650                 return r;
5651             },
5652
5653             "contains" : function(c, v){
5654                 var r = [], ri = -1;
5655                 for(var i = 0, ci; ci = c[i]; i++){
5656                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
5657                         r[++ri] = ci;
5658                     }
5659                 }
5660                 return r;
5661             },
5662
5663             "nodeValue" : function(c, v){
5664                 var r = [], ri = -1;
5665                 for(var i = 0, ci; ci = c[i]; i++){
5666                     if(ci.firstChild && ci.firstChild.nodeValue == v){
5667                         r[++ri] = ci;
5668                     }
5669                 }
5670                 return r;
5671             },
5672
5673             "checked" : function(c){
5674                 var r = [], ri = -1;
5675                 for(var i = 0, ci; ci = c[i]; i++){
5676                     if(ci.checked == true){
5677                         r[++ri] = ci;
5678                     }
5679                 }
5680                 return r;
5681             },
5682
5683             "not" : function(c, ss){
5684                 return Roo.DomQuery.filter(c, ss, true);
5685             },
5686
5687             "odd" : function(c){
5688                 return this["nth-child"](c, "odd");
5689             },
5690
5691             "even" : function(c){
5692                 return this["nth-child"](c, "even");
5693             },
5694
5695             "nth" : function(c, a){
5696                 return c[a-1] || [];
5697             },
5698
5699             "first" : function(c){
5700                 return c[0] || [];
5701             },
5702
5703             "last" : function(c){
5704                 return c[c.length-1] || [];
5705             },
5706
5707             "has" : function(c, ss){
5708                 var s = Roo.DomQuery.select;
5709                 var r = [], ri = -1;
5710                 for(var i = 0, ci; ci = c[i]; i++){
5711                     if(s(ss, ci).length > 0){
5712                         r[++ri] = ci;
5713                     }
5714                 }
5715                 return r;
5716             },
5717
5718             "next" : function(c, ss){
5719                 var is = Roo.DomQuery.is;
5720                 var r = [], ri = -1;
5721                 for(var i = 0, ci; ci = c[i]; i++){
5722                     var n = next(ci);
5723                     if(n && is(n, ss)){
5724                         r[++ri] = ci;
5725                     }
5726                 }
5727                 return r;
5728             },
5729
5730             "prev" : function(c, ss){
5731                 var is = Roo.DomQuery.is;
5732                 var r = [], ri = -1;
5733                 for(var i = 0, ci; ci = c[i]; i++){
5734                     var n = prev(ci);
5735                     if(n && is(n, ss)){
5736                         r[++ri] = ci;
5737                     }
5738                 }
5739                 return r;
5740             }
5741         }
5742     };
5743 }();
5744
5745 /**
5746  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
5747  * @param {String} path The selector/xpath query
5748  * @param {Node} root (optional) The start of the query (defaults to document).
5749  * @return {Array}
5750  * @member Roo
5751  * @method query
5752  */
5753 Roo.query = Roo.DomQuery.select;
5754 /*
5755  * Based on:
5756  * Ext JS Library 1.1.1
5757  * Copyright(c) 2006-2007, Ext JS, LLC.
5758  *
5759  * Originally Released Under LGPL - original licence link has changed is not relivant.
5760  *
5761  * Fork - LGPL
5762  * <script type="text/javascript">
5763  */
5764
5765 /**
5766  * @class Roo.util.Observable
5767  * Base class that provides a common interface for publishing events. Subclasses are expected to
5768  * to have a property "events" with all the events defined.<br>
5769  * For example:
5770  * <pre><code>
5771  Employee = function(name){
5772     this.name = name;
5773     this.addEvents({
5774         "fired" : true,
5775         "quit" : true
5776     });
5777  }
5778  Roo.extend(Employee, Roo.util.Observable);
5779 </code></pre>
5780  * @param {Object} config properties to use (incuding events / listeners)
5781  */
5782
5783 Roo.util.Observable = function(cfg){
5784     
5785     cfg = cfg|| {};
5786     this.addEvents(cfg.events || {});
5787     if (cfg.events) {
5788         delete cfg.events; // make sure
5789     }
5790      
5791     Roo.apply(this, cfg);
5792     
5793     if(this.listeners){
5794         this.on(this.listeners);
5795         delete this.listeners;
5796     }
5797 };
5798 Roo.util.Observable.prototype = {
5799     /** 
5800  * @cfg {Object} listeners  list of events and functions to call for this object, 
5801  * For example :
5802  * <pre><code>
5803     listeners :  { 
5804        'click' : function(e) {
5805            ..... 
5806         } ,
5807         .... 
5808     } 
5809   </code></pre>
5810  */
5811     
5812     
5813     /**
5814      * Fires the specified event with the passed parameters (minus the event name).
5815      * @param {String} eventName
5816      * @param {Object...} args Variable number of parameters are passed to handlers
5817      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
5818      */
5819     fireEvent : function(){
5820         var ce = this.events[arguments[0].toLowerCase()];
5821         if(typeof ce == "object"){
5822             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
5823         }else{
5824             return true;
5825         }
5826     },
5827
5828     // private
5829     filterOptRe : /^(?:scope|delay|buffer|single)$/,
5830
5831     /**
5832      * Appends an event handler to this component
5833      * @param {String}   eventName The type of event to listen for
5834      * @param {Function} handler The method the event invokes
5835      * @param {Object}   scope (optional) The scope in which to execute the handler
5836      * function. The handler function's "this" context.
5837      * @param {Object}   options (optional) An object containing handler configuration
5838      * properties. This may contain any of the following properties:<ul>
5839      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
5840      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
5841      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
5842      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
5843      * by the specified number of milliseconds. If the event fires again within that time, the original
5844      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
5845      * </ul><br>
5846      * <p>
5847      * <b>Combining Options</b><br>
5848      * Using the options argument, it is possible to combine different types of listeners:<br>
5849      * <br>
5850      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
5851                 <pre><code>
5852                 el.on('click', this.onClick, this, {
5853                         single: true,
5854                 delay: 100,
5855                 forumId: 4
5856                 });
5857                 </code></pre>
5858      * <p>
5859      * <b>Attaching multiple handlers in 1 call</b><br>
5860      * The method also allows for a single argument to be passed which is a config object containing properties
5861      * which specify multiple handlers.
5862      * <pre><code>
5863                 el.on({
5864                         'click': {
5865                         fn: this.onClick,
5866                         scope: this,
5867                         delay: 100
5868                 }, 
5869                 'mouseover': {
5870                         fn: this.onMouseOver,
5871                         scope: this
5872                 },
5873                 'mouseout': {
5874                         fn: this.onMouseOut,
5875                         scope: this
5876                 }
5877                 });
5878                 </code></pre>
5879      * <p>
5880      * Or a shorthand syntax which passes the same scope object to all handlers:
5881         <pre><code>
5882                 el.on({
5883                         'click': this.onClick,
5884                 'mouseover': this.onMouseOver,
5885                 'mouseout': this.onMouseOut,
5886                 scope: this
5887                 });
5888                 </code></pre>
5889      */
5890     addListener : function(eventName, fn, scope, o){
5891         if(typeof eventName == "object"){
5892             o = eventName;
5893             for(var e in o){
5894                 if(this.filterOptRe.test(e)){
5895                     continue;
5896                 }
5897                 if(typeof o[e] == "function"){
5898                     // shared options
5899                     this.addListener(e, o[e], o.scope,  o);
5900                 }else{
5901                     // individual options
5902                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
5903                 }
5904             }
5905             return;
5906         }
5907         o = (!o || typeof o == "boolean") ? {} : o;
5908         eventName = eventName.toLowerCase();
5909         var ce = this.events[eventName] || true;
5910         if(typeof ce == "boolean"){
5911             ce = new Roo.util.Event(this, eventName);
5912             this.events[eventName] = ce;
5913         }
5914         ce.addListener(fn, scope, o);
5915     },
5916
5917     /**
5918      * Removes a listener
5919      * @param {String}   eventName     The type of event to listen for
5920      * @param {Function} handler        The handler to remove
5921      * @param {Object}   scope  (optional) The scope (this object) for the handler
5922      */
5923     removeListener : function(eventName, fn, scope){
5924         var ce = this.events[eventName.toLowerCase()];
5925         if(typeof ce == "object"){
5926             ce.removeListener(fn, scope);
5927         }
5928     },
5929
5930     /**
5931      * Removes all listeners for this object
5932      */
5933     purgeListeners : function(){
5934         for(var evt in this.events){
5935             if(typeof this.events[evt] == "object"){
5936                  this.events[evt].clearListeners();
5937             }
5938         }
5939     },
5940
5941     relayEvents : function(o, events){
5942         var createHandler = function(ename){
5943             return function(){
5944                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
5945             };
5946         };
5947         for(var i = 0, len = events.length; i < len; i++){
5948             var ename = events[i];
5949             if(!this.events[ename]){ this.events[ename] = true; };
5950             o.on(ename, createHandler(ename), this);
5951         }
5952     },
5953
5954     /**
5955      * Used to define events on this Observable
5956      * @param {Object} object The object with the events defined
5957      */
5958     addEvents : function(o){
5959         if(!this.events){
5960             this.events = {};
5961         }
5962         Roo.applyIf(this.events, o);
5963     },
5964
5965     /**
5966      * Checks to see if this object has any listeners for a specified event
5967      * @param {String} eventName The name of the event to check for
5968      * @return {Boolean} True if the event is being listened for, else false
5969      */
5970     hasListener : function(eventName){
5971         var e = this.events[eventName];
5972         return typeof e == "object" && e.listeners.length > 0;
5973     }
5974 };
5975 /**
5976  * Appends an event handler to this element (shorthand for addListener)
5977  * @param {String}   eventName     The type of event to listen for
5978  * @param {Function} handler        The method the event invokes
5979  * @param {Object}   scope (optional) The scope in which to execute the handler
5980  * function. The handler function's "this" context.
5981  * @param {Object}   options  (optional)
5982  * @method
5983  */
5984 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
5985 /**
5986  * Removes a listener (shorthand for removeListener)
5987  * @param {String}   eventName     The type of event to listen for
5988  * @param {Function} handler        The handler to remove
5989  * @param {Object}   scope  (optional) The scope (this object) for the handler
5990  * @method
5991  */
5992 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
5993
5994 /**
5995  * Starts capture on the specified Observable. All events will be passed
5996  * to the supplied function with the event name + standard signature of the event
5997  * <b>before</b> the event is fired. If the supplied function returns false,
5998  * the event will not fire.
5999  * @param {Observable} o The Observable to capture
6000  * @param {Function} fn The function to call
6001  * @param {Object} scope (optional) The scope (this object) for the fn
6002  * @static
6003  */
6004 Roo.util.Observable.capture = function(o, fn, scope){
6005     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
6006 };
6007
6008 /**
6009  * Removes <b>all</b> added captures from the Observable.
6010  * @param {Observable} o The Observable to release
6011  * @static
6012  */
6013 Roo.util.Observable.releaseCapture = function(o){
6014     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
6015 };
6016
6017 (function(){
6018
6019     var createBuffered = function(h, o, scope){
6020         var task = new Roo.util.DelayedTask();
6021         return function(){
6022             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
6023         };
6024     };
6025
6026     var createSingle = function(h, e, fn, scope){
6027         return function(){
6028             e.removeListener(fn, scope);
6029             return h.apply(scope, arguments);
6030         };
6031     };
6032
6033     var createDelayed = function(h, o, scope){
6034         return function(){
6035             var args = Array.prototype.slice.call(arguments, 0);
6036             setTimeout(function(){
6037                 h.apply(scope, args);
6038             }, o.delay || 10);
6039         };
6040     };
6041
6042     Roo.util.Event = function(obj, name){
6043         this.name = name;
6044         this.obj = obj;
6045         this.listeners = [];
6046     };
6047
6048     Roo.util.Event.prototype = {
6049         addListener : function(fn, scope, options){
6050             var o = options || {};
6051             scope = scope || this.obj;
6052             if(!this.isListening(fn, scope)){
6053                 var l = {fn: fn, scope: scope, options: o};
6054                 var h = fn;
6055                 if(o.delay){
6056                     h = createDelayed(h, o, scope);
6057                 }
6058                 if(o.single){
6059                     h = createSingle(h, this, fn, scope);
6060                 }
6061                 if(o.buffer){
6062                     h = createBuffered(h, o, scope);
6063                 }
6064                 l.fireFn = h;
6065                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
6066                     this.listeners.push(l);
6067                 }else{
6068                     this.listeners = this.listeners.slice(0);
6069                     this.listeners.push(l);
6070                 }
6071             }
6072         },
6073
6074         findListener : function(fn, scope){
6075             scope = scope || this.obj;
6076             var ls = this.listeners;
6077             for(var i = 0, len = ls.length; i < len; i++){
6078                 var l = ls[i];
6079                 if(l.fn == fn && l.scope == scope){
6080                     return i;
6081                 }
6082             }
6083             return -1;
6084         },
6085
6086         isListening : function(fn, scope){
6087             return this.findListener(fn, scope) != -1;
6088         },
6089
6090         removeListener : function(fn, scope){
6091             var index;
6092             if((index = this.findListener(fn, scope)) != -1){
6093                 if(!this.firing){
6094                     this.listeners.splice(index, 1);
6095                 }else{
6096                     this.listeners = this.listeners.slice(0);
6097                     this.listeners.splice(index, 1);
6098                 }
6099                 return true;
6100             }
6101             return false;
6102         },
6103
6104         clearListeners : function(){
6105             this.listeners = [];
6106         },
6107
6108         fire : function(){
6109             var ls = this.listeners, scope, len = ls.length;
6110             if(len > 0){
6111                 this.firing = true;
6112                 var args = Array.prototype.slice.call(arguments, 0);                
6113                 for(var i = 0; i < len; i++){
6114                     var l = ls[i];
6115                     if(l.fireFn.apply(l.scope||this.obj||window, args) === false){
6116                         this.firing = false;
6117                         return false;
6118                     }
6119                 }
6120                 this.firing = false;
6121             }
6122             return true;
6123         }
6124     };
6125 })();/*
6126  * RooJS Library 
6127  * Copyright(c) 2007-2017, Roo J Solutions Ltd
6128  *
6129  * Licence LGPL 
6130  *
6131  */
6132  
6133 /**
6134  * @class Roo.Document
6135  * @extends Roo.util.Observable
6136  * This is a convience class to wrap up the main document loading code.. , rather than adding Roo.onReady(......)
6137  * 
6138  * @param {Object} config the methods and properties of the 'base' class for the application.
6139  * 
6140  *  Generic Page handler - implement this to start your app..
6141  * 
6142  * eg.
6143  *  MyProject = new Roo.Document({
6144         events : {
6145             'load' : true // your events..
6146         },
6147         listeners : {
6148             'ready' : function() {
6149                 // fired on Roo.onReady()
6150             }
6151         }
6152  * 
6153  */
6154 Roo.Document = function(cfg) {
6155      
6156     this.addEvents({ 
6157         'ready' : true
6158     });
6159     Roo.util.Observable.call(this,cfg);
6160     
6161     var _this = this;
6162     
6163     Roo.onReady(function() {
6164         _this.fireEvent('ready');
6165     },null,false);
6166     
6167     
6168 }
6169
6170 Roo.extend(Roo.Document, Roo.util.Observable, {});/*
6171  * Based on:
6172  * Ext JS Library 1.1.1
6173  * Copyright(c) 2006-2007, Ext JS, LLC.
6174  *
6175  * Originally Released Under LGPL - original licence link has changed is not relivant.
6176  *
6177  * Fork - LGPL
6178  * <script type="text/javascript">
6179  */
6180
6181 /**
6182  * @class Roo.EventManager
6183  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
6184  * several useful events directly.
6185  * See {@link Roo.EventObject} for more details on normalized event objects.
6186  * @singleton
6187  */
6188 Roo.EventManager = function(){
6189     var docReadyEvent, docReadyProcId, docReadyState = false;
6190     var resizeEvent, resizeTask, textEvent, textSize;
6191     var E = Roo.lib.Event;
6192     var D = Roo.lib.Dom;
6193
6194     
6195     
6196
6197     var fireDocReady = function(){
6198         if(!docReadyState){
6199             docReadyState = true;
6200             Roo.isReady = true;
6201             if(docReadyProcId){
6202                 clearInterval(docReadyProcId);
6203             }
6204             if(Roo.isGecko || Roo.isOpera) {
6205                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
6206             }
6207             if(Roo.isIE){
6208                 var defer = document.getElementById("ie-deferred-loader");
6209                 if(defer){
6210                     defer.onreadystatechange = null;
6211                     defer.parentNode.removeChild(defer);
6212                 }
6213             }
6214             if(docReadyEvent){
6215                 docReadyEvent.fire();
6216                 docReadyEvent.clearListeners();
6217             }
6218         }
6219     };
6220     
6221     var initDocReady = function(){
6222         docReadyEvent = new Roo.util.Event();
6223         if(Roo.isGecko || Roo.isOpera) {
6224             document.addEventListener("DOMContentLoaded", fireDocReady, false);
6225         }else if(Roo.isIE){
6226             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
6227             var defer = document.getElementById("ie-deferred-loader");
6228             defer.onreadystatechange = function(){
6229                 if(this.readyState == "complete"){
6230                     fireDocReady();
6231                 }
6232             };
6233         }else if(Roo.isSafari){ 
6234             docReadyProcId = setInterval(function(){
6235                 var rs = document.readyState;
6236                 if(rs == "complete") {
6237                     fireDocReady();     
6238                  }
6239             }, 10);
6240         }
6241         // no matter what, make sure it fires on load
6242         E.on(window, "load", fireDocReady);
6243     };
6244
6245     var createBuffered = function(h, o){
6246         var task = new Roo.util.DelayedTask(h);
6247         return function(e){
6248             // create new event object impl so new events don't wipe out properties
6249             e = new Roo.EventObjectImpl(e);
6250             task.delay(o.buffer, h, null, [e]);
6251         };
6252     };
6253
6254     var createSingle = function(h, el, ename, fn){
6255         return function(e){
6256             Roo.EventManager.removeListener(el, ename, fn);
6257             h(e);
6258         };
6259     };
6260
6261     var createDelayed = function(h, o){
6262         return function(e){
6263             // create new event object impl so new events don't wipe out properties
6264             e = new Roo.EventObjectImpl(e);
6265             setTimeout(function(){
6266                 h(e);
6267             }, o.delay || 10);
6268         };
6269     };
6270     var transitionEndVal = false;
6271     
6272     var transitionEnd = function()
6273     {
6274         if (transitionEndVal) {
6275             return transitionEndVal;
6276         }
6277         var el = document.createElement('div');
6278
6279         var transEndEventNames = {
6280             WebkitTransition : 'webkitTransitionEnd',
6281             MozTransition    : 'transitionend',
6282             OTransition      : 'oTransitionEnd otransitionend',
6283             transition       : 'transitionend'
6284         };
6285     
6286         for (var name in transEndEventNames) {
6287             if (el.style[name] !== undefined) {
6288                 transitionEndVal = transEndEventNames[name];
6289                 return  transitionEndVal ;
6290             }
6291         }
6292     }
6293     
6294
6295     var listen = function(element, ename, opt, fn, scope){
6296         var o = (!opt || typeof opt == "boolean") ? {} : opt;
6297         fn = fn || o.fn; scope = scope || o.scope;
6298         var el = Roo.getDom(element);
6299         
6300         
6301         if(!el){
6302             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
6303         }
6304         
6305         if (ename == 'transitionend') {
6306             ename = transitionEnd();
6307         }
6308         var h = function(e){
6309             e = Roo.EventObject.setEvent(e);
6310             var t;
6311             if(o.delegate){
6312                 t = e.getTarget(o.delegate, el);
6313                 if(!t){
6314                     return;
6315                 }
6316             }else{
6317                 t = e.target;
6318             }
6319             if(o.stopEvent === true){
6320                 e.stopEvent();
6321             }
6322             if(o.preventDefault === true){
6323                e.preventDefault();
6324             }
6325             if(o.stopPropagation === true){
6326                 e.stopPropagation();
6327             }
6328
6329             if(o.normalized === false){
6330                 e = e.browserEvent;
6331             }
6332
6333             fn.call(scope || el, e, t, o);
6334         };
6335         if(o.delay){
6336             h = createDelayed(h, o);
6337         }
6338         if(o.single){
6339             h = createSingle(h, el, ename, fn);
6340         }
6341         if(o.buffer){
6342             h = createBuffered(h, o);
6343         }
6344         
6345         fn._handlers = fn._handlers || [];
6346         
6347         
6348         fn._handlers.push([Roo.id(el), ename, h]);
6349         
6350         
6351          
6352         E.on(el, ename, h);
6353         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
6354             el.addEventListener("DOMMouseScroll", h, false);
6355             E.on(window, 'unload', function(){
6356                 el.removeEventListener("DOMMouseScroll", h, false);
6357             });
6358         }
6359         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6360             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
6361         }
6362         return h;
6363     };
6364
6365     var stopListening = function(el, ename, fn){
6366         var id = Roo.id(el), hds = fn._handlers, hd = fn;
6367         if(hds){
6368             for(var i = 0, len = hds.length; i < len; i++){
6369                 var h = hds[i];
6370                 if(h[0] == id && h[1] == ename){
6371                     hd = h[2];
6372                     hds.splice(i, 1);
6373                     break;
6374                 }
6375             }
6376         }
6377         E.un(el, ename, hd);
6378         el = Roo.getDom(el);
6379         if(ename == "mousewheel" && el.addEventListener){
6380             el.removeEventListener("DOMMouseScroll", hd, false);
6381         }
6382         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6383             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
6384         }
6385     };
6386
6387     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
6388     
6389     var pub = {
6390         
6391         
6392         /** 
6393          * Fix for doc tools
6394          * @scope Roo.EventManager
6395          */
6396         
6397         
6398         /** 
6399          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
6400          * object with a Roo.EventObject
6401          * @param {Function} fn        The method the event invokes
6402          * @param {Object}   scope    An object that becomes the scope of the handler
6403          * @param {boolean}  override If true, the obj passed in becomes
6404          *                             the execution scope of the listener
6405          * @return {Function} The wrapped function
6406          * @deprecated
6407          */
6408         wrap : function(fn, scope, override){
6409             return function(e){
6410                 Roo.EventObject.setEvent(e);
6411                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
6412             };
6413         },
6414         
6415         /**
6416      * Appends an event handler to an element (shorthand for addListener)
6417      * @param {String/HTMLElement}   element        The html element or id to assign the
6418      * @param {String}   eventName The type of event to listen for
6419      * @param {Function} handler The method the event invokes
6420      * @param {Object}   scope (optional) The scope in which to execute the handler
6421      * function. The handler function's "this" context.
6422      * @param {Object}   options (optional) An object containing handler configuration
6423      * properties. This may contain any of the following properties:<ul>
6424      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6425      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6426      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6427      * <li>preventDefault {Boolean} True to prevent the default action</li>
6428      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6429      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6430      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6431      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6432      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6433      * by the specified number of milliseconds. If the event fires again within that time, the original
6434      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6435      * </ul><br>
6436      * <p>
6437      * <b>Combining Options</b><br>
6438      * Using the options argument, it is possible to combine different types of listeners:<br>
6439      * <br>
6440      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6441      * Code:<pre><code>
6442 el.on('click', this.onClick, this, {
6443     single: true,
6444     delay: 100,
6445     stopEvent : true,
6446     forumId: 4
6447 });</code></pre>
6448      * <p>
6449      * <b>Attaching multiple handlers in 1 call</b><br>
6450       * The method also allows for a single argument to be passed which is a config object containing properties
6451      * which specify multiple handlers.
6452      * <p>
6453      * Code:<pre><code>
6454 el.on({
6455     'click' : {
6456         fn: this.onClick
6457         scope: this,
6458         delay: 100
6459     },
6460     'mouseover' : {
6461         fn: this.onMouseOver
6462         scope: this
6463     },
6464     'mouseout' : {
6465         fn: this.onMouseOut
6466         scope: this
6467     }
6468 });</code></pre>
6469      * <p>
6470      * Or a shorthand syntax:<br>
6471      * Code:<pre><code>
6472 el.on({
6473     'click' : this.onClick,
6474     'mouseover' : this.onMouseOver,
6475     'mouseout' : this.onMouseOut
6476     scope: this
6477 });</code></pre>
6478      */
6479         addListener : function(element, eventName, fn, scope, options){
6480             if(typeof eventName == "object"){
6481                 var o = eventName;
6482                 for(var e in o){
6483                     if(propRe.test(e)){
6484                         continue;
6485                     }
6486                     if(typeof o[e] == "function"){
6487                         // shared options
6488                         listen(element, e, o, o[e], o.scope);
6489                     }else{
6490                         // individual options
6491                         listen(element, e, o[e]);
6492                     }
6493                 }
6494                 return;
6495             }
6496             return listen(element, eventName, options, fn, scope);
6497         },
6498         
6499         /**
6500          * Removes an event handler
6501          *
6502          * @param {String/HTMLElement}   element        The id or html element to remove the 
6503          *                             event from
6504          * @param {String}   eventName     The type of event
6505          * @param {Function} fn
6506          * @return {Boolean} True if a listener was actually removed
6507          */
6508         removeListener : function(element, eventName, fn){
6509             return stopListening(element, eventName, fn);
6510         },
6511         
6512         /**
6513          * Fires when the document is ready (before onload and before images are loaded). Can be 
6514          * accessed shorthanded Roo.onReady().
6515          * @param {Function} fn        The method the event invokes
6516          * @param {Object}   scope    An  object that becomes the scope of the handler
6517          * @param {boolean}  options
6518          */
6519         onDocumentReady : function(fn, scope, options){
6520             if(docReadyState){ // if it already fired
6521                 docReadyEvent.addListener(fn, scope, options);
6522                 docReadyEvent.fire();
6523                 docReadyEvent.clearListeners();
6524                 return;
6525             }
6526             if(!docReadyEvent){
6527                 initDocReady();
6528             }
6529             docReadyEvent.addListener(fn, scope, options);
6530         },
6531         
6532         /**
6533          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
6534          * @param {Function} fn        The method the event invokes
6535          * @param {Object}   scope    An object that becomes the scope of the handler
6536          * @param {boolean}  options
6537          */
6538         onWindowResize : function(fn, scope, options){
6539             if(!resizeEvent){
6540                 resizeEvent = new Roo.util.Event();
6541                 resizeTask = new Roo.util.DelayedTask(function(){
6542                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6543                 });
6544                 E.on(window, "resize", function(){
6545                     if(Roo.isIE){
6546                         resizeTask.delay(50);
6547                     }else{
6548                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6549                     }
6550                 });
6551             }
6552             resizeEvent.addListener(fn, scope, options);
6553         },
6554
6555         /**
6556          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
6557          * @param {Function} fn        The method the event invokes
6558          * @param {Object}   scope    An object that becomes the scope of the handler
6559          * @param {boolean}  options
6560          */
6561         onTextResize : function(fn, scope, options){
6562             if(!textEvent){
6563                 textEvent = new Roo.util.Event();
6564                 var textEl = new Roo.Element(document.createElement('div'));
6565                 textEl.dom.className = 'x-text-resize';
6566                 textEl.dom.innerHTML = 'X';
6567                 textEl.appendTo(document.body);
6568                 textSize = textEl.dom.offsetHeight;
6569                 setInterval(function(){
6570                     if(textEl.dom.offsetHeight != textSize){
6571                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
6572                     }
6573                 }, this.textResizeInterval);
6574             }
6575             textEvent.addListener(fn, scope, options);
6576         },
6577
6578         /**
6579          * Removes the passed window resize listener.
6580          * @param {Function} fn        The method the event invokes
6581          * @param {Object}   scope    The scope of handler
6582          */
6583         removeResizeListener : function(fn, scope){
6584             if(resizeEvent){
6585                 resizeEvent.removeListener(fn, scope);
6586             }
6587         },
6588
6589         // private
6590         fireResize : function(){
6591             if(resizeEvent){
6592                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6593             }   
6594         },
6595         /**
6596          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
6597          */
6598         ieDeferSrc : false,
6599         /**
6600          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
6601          */
6602         textResizeInterval : 50
6603     };
6604     
6605     /**
6606      * Fix for doc tools
6607      * @scopeAlias pub=Roo.EventManager
6608      */
6609     
6610      /**
6611      * Appends an event handler to an element (shorthand for addListener)
6612      * @param {String/HTMLElement}   element        The html element or id to assign the
6613      * @param {String}   eventName The type of event to listen for
6614      * @param {Function} handler The method the event invokes
6615      * @param {Object}   scope (optional) The scope in which to execute the handler
6616      * function. The handler function's "this" context.
6617      * @param {Object}   options (optional) An object containing handler configuration
6618      * properties. This may contain any of the following properties:<ul>
6619      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6620      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6621      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6622      * <li>preventDefault {Boolean} True to prevent the default action</li>
6623      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6624      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6625      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6626      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6627      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6628      * by the specified number of milliseconds. If the event fires again within that time, the original
6629      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6630      * </ul><br>
6631      * <p>
6632      * <b>Combining Options</b><br>
6633      * Using the options argument, it is possible to combine different types of listeners:<br>
6634      * <br>
6635      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6636      * Code:<pre><code>
6637 el.on('click', this.onClick, this, {
6638     single: true,
6639     delay: 100,
6640     stopEvent : true,
6641     forumId: 4
6642 });</code></pre>
6643      * <p>
6644      * <b>Attaching multiple handlers in 1 call</b><br>
6645       * The method also allows for a single argument to be passed which is a config object containing properties
6646      * which specify multiple handlers.
6647      * <p>
6648      * Code:<pre><code>
6649 el.on({
6650     'click' : {
6651         fn: this.onClick
6652         scope: this,
6653         delay: 100
6654     },
6655     'mouseover' : {
6656         fn: this.onMouseOver
6657         scope: this
6658     },
6659     'mouseout' : {
6660         fn: this.onMouseOut
6661         scope: this
6662     }
6663 });</code></pre>
6664      * <p>
6665      * Or a shorthand syntax:<br>
6666      * Code:<pre><code>
6667 el.on({
6668     'click' : this.onClick,
6669     'mouseover' : this.onMouseOver,
6670     'mouseout' : this.onMouseOut
6671     scope: this
6672 });</code></pre>
6673      */
6674     pub.on = pub.addListener;
6675     pub.un = pub.removeListener;
6676
6677     pub.stoppedMouseDownEvent = new Roo.util.Event();
6678     return pub;
6679 }();
6680 /**
6681   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
6682   * @param {Function} fn        The method the event invokes
6683   * @param {Object}   scope    An  object that becomes the scope of the handler
6684   * @param {boolean}  override If true, the obj passed in becomes
6685   *                             the execution scope of the listener
6686   * @member Roo
6687   * @method onReady
6688  */
6689 Roo.onReady = Roo.EventManager.onDocumentReady;
6690
6691 Roo.onReady(function(){
6692     var bd = Roo.get(document.body);
6693     if(!bd){ return; }
6694
6695     var cls = [
6696             Roo.isIE ? "roo-ie"
6697             : Roo.isIE11 ? "roo-ie11"
6698             : Roo.isEdge ? "roo-edge"
6699             : Roo.isGecko ? "roo-gecko"
6700             : Roo.isOpera ? "roo-opera"
6701             : Roo.isSafari ? "roo-safari" : ""];
6702
6703     if(Roo.isMac){
6704         cls.push("roo-mac");
6705     }
6706     if(Roo.isLinux){
6707         cls.push("roo-linux");
6708     }
6709     if(Roo.isIOS){
6710         cls.push("roo-ios");
6711     }
6712     if(Roo.isTouch){
6713         cls.push("roo-touch");
6714     }
6715     if(Roo.isBorderBox){
6716         cls.push('roo-border-box');
6717     }
6718     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
6719         var p = bd.dom.parentNode;
6720         if(p){
6721             p.className += ' roo-strict';
6722         }
6723     }
6724     bd.addClass(cls.join(' '));
6725 });
6726
6727 /**
6728  * @class Roo.EventObject
6729  * EventObject exposes the Yahoo! UI Event functionality directly on the object
6730  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
6731  * Example:
6732  * <pre><code>
6733  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
6734     e.preventDefault();
6735     var target = e.getTarget();
6736     ...
6737  }
6738  var myDiv = Roo.get("myDiv");
6739  myDiv.on("click", handleClick);
6740  //or
6741  Roo.EventManager.on("myDiv", 'click', handleClick);
6742  Roo.EventManager.addListener("myDiv", 'click', handleClick);
6743  </code></pre>
6744  * @singleton
6745  */
6746 Roo.EventObject = function(){
6747     
6748     var E = Roo.lib.Event;
6749     
6750     // safari keypress events for special keys return bad keycodes
6751     var safariKeys = {
6752         63234 : 37, // left
6753         63235 : 39, // right
6754         63232 : 38, // up
6755         63233 : 40, // down
6756         63276 : 33, // page up
6757         63277 : 34, // page down
6758         63272 : 46, // delete
6759         63273 : 36, // home
6760         63275 : 35  // end
6761     };
6762
6763     // normalize button clicks
6764     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
6765                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
6766
6767     Roo.EventObjectImpl = function(e){
6768         if(e){
6769             this.setEvent(e.browserEvent || e);
6770         }
6771     };
6772     Roo.EventObjectImpl.prototype = {
6773         /**
6774          * Used to fix doc tools.
6775          * @scope Roo.EventObject.prototype
6776          */
6777             
6778
6779         
6780         
6781         /** The normal browser event */
6782         browserEvent : null,
6783         /** The button pressed in a mouse event */
6784         button : -1,
6785         /** True if the shift key was down during the event */
6786         shiftKey : false,
6787         /** True if the control key was down during the event */
6788         ctrlKey : false,
6789         /** True if the alt key was down during the event */
6790         altKey : false,
6791
6792         /** Key constant 
6793         * @type Number */
6794         BACKSPACE : 8,
6795         /** Key constant 
6796         * @type Number */
6797         TAB : 9,
6798         /** Key constant 
6799         * @type Number */
6800         RETURN : 13,
6801         /** Key constant 
6802         * @type Number */
6803         ENTER : 13,
6804         /** Key constant 
6805         * @type Number */
6806         SHIFT : 16,
6807         /** Key constant 
6808         * @type Number */
6809         CONTROL : 17,
6810         /** Key constant 
6811         * @type Number */
6812         ESC : 27,
6813         /** Key constant 
6814         * @type Number */
6815         SPACE : 32,
6816         /** Key constant 
6817         * @type Number */
6818         PAGEUP : 33,
6819         /** Key constant 
6820         * @type Number */
6821         PAGEDOWN : 34,
6822         /** Key constant 
6823         * @type Number */
6824         END : 35,
6825         /** Key constant 
6826         * @type Number */
6827         HOME : 36,
6828         /** Key constant 
6829         * @type Number */
6830         LEFT : 37,
6831         /** Key constant 
6832         * @type Number */
6833         UP : 38,
6834         /** Key constant 
6835         * @type Number */
6836         RIGHT : 39,
6837         /** Key constant 
6838         * @type Number */
6839         DOWN : 40,
6840         /** Key constant 
6841         * @type Number */
6842         DELETE : 46,
6843         /** Key constant 
6844         * @type Number */
6845         F5 : 116,
6846
6847            /** @private */
6848         setEvent : function(e){
6849             if(e == this || (e && e.browserEvent)){ // already wrapped
6850                 return e;
6851             }
6852             this.browserEvent = e;
6853             if(e){
6854                 // normalize buttons
6855                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
6856                 if(e.type == 'click' && this.button == -1){
6857                     this.button = 0;
6858                 }
6859                 this.type = e.type;
6860                 this.shiftKey = e.shiftKey;
6861                 // mac metaKey behaves like ctrlKey
6862                 this.ctrlKey = e.ctrlKey || e.metaKey;
6863                 this.altKey = e.altKey;
6864                 // in getKey these will be normalized for the mac
6865                 this.keyCode = e.keyCode;
6866                 // keyup warnings on firefox.
6867                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
6868                 // cache the target for the delayed and or buffered events
6869                 this.target = E.getTarget(e);
6870                 // same for XY
6871                 this.xy = E.getXY(e);
6872             }else{
6873                 this.button = -1;
6874                 this.shiftKey = false;
6875                 this.ctrlKey = false;
6876                 this.altKey = false;
6877                 this.keyCode = 0;
6878                 this.charCode =0;
6879                 this.target = null;
6880                 this.xy = [0, 0];
6881             }
6882             return this;
6883         },
6884
6885         /**
6886          * Stop the event (preventDefault and stopPropagation)
6887          */
6888         stopEvent : function(){
6889             if(this.browserEvent){
6890                 if(this.browserEvent.type == 'mousedown'){
6891                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6892                 }
6893                 E.stopEvent(this.browserEvent);
6894             }
6895         },
6896
6897         /**
6898          * Prevents the browsers default handling of the event.
6899          */
6900         preventDefault : function(){
6901             if(this.browserEvent){
6902                 E.preventDefault(this.browserEvent);
6903             }
6904         },
6905
6906         /** @private */
6907         isNavKeyPress : function(){
6908             var k = this.keyCode;
6909             k = Roo.isSafari ? (safariKeys[k] || k) : k;
6910             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
6911         },
6912
6913         isSpecialKey : function(){
6914             var k = this.keyCode;
6915             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
6916             (k == 16) || (k == 17) ||
6917             (k >= 18 && k <= 20) ||
6918             (k >= 33 && k <= 35) ||
6919             (k >= 36 && k <= 39) ||
6920             (k >= 44 && k <= 45);
6921         },
6922         /**
6923          * Cancels bubbling of the event.
6924          */
6925         stopPropagation : function(){
6926             if(this.browserEvent){
6927                 if(this.type == 'mousedown'){
6928                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6929                 }
6930                 E.stopPropagation(this.browserEvent);
6931             }
6932         },
6933
6934         /**
6935          * Gets the key code for the event.
6936          * @return {Number}
6937          */
6938         getCharCode : function(){
6939             return this.charCode || this.keyCode;
6940         },
6941
6942         /**
6943          * Returns a normalized keyCode for the event.
6944          * @return {Number} The key code
6945          */
6946         getKey : function(){
6947             var k = this.keyCode || this.charCode;
6948             return Roo.isSafari ? (safariKeys[k] || k) : k;
6949         },
6950
6951         /**
6952          * Gets the x coordinate of the event.
6953          * @return {Number}
6954          */
6955         getPageX : function(){
6956             return this.xy[0];
6957         },
6958
6959         /**
6960          * Gets the y coordinate of the event.
6961          * @return {Number}
6962          */
6963         getPageY : function(){
6964             return this.xy[1];
6965         },
6966
6967         /**
6968          * Gets the time of the event.
6969          * @return {Number}
6970          */
6971         getTime : function(){
6972             if(this.browserEvent){
6973                 return E.getTime(this.browserEvent);
6974             }
6975             return null;
6976         },
6977
6978         /**
6979          * Gets the page coordinates of the event.
6980          * @return {Array} The xy values like [x, y]
6981          */
6982         getXY : function(){
6983             return this.xy;
6984         },
6985
6986         /**
6987          * Gets the target for the event.
6988          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
6989          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6990                 search as a number or element (defaults to 10 || document.body)
6991          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6992          * @return {HTMLelement}
6993          */
6994         getTarget : function(selector, maxDepth, returnEl){
6995             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
6996         },
6997         /**
6998          * Gets the related target.
6999          * @return {HTMLElement}
7000          */
7001         getRelatedTarget : function(){
7002             if(this.browserEvent){
7003                 return E.getRelatedTarget(this.browserEvent);
7004             }
7005             return null;
7006         },
7007
7008         /**
7009          * Normalizes mouse wheel delta across browsers
7010          * @return {Number} The delta
7011          */
7012         getWheelDelta : function(){
7013             var e = this.browserEvent;
7014             var delta = 0;
7015             if(e.wheelDelta){ /* IE/Opera. */
7016                 delta = e.wheelDelta/120;
7017             }else if(e.detail){ /* Mozilla case. */
7018                 delta = -e.detail/3;
7019             }
7020             return delta;
7021         },
7022
7023         /**
7024          * Returns true if the control, meta, shift or alt key was pressed during this event.
7025          * @return {Boolean}
7026          */
7027         hasModifier : function(){
7028             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
7029         },
7030
7031         /**
7032          * Returns true if the target of this event equals el or is a child of el
7033          * @param {String/HTMLElement/Element} el
7034          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
7035          * @return {Boolean}
7036          */
7037         within : function(el, related){
7038             var t = this[related ? "getRelatedTarget" : "getTarget"]();
7039             return t && Roo.fly(el).contains(t);
7040         },
7041
7042         getPoint : function(){
7043             return new Roo.lib.Point(this.xy[0], this.xy[1]);
7044         }
7045     };
7046
7047     return new Roo.EventObjectImpl();
7048 }();
7049             
7050     /*
7051  * Based on:
7052  * Ext JS Library 1.1.1
7053  * Copyright(c) 2006-2007, Ext JS, LLC.
7054  *
7055  * Originally Released Under LGPL - original licence link has changed is not relivant.
7056  *
7057  * Fork - LGPL
7058  * <script type="text/javascript">
7059  */
7060
7061  
7062 // was in Composite Element!??!?!
7063  
7064 (function(){
7065     var D = Roo.lib.Dom;
7066     var E = Roo.lib.Event;
7067     var A = Roo.lib.Anim;
7068
7069     // local style camelizing for speed
7070     var propCache = {};
7071     var camelRe = /(-[a-z])/gi;
7072     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
7073     var view = document.defaultView;
7074
7075 /**
7076  * @class Roo.Element
7077  * Represents an Element in the DOM.<br><br>
7078  * Usage:<br>
7079 <pre><code>
7080 var el = Roo.get("my-div");
7081
7082 // or with getEl
7083 var el = getEl("my-div");
7084
7085 // or with a DOM element
7086 var el = Roo.get(myDivElement);
7087 </code></pre>
7088  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
7089  * each call instead of constructing a new one.<br><br>
7090  * <b>Animations</b><br />
7091  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
7092  * should either be a boolean (true) or an object literal with animation options. The animation options are:
7093 <pre>
7094 Option    Default   Description
7095 --------- --------  ---------------------------------------------
7096 duration  .35       The duration of the animation in seconds
7097 easing    easeOut   The YUI easing method
7098 callback  none      A function to execute when the anim completes
7099 scope     this      The scope (this) of the callback function
7100 </pre>
7101 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
7102 * manipulate the animation. Here's an example:
7103 <pre><code>
7104 var el = Roo.get("my-div");
7105
7106 // no animation
7107 el.setWidth(100);
7108
7109 // default animation
7110 el.setWidth(100, true);
7111
7112 // animation with some options set
7113 el.setWidth(100, {
7114     duration: 1,
7115     callback: this.foo,
7116     scope: this
7117 });
7118
7119 // using the "anim" property to get the Anim object
7120 var opt = {
7121     duration: 1,
7122     callback: this.foo,
7123     scope: this
7124 };
7125 el.setWidth(100, opt);
7126 ...
7127 if(opt.anim.isAnimated()){
7128     opt.anim.stop();
7129 }
7130 </code></pre>
7131 * <b> Composite (Collections of) Elements</b><br />
7132  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
7133  * @constructor Create a new Element directly.
7134  * @param {String/HTMLElement} element
7135  * @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).
7136  */
7137     Roo.Element = function(element, forceNew){
7138         var dom = typeof element == "string" ?
7139                 document.getElementById(element) : element;
7140         if(!dom){ // invalid id/element
7141             return null;
7142         }
7143         var id = dom.id;
7144         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
7145             return Roo.Element.cache[id];
7146         }
7147
7148         /**
7149          * The DOM element
7150          * @type HTMLElement
7151          */
7152         this.dom = dom;
7153
7154         /**
7155          * The DOM element ID
7156          * @type String
7157          */
7158         this.id = id || Roo.id(dom);
7159     };
7160
7161     var El = Roo.Element;
7162
7163     El.prototype = {
7164         /**
7165          * The element's default display mode  (defaults to "") 
7166          * @type String
7167          */
7168         originalDisplay : "",
7169
7170         
7171         // note this is overridden in BS version..
7172         visibilityMode : 1, 
7173         /**
7174          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
7175          * @type String
7176          */
7177         defaultUnit : "px",
7178         
7179         /**
7180          * Sets the element's visibility mode. When setVisible() is called it
7181          * will use this to determine whether to set the visibility or the display property.
7182          * @param visMode Element.VISIBILITY or Element.DISPLAY
7183          * @return {Roo.Element} this
7184          */
7185         setVisibilityMode : function(visMode){
7186             this.visibilityMode = visMode;
7187             return this;
7188         },
7189         /**
7190          * Convenience method for setVisibilityMode(Element.DISPLAY)
7191          * @param {String} display (optional) What to set display to when visible
7192          * @return {Roo.Element} this
7193          */
7194         enableDisplayMode : function(display){
7195             this.setVisibilityMode(El.DISPLAY);
7196             if(typeof display != "undefined") { this.originalDisplay = display; }
7197             return this;
7198         },
7199
7200         /**
7201          * 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)
7202          * @param {String} selector The simple selector to test
7203          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7204                 search as a number or element (defaults to 10 || document.body)
7205          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7206          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7207          */
7208         findParent : function(simpleSelector, maxDepth, returnEl){
7209             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
7210             maxDepth = maxDepth || 50;
7211             if(typeof maxDepth != "number"){
7212                 stopEl = Roo.getDom(maxDepth);
7213                 maxDepth = 10;
7214             }
7215             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
7216                 if(dq.is(p, simpleSelector)){
7217                     return returnEl ? Roo.get(p) : p;
7218                 }
7219                 depth++;
7220                 p = p.parentNode;
7221             }
7222             return null;
7223         },
7224
7225
7226         /**
7227          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
7228          * @param {String} selector The simple selector to test
7229          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7230                 search as a number or element (defaults to 10 || document.body)
7231          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7232          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7233          */
7234         findParentNode : function(simpleSelector, maxDepth, returnEl){
7235             var p = Roo.fly(this.dom.parentNode, '_internal');
7236             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
7237         },
7238         
7239         /**
7240          * Looks at  the scrollable parent element
7241          */
7242         findScrollableParent : function()
7243         {
7244             var overflowRegex = /(auto|scroll)/;
7245             
7246             if(this.getStyle('position') === 'fixed'){
7247                 return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
7248             }
7249             
7250             var excludeStaticParent = this.getStyle('position') === "absolute";
7251             
7252             for (var parent = this; (parent = Roo.get(parent.dom.parentNode));){
7253                 
7254                 if (excludeStaticParent && parent.getStyle('position') === "static") {
7255                     continue;
7256                 }
7257                 
7258                 if (overflowRegex.test(parent.getStyle('overflow') + parent.getStyle('overflow-x') + parent.getStyle('overflow-y'))){
7259                     return parent;
7260                 }
7261                 
7262                 if(parent.dom.nodeName.toLowerCase() == 'body'){
7263                     return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
7264                 }
7265             }
7266             
7267             return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
7268         },
7269
7270         /**
7271          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
7272          * This is a shortcut for findParentNode() that always returns an Roo.Element.
7273          * @param {String} selector The simple selector to test
7274          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7275                 search as a number or element (defaults to 10 || document.body)
7276          * @return {Roo.Element} The matching DOM node (or null if no match was found)
7277          */
7278         up : function(simpleSelector, maxDepth){
7279             return this.findParentNode(simpleSelector, maxDepth, true);
7280         },
7281
7282
7283
7284         /**
7285          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
7286          * @param {String} selector The simple selector to test
7287          * @return {Boolean} True if this element matches the selector, else false
7288          */
7289         is : function(simpleSelector){
7290             return Roo.DomQuery.is(this.dom, simpleSelector);
7291         },
7292
7293         /**
7294          * Perform animation on this element.
7295          * @param {Object} args The YUI animation control args
7296          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
7297          * @param {Function} onComplete (optional) Function to call when animation completes
7298          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
7299          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
7300          * @return {Roo.Element} this
7301          */
7302         animate : function(args, duration, onComplete, easing, animType){
7303             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
7304             return this;
7305         },
7306
7307         /*
7308          * @private Internal animation call
7309          */
7310         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
7311             animType = animType || 'run';
7312             opt = opt || {};
7313             var anim = Roo.lib.Anim[animType](
7314                 this.dom, args,
7315                 (opt.duration || defaultDur) || .35,
7316                 (opt.easing || defaultEase) || 'easeOut',
7317                 function(){
7318                     Roo.callback(cb, this);
7319                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
7320                 },
7321                 this
7322             );
7323             opt.anim = anim;
7324             return anim;
7325         },
7326
7327         // private legacy anim prep
7328         preanim : function(a, i){
7329             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
7330         },
7331
7332         /**
7333          * Removes worthless text nodes
7334          * @param {Boolean} forceReclean (optional) By default the element
7335          * keeps track if it has been cleaned already so
7336          * you can call this over and over. However, if you update the element and
7337          * need to force a reclean, you can pass true.
7338          */
7339         clean : function(forceReclean){
7340             if(this.isCleaned && forceReclean !== true){
7341                 return this;
7342             }
7343             var ns = /\S/;
7344             var d = this.dom, n = d.firstChild, ni = -1;
7345             while(n){
7346                 var nx = n.nextSibling;
7347                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
7348                     d.removeChild(n);
7349                 }else{
7350                     n.nodeIndex = ++ni;
7351                 }
7352                 n = nx;
7353             }
7354             this.isCleaned = true;
7355             return this;
7356         },
7357
7358         // private
7359         calcOffsetsTo : function(el){
7360             el = Roo.get(el);
7361             var d = el.dom;
7362             var restorePos = false;
7363             if(el.getStyle('position') == 'static'){
7364                 el.position('relative');
7365                 restorePos = true;
7366             }
7367             var x = 0, y =0;
7368             var op = this.dom;
7369             while(op && op != d && op.tagName != 'HTML'){
7370                 x+= op.offsetLeft;
7371                 y+= op.offsetTop;
7372                 op = op.offsetParent;
7373             }
7374             if(restorePos){
7375                 el.position('static');
7376             }
7377             return [x, y];
7378         },
7379
7380         /**
7381          * Scrolls this element into view within the passed container.
7382          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
7383          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
7384          * @return {Roo.Element} this
7385          */
7386         scrollIntoView : function(container, hscroll){
7387             var c = Roo.getDom(container) || document.body;
7388             var el = this.dom;
7389
7390             var o = this.calcOffsetsTo(c),
7391                 l = o[0],
7392                 t = o[1],
7393                 b = t+el.offsetHeight,
7394                 r = l+el.offsetWidth;
7395
7396             var ch = c.clientHeight;
7397             var ct = parseInt(c.scrollTop, 10);
7398             var cl = parseInt(c.scrollLeft, 10);
7399             var cb = ct + ch;
7400             var cr = cl + c.clientWidth;
7401
7402             if(t < ct){
7403                 c.scrollTop = t;
7404             }else if(b > cb){
7405                 c.scrollTop = b-ch;
7406             }
7407
7408             if(hscroll !== false){
7409                 if(l < cl){
7410                     c.scrollLeft = l;
7411                 }else if(r > cr){
7412                     c.scrollLeft = r-c.clientWidth;
7413                 }
7414             }
7415             return this;
7416         },
7417
7418         // private
7419         scrollChildIntoView : function(child, hscroll){
7420             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
7421         },
7422
7423         /**
7424          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
7425          * the new height may not be available immediately.
7426          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
7427          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
7428          * @param {Function} onComplete (optional) Function to call when animation completes
7429          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
7430          * @return {Roo.Element} this
7431          */
7432         autoHeight : function(animate, duration, onComplete, easing){
7433             var oldHeight = this.getHeight();
7434             this.clip();
7435             this.setHeight(1); // force clipping
7436             setTimeout(function(){
7437                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
7438                 if(!animate){
7439                     this.setHeight(height);
7440                     this.unclip();
7441                     if(typeof onComplete == "function"){
7442                         onComplete();
7443                     }
7444                 }else{
7445                     this.setHeight(oldHeight); // restore original height
7446                     this.setHeight(height, animate, duration, function(){
7447                         this.unclip();
7448                         if(typeof onComplete == "function") { onComplete(); }
7449                     }.createDelegate(this), easing);
7450                 }
7451             }.createDelegate(this), 0);
7452             return this;
7453         },
7454
7455         /**
7456          * Returns true if this element is an ancestor of the passed element
7457          * @param {HTMLElement/String} el The element to check
7458          * @return {Boolean} True if this element is an ancestor of el, else false
7459          */
7460         contains : function(el){
7461             if(!el){return false;}
7462             return D.isAncestor(this.dom, el.dom ? el.dom : el);
7463         },
7464
7465         /**
7466          * Checks whether the element is currently visible using both visibility and display properties.
7467          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
7468          * @return {Boolean} True if the element is currently visible, else false
7469          */
7470         isVisible : function(deep) {
7471             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
7472             if(deep !== true || !vis){
7473                 return vis;
7474             }
7475             var p = this.dom.parentNode;
7476             while(p && p.tagName.toLowerCase() != "body"){
7477                 if(!Roo.fly(p, '_isVisible').isVisible()){
7478                     return false;
7479                 }
7480                 p = p.parentNode;
7481             }
7482             return true;
7483         },
7484
7485         /**
7486          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
7487          * @param {String} selector The CSS selector
7488          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
7489          * @return {CompositeElement/CompositeElementLite} The composite element
7490          */
7491         select : function(selector, unique){
7492             return El.select(selector, unique, this.dom);
7493         },
7494
7495         /**
7496          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
7497          * @param {String} selector The CSS selector
7498          * @return {Array} An array of the matched nodes
7499          */
7500         query : function(selector, unique){
7501             return Roo.DomQuery.select(selector, this.dom);
7502         },
7503
7504         /**
7505          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
7506          * @param {String} selector The CSS selector
7507          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7508          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7509          */
7510         child : function(selector, returnDom){
7511             var n = Roo.DomQuery.selectNode(selector, this.dom);
7512             return returnDom ? n : Roo.get(n);
7513         },
7514
7515         /**
7516          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
7517          * @param {String} selector The CSS selector
7518          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7519          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7520          */
7521         down : function(selector, returnDom){
7522             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
7523             return returnDom ? n : Roo.get(n);
7524         },
7525
7526         /**
7527          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
7528          * @param {String} group The group the DD object is member of
7529          * @param {Object} config The DD config object
7530          * @param {Object} overrides An object containing methods to override/implement on the DD object
7531          * @return {Roo.dd.DD} The DD object
7532          */
7533         initDD : function(group, config, overrides){
7534             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
7535             return Roo.apply(dd, overrides);
7536         },
7537
7538         /**
7539          * Initializes a {@link Roo.dd.DDProxy} object for this element.
7540          * @param {String} group The group the DDProxy object is member of
7541          * @param {Object} config The DDProxy config object
7542          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
7543          * @return {Roo.dd.DDProxy} The DDProxy object
7544          */
7545         initDDProxy : function(group, config, overrides){
7546             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
7547             return Roo.apply(dd, overrides);
7548         },
7549
7550         /**
7551          * Initializes a {@link Roo.dd.DDTarget} object for this element.
7552          * @param {String} group The group the DDTarget object is member of
7553          * @param {Object} config The DDTarget config object
7554          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
7555          * @return {Roo.dd.DDTarget} The DDTarget object
7556          */
7557         initDDTarget : function(group, config, overrides){
7558             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
7559             return Roo.apply(dd, overrides);
7560         },
7561
7562         /**
7563          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
7564          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
7565          * @param {Boolean} visible Whether the element is visible
7566          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7567          * @return {Roo.Element} this
7568          */
7569          setVisible : function(visible, animate){
7570             if(!animate || !A){
7571                 if(this.visibilityMode == El.DISPLAY){
7572                     this.setDisplayed(visible);
7573                 }else{
7574                     this.fixDisplay();
7575                     this.dom.style.visibility = visible ? "visible" : "hidden";
7576                 }
7577             }else{
7578                 // closure for composites
7579                 var dom = this.dom;
7580                 var visMode = this.visibilityMode;
7581                 if(visible){
7582                     this.setOpacity(.01);
7583                     this.setVisible(true);
7584                 }
7585                 this.anim({opacity: { to: (visible?1:0) }},
7586                       this.preanim(arguments, 1),
7587                       null, .35, 'easeIn', function(){
7588                          if(!visible){
7589                              if(visMode == El.DISPLAY){
7590                                  dom.style.display = "none";
7591                              }else{
7592                                  dom.style.visibility = "hidden";
7593                              }
7594                              Roo.get(dom).setOpacity(1);
7595                          }
7596                      });
7597             }
7598             return this;
7599         },
7600
7601         /**
7602          * Returns true if display is not "none"
7603          * @return {Boolean}
7604          */
7605         isDisplayed : function() {
7606             return this.getStyle("display") != "none";
7607         },
7608
7609         /**
7610          * Toggles the element's visibility or display, depending on visibility mode.
7611          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7612          * @return {Roo.Element} this
7613          */
7614         toggle : function(animate){
7615             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
7616             return this;
7617         },
7618
7619         /**
7620          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
7621          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
7622          * @return {Roo.Element} this
7623          */
7624         setDisplayed : function(value) {
7625             if(typeof value == "boolean"){
7626                value = value ? this.originalDisplay : "none";
7627             }
7628             this.setStyle("display", value);
7629             return this;
7630         },
7631
7632         /**
7633          * Tries to focus the element. Any exceptions are caught and ignored.
7634          * @return {Roo.Element} this
7635          */
7636         focus : function() {
7637             try{
7638                 this.dom.focus();
7639             }catch(e){}
7640             return this;
7641         },
7642
7643         /**
7644          * Tries to blur the element. Any exceptions are caught and ignored.
7645          * @return {Roo.Element} this
7646          */
7647         blur : function() {
7648             try{
7649                 this.dom.blur();
7650             }catch(e){}
7651             return this;
7652         },
7653
7654         /**
7655          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
7656          * @param {String/Array} className The CSS class to add, or an array of classes
7657          * @return {Roo.Element} this
7658          */
7659         addClass : function(className){
7660             if(className instanceof Array){
7661                 for(var i = 0, len = className.length; i < len; i++) {
7662                     this.addClass(className[i]);
7663                 }
7664             }else{
7665                 if(className && !this.hasClass(className)){
7666                     this.dom.className = this.dom.className + " " + className;
7667                 }
7668             }
7669             return this;
7670         },
7671
7672         /**
7673          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
7674          * @param {String/Array} className The CSS class to add, or an array of classes
7675          * @return {Roo.Element} this
7676          */
7677         radioClass : function(className){
7678             var siblings = this.dom.parentNode.childNodes;
7679             for(var i = 0; i < siblings.length; i++) {
7680                 var s = siblings[i];
7681                 if(s.nodeType == 1){
7682                     Roo.get(s).removeClass(className);
7683                 }
7684             }
7685             this.addClass(className);
7686             return this;
7687         },
7688
7689         /**
7690          * Removes one or more CSS classes from the element.
7691          * @param {String/Array} className The CSS class to remove, or an array of classes
7692          * @return {Roo.Element} this
7693          */
7694         removeClass : function(className){
7695             if(!className || !this.dom.className){
7696                 return this;
7697             }
7698             if(className instanceof Array){
7699                 for(var i = 0, len = className.length; i < len; i++) {
7700                     this.removeClass(className[i]);
7701                 }
7702             }else{
7703                 if(this.hasClass(className)){
7704                     var re = this.classReCache[className];
7705                     if (!re) {
7706                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
7707                        this.classReCache[className] = re;
7708                     }
7709                     this.dom.className =
7710                         this.dom.className.replace(re, " ");
7711                 }
7712             }
7713             return this;
7714         },
7715
7716         // private
7717         classReCache: {},
7718
7719         /**
7720          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
7721          * @param {String} className The CSS class to toggle
7722          * @return {Roo.Element} this
7723          */
7724         toggleClass : function(className){
7725             if(this.hasClass(className)){
7726                 this.removeClass(className);
7727             }else{
7728                 this.addClass(className);
7729             }
7730             return this;
7731         },
7732
7733         /**
7734          * Checks if the specified CSS class exists on this element's DOM node.
7735          * @param {String} className The CSS class to check for
7736          * @return {Boolean} True if the class exists, else false
7737          */
7738         hasClass : function(className){
7739             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
7740         },
7741
7742         /**
7743          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
7744          * @param {String} oldClassName The CSS class to replace
7745          * @param {String} newClassName The replacement CSS class
7746          * @return {Roo.Element} this
7747          */
7748         replaceClass : function(oldClassName, newClassName){
7749             this.removeClass(oldClassName);
7750             this.addClass(newClassName);
7751             return this;
7752         },
7753
7754         /**
7755          * Returns an object with properties matching the styles requested.
7756          * For example, el.getStyles('color', 'font-size', 'width') might return
7757          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
7758          * @param {String} style1 A style name
7759          * @param {String} style2 A style name
7760          * @param {String} etc.
7761          * @return {Object} The style object
7762          */
7763         getStyles : function(){
7764             var a = arguments, len = a.length, r = {};
7765             for(var i = 0; i < len; i++){
7766                 r[a[i]] = this.getStyle(a[i]);
7767             }
7768             return r;
7769         },
7770
7771         /**
7772          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
7773          * @param {String} property The style property whose value is returned.
7774          * @return {String} The current value of the style property for this element.
7775          */
7776         getStyle : function(){
7777             return view && view.getComputedStyle ?
7778                 function(prop){
7779                     var el = this.dom, v, cs, camel;
7780                     if(prop == 'float'){
7781                         prop = "cssFloat";
7782                     }
7783                     if(el.style && (v = el.style[prop])){
7784                         return v;
7785                     }
7786                     if(cs = view.getComputedStyle(el, "")){
7787                         if(!(camel = propCache[prop])){
7788                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
7789                         }
7790                         return cs[camel];
7791                     }
7792                     return null;
7793                 } :
7794                 function(prop){
7795                     var el = this.dom, v, cs, camel;
7796                     if(prop == 'opacity'){
7797                         if(typeof el.style.filter == 'string'){
7798                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
7799                             if(m){
7800                                 var fv = parseFloat(m[1]);
7801                                 if(!isNaN(fv)){
7802                                     return fv ? fv / 100 : 0;
7803                                 }
7804                             }
7805                         }
7806                         return 1;
7807                     }else if(prop == 'float'){
7808                         prop = "styleFloat";
7809                     }
7810                     if(!(camel = propCache[prop])){
7811                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
7812                     }
7813                     if(v = el.style[camel]){
7814                         return v;
7815                     }
7816                     if(cs = el.currentStyle){
7817                         return cs[camel];
7818                     }
7819                     return null;
7820                 };
7821         }(),
7822
7823         /**
7824          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
7825          * @param {String/Object} property The style property to be set, or an object of multiple styles.
7826          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
7827          * @return {Roo.Element} this
7828          */
7829         setStyle : function(prop, value){
7830             if(typeof prop == "string"){
7831                 
7832                 if (prop == 'float') {
7833                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
7834                     return this;
7835                 }
7836                 
7837                 var camel;
7838                 if(!(camel = propCache[prop])){
7839                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
7840                 }
7841                 
7842                 if(camel == 'opacity') {
7843                     this.setOpacity(value);
7844                 }else{
7845                     this.dom.style[camel] = value;
7846                 }
7847             }else{
7848                 for(var style in prop){
7849                     if(typeof prop[style] != "function"){
7850                        this.setStyle(style, prop[style]);
7851                     }
7852                 }
7853             }
7854             return this;
7855         },
7856
7857         /**
7858          * More flexible version of {@link #setStyle} for setting style properties.
7859          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
7860          * a function which returns such a specification.
7861          * @return {Roo.Element} this
7862          */
7863         applyStyles : function(style){
7864             Roo.DomHelper.applyStyles(this.dom, style);
7865             return this;
7866         },
7867
7868         /**
7869           * 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).
7870           * @return {Number} The X position of the element
7871           */
7872         getX : function(){
7873             return D.getX(this.dom);
7874         },
7875
7876         /**
7877           * 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).
7878           * @return {Number} The Y position of the element
7879           */
7880         getY : function(){
7881             return D.getY(this.dom);
7882         },
7883
7884         /**
7885           * 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).
7886           * @return {Array} The XY position of the element
7887           */
7888         getXY : function(){
7889             return D.getXY(this.dom);
7890         },
7891
7892         /**
7893          * 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).
7894          * @param {Number} The X position of the element
7895          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7896          * @return {Roo.Element} this
7897          */
7898         setX : function(x, animate){
7899             if(!animate || !A){
7900                 D.setX(this.dom, x);
7901             }else{
7902                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
7903             }
7904             return this;
7905         },
7906
7907         /**
7908          * 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).
7909          * @param {Number} The Y position of the element
7910          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7911          * @return {Roo.Element} this
7912          */
7913         setY : function(y, animate){
7914             if(!animate || !A){
7915                 D.setY(this.dom, y);
7916             }else{
7917                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
7918             }
7919             return this;
7920         },
7921
7922         /**
7923          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
7924          * @param {String} left The left CSS property value
7925          * @return {Roo.Element} this
7926          */
7927         setLeft : function(left){
7928             this.setStyle("left", this.addUnits(left));
7929             return this;
7930         },
7931
7932         /**
7933          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
7934          * @param {String} top The top CSS property value
7935          * @return {Roo.Element} this
7936          */
7937         setTop : function(top){
7938             this.setStyle("top", this.addUnits(top));
7939             return this;
7940         },
7941
7942         /**
7943          * Sets the element's CSS right style.
7944          * @param {String} right The right CSS property value
7945          * @return {Roo.Element} this
7946          */
7947         setRight : function(right){
7948             this.setStyle("right", this.addUnits(right));
7949             return this;
7950         },
7951
7952         /**
7953          * Sets the element's CSS bottom style.
7954          * @param {String} bottom The bottom CSS property value
7955          * @return {Roo.Element} this
7956          */
7957         setBottom : function(bottom){
7958             this.setStyle("bottom", this.addUnits(bottom));
7959             return this;
7960         },
7961
7962         /**
7963          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7964          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7965          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
7966          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7967          * @return {Roo.Element} this
7968          */
7969         setXY : function(pos, animate){
7970             if(!animate || !A){
7971                 D.setXY(this.dom, pos);
7972             }else{
7973                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
7974             }
7975             return this;
7976         },
7977
7978         /**
7979          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7980          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7981          * @param {Number} x X value for new position (coordinates are page-based)
7982          * @param {Number} y Y value for new position (coordinates are page-based)
7983          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7984          * @return {Roo.Element} this
7985          */
7986         setLocation : function(x, y, animate){
7987             this.setXY([x, y], this.preanim(arguments, 2));
7988             return this;
7989         },
7990
7991         /**
7992          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7993          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7994          * @param {Number} x X value for new position (coordinates are page-based)
7995          * @param {Number} y Y value for new position (coordinates are page-based)
7996          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7997          * @return {Roo.Element} this
7998          */
7999         moveTo : function(x, y, animate){
8000             this.setXY([x, y], this.preanim(arguments, 2));
8001             return this;
8002         },
8003
8004         /**
8005          * Returns the region of the given element.
8006          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
8007          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
8008          */
8009         getRegion : function(){
8010             return D.getRegion(this.dom);
8011         },
8012
8013         /**
8014          * Returns the offset height of the element
8015          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
8016          * @return {Number} The element's height
8017          */
8018         getHeight : function(contentHeight){
8019             var h = this.dom.offsetHeight || 0;
8020             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
8021         },
8022
8023         /**
8024          * Returns the offset width of the element
8025          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
8026          * @return {Number} The element's width
8027          */
8028         getWidth : function(contentWidth){
8029             var w = this.dom.offsetWidth || 0;
8030             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
8031         },
8032
8033         /**
8034          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
8035          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
8036          * if a height has not been set using CSS.
8037          * @return {Number}
8038          */
8039         getComputedHeight : function(){
8040             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
8041             if(!h){
8042                 h = parseInt(this.getStyle('height'), 10) || 0;
8043                 if(!this.isBorderBox()){
8044                     h += this.getFrameWidth('tb');
8045                 }
8046             }
8047             return h;
8048         },
8049
8050         /**
8051          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
8052          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
8053          * if a width has not been set using CSS.
8054          * @return {Number}
8055          */
8056         getComputedWidth : function(){
8057             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
8058             if(!w){
8059                 w = parseInt(this.getStyle('width'), 10) || 0;
8060                 if(!this.isBorderBox()){
8061                     w += this.getFrameWidth('lr');
8062                 }
8063             }
8064             return w;
8065         },
8066
8067         /**
8068          * Returns the size of the element.
8069          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
8070          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
8071          */
8072         getSize : function(contentSize){
8073             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
8074         },
8075
8076         /**
8077          * Returns the width and height of the viewport.
8078          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
8079          */
8080         getViewSize : function(){
8081             var d = this.dom, doc = document, aw = 0, ah = 0;
8082             if(d == doc || d == doc.body){
8083                 return {width : D.getViewWidth(), height: D.getViewHeight()};
8084             }else{
8085                 return {
8086                     width : d.clientWidth,
8087                     height: d.clientHeight
8088                 };
8089             }
8090         },
8091
8092         /**
8093          * Returns the value of the "value" attribute
8094          * @param {Boolean} asNumber true to parse the value as a number
8095          * @return {String/Number}
8096          */
8097         getValue : function(asNumber){
8098             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
8099         },
8100
8101         // private
8102         adjustWidth : function(width){
8103             if(typeof width == "number"){
8104                 if(this.autoBoxAdjust && !this.isBorderBox()){
8105                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8106                 }
8107                 if(width < 0){
8108                     width = 0;
8109                 }
8110             }
8111             return width;
8112         },
8113
8114         // private
8115         adjustHeight : function(height){
8116             if(typeof height == "number"){
8117                if(this.autoBoxAdjust && !this.isBorderBox()){
8118                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8119                }
8120                if(height < 0){
8121                    height = 0;
8122                }
8123             }
8124             return height;
8125         },
8126
8127         /**
8128          * Set the width of the element
8129          * @param {Number} width The new width
8130          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8131          * @return {Roo.Element} this
8132          */
8133         setWidth : function(width, animate){
8134             width = this.adjustWidth(width);
8135             if(!animate || !A){
8136                 this.dom.style.width = this.addUnits(width);
8137             }else{
8138                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
8139             }
8140             return this;
8141         },
8142
8143         /**
8144          * Set the height of the element
8145          * @param {Number} height The new height
8146          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8147          * @return {Roo.Element} this
8148          */
8149          setHeight : function(height, animate){
8150             height = this.adjustHeight(height);
8151             if(!animate || !A){
8152                 this.dom.style.height = this.addUnits(height);
8153             }else{
8154                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
8155             }
8156             return this;
8157         },
8158
8159         /**
8160          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
8161          * @param {Number} width The new width
8162          * @param {Number} height The new height
8163          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8164          * @return {Roo.Element} this
8165          */
8166          setSize : function(width, height, animate){
8167             if(typeof width == "object"){ // in case of object from getSize()
8168                 height = width.height; width = width.width;
8169             }
8170             width = this.adjustWidth(width); height = this.adjustHeight(height);
8171             if(!animate || !A){
8172                 this.dom.style.width = this.addUnits(width);
8173                 this.dom.style.height = this.addUnits(height);
8174             }else{
8175                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
8176             }
8177             return this;
8178         },
8179
8180         /**
8181          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
8182          * @param {Number} x X value for new position (coordinates are page-based)
8183          * @param {Number} y Y value for new position (coordinates are page-based)
8184          * @param {Number} width The new width
8185          * @param {Number} height The new height
8186          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8187          * @return {Roo.Element} this
8188          */
8189         setBounds : function(x, y, width, height, animate){
8190             if(!animate || !A){
8191                 this.setSize(width, height);
8192                 this.setLocation(x, y);
8193             }else{
8194                 width = this.adjustWidth(width); height = this.adjustHeight(height);
8195                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
8196                               this.preanim(arguments, 4), 'motion');
8197             }
8198             return this;
8199         },
8200
8201         /**
8202          * 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.
8203          * @param {Roo.lib.Region} region The region to fill
8204          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8205          * @return {Roo.Element} this
8206          */
8207         setRegion : function(region, animate){
8208             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
8209             return this;
8210         },
8211
8212         /**
8213          * Appends an event handler
8214          *
8215          * @param {String}   eventName     The type of event to append
8216          * @param {Function} fn        The method the event invokes
8217          * @param {Object} scope       (optional) The scope (this object) of the fn
8218          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
8219          */
8220         addListener : function(eventName, fn, scope, options){
8221             if (this.dom) {
8222                 Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
8223             }
8224         },
8225
8226         /**
8227          * Removes an event handler from this element
8228          * @param {String} eventName the type of event to remove
8229          * @param {Function} fn the method the event invokes
8230          * @return {Roo.Element} this
8231          */
8232         removeListener : function(eventName, fn){
8233             Roo.EventManager.removeListener(this.dom,  eventName, fn);
8234             return this;
8235         },
8236
8237         /**
8238          * Removes all previous added listeners from this element
8239          * @return {Roo.Element} this
8240          */
8241         removeAllListeners : function(){
8242             E.purgeElement(this.dom);
8243             return this;
8244         },
8245
8246         relayEvent : function(eventName, observable){
8247             this.on(eventName, function(e){
8248                 observable.fireEvent(eventName, e);
8249             });
8250         },
8251
8252         /**
8253          * Set the opacity of the element
8254          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
8255          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8256          * @return {Roo.Element} this
8257          */
8258          setOpacity : function(opacity, animate){
8259             if(!animate || !A){
8260                 var s = this.dom.style;
8261                 if(Roo.isIE){
8262                     s.zoom = 1;
8263                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
8264                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
8265                 }else{
8266                     s.opacity = opacity;
8267                 }
8268             }else{
8269                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
8270             }
8271             return this;
8272         },
8273
8274         /**
8275          * Gets the left X coordinate
8276          * @param {Boolean} local True to get the local css position instead of page coordinate
8277          * @return {Number}
8278          */
8279         getLeft : function(local){
8280             if(!local){
8281                 return this.getX();
8282             }else{
8283                 return parseInt(this.getStyle("left"), 10) || 0;
8284             }
8285         },
8286
8287         /**
8288          * Gets the right X coordinate of the element (element X position + element width)
8289          * @param {Boolean} local True to get the local css position instead of page coordinate
8290          * @return {Number}
8291          */
8292         getRight : function(local){
8293             if(!local){
8294                 return this.getX() + this.getWidth();
8295             }else{
8296                 return (this.getLeft(true) + this.getWidth()) || 0;
8297             }
8298         },
8299
8300         /**
8301          * Gets the top Y coordinate
8302          * @param {Boolean} local True to get the local css position instead of page coordinate
8303          * @return {Number}
8304          */
8305         getTop : function(local) {
8306             if(!local){
8307                 return this.getY();
8308             }else{
8309                 return parseInt(this.getStyle("top"), 10) || 0;
8310             }
8311         },
8312
8313         /**
8314          * Gets the bottom Y coordinate of the element (element Y position + element height)
8315          * @param {Boolean} local True to get the local css position instead of page coordinate
8316          * @return {Number}
8317          */
8318         getBottom : function(local){
8319             if(!local){
8320                 return this.getY() + this.getHeight();
8321             }else{
8322                 return (this.getTop(true) + this.getHeight()) || 0;
8323             }
8324         },
8325
8326         /**
8327         * Initializes positioning on this element. If a desired position is not passed, it will make the
8328         * the element positioned relative IF it is not already positioned.
8329         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
8330         * @param {Number} zIndex (optional) The zIndex to apply
8331         * @param {Number} x (optional) Set the page X position
8332         * @param {Number} y (optional) Set the page Y position
8333         */
8334         position : function(pos, zIndex, x, y){
8335             if(!pos){
8336                if(this.getStyle('position') == 'static'){
8337                    this.setStyle('position', 'relative');
8338                }
8339             }else{
8340                 this.setStyle("position", pos);
8341             }
8342             if(zIndex){
8343                 this.setStyle("z-index", zIndex);
8344             }
8345             if(x !== undefined && y !== undefined){
8346                 this.setXY([x, y]);
8347             }else if(x !== undefined){
8348                 this.setX(x);
8349             }else if(y !== undefined){
8350                 this.setY(y);
8351             }
8352         },
8353
8354         /**
8355         * Clear positioning back to the default when the document was loaded
8356         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
8357         * @return {Roo.Element} this
8358          */
8359         clearPositioning : function(value){
8360             value = value ||'';
8361             this.setStyle({
8362                 "left": value,
8363                 "right": value,
8364                 "top": value,
8365                 "bottom": value,
8366                 "z-index": "",
8367                 "position" : "static"
8368             });
8369             return this;
8370         },
8371
8372         /**
8373         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
8374         * snapshot before performing an update and then restoring the element.
8375         * @return {Object}
8376         */
8377         getPositioning : function(){
8378             var l = this.getStyle("left");
8379             var t = this.getStyle("top");
8380             return {
8381                 "position" : this.getStyle("position"),
8382                 "left" : l,
8383                 "right" : l ? "" : this.getStyle("right"),
8384                 "top" : t,
8385                 "bottom" : t ? "" : this.getStyle("bottom"),
8386                 "z-index" : this.getStyle("z-index")
8387             };
8388         },
8389
8390         /**
8391          * Gets the width of the border(s) for the specified side(s)
8392          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8393          * passing lr would get the border (l)eft width + the border (r)ight width.
8394          * @return {Number} The width of the sides passed added together
8395          */
8396         getBorderWidth : function(side){
8397             return this.addStyles(side, El.borders);
8398         },
8399
8400         /**
8401          * Gets the width of the padding(s) for the specified side(s)
8402          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8403          * passing lr would get the padding (l)eft + the padding (r)ight.
8404          * @return {Number} The padding of the sides passed added together
8405          */
8406         getPadding : function(side){
8407             return this.addStyles(side, El.paddings);
8408         },
8409
8410         /**
8411         * Set positioning with an object returned by getPositioning().
8412         * @param {Object} posCfg
8413         * @return {Roo.Element} this
8414          */
8415         setPositioning : function(pc){
8416             this.applyStyles(pc);
8417             if(pc.right == "auto"){
8418                 this.dom.style.right = "";
8419             }
8420             if(pc.bottom == "auto"){
8421                 this.dom.style.bottom = "";
8422             }
8423             return this;
8424         },
8425
8426         // private
8427         fixDisplay : function(){
8428             if(this.getStyle("display") == "none"){
8429                 this.setStyle("visibility", "hidden");
8430                 this.setStyle("display", this.originalDisplay); // first try reverting to default
8431                 if(this.getStyle("display") == "none"){ // if that fails, default to block
8432                     this.setStyle("display", "block");
8433                 }
8434             }
8435         },
8436
8437         /**
8438          * Quick set left and top adding default units
8439          * @param {String} left The left CSS property value
8440          * @param {String} top The top CSS property value
8441          * @return {Roo.Element} this
8442          */
8443          setLeftTop : function(left, top){
8444             this.dom.style.left = this.addUnits(left);
8445             this.dom.style.top = this.addUnits(top);
8446             return this;
8447         },
8448
8449         /**
8450          * Move this element relative to its current position.
8451          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
8452          * @param {Number} distance How far to move the element in pixels
8453          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8454          * @return {Roo.Element} this
8455          */
8456          move : function(direction, distance, animate){
8457             var xy = this.getXY();
8458             direction = direction.toLowerCase();
8459             switch(direction){
8460                 case "l":
8461                 case "left":
8462                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
8463                     break;
8464                case "r":
8465                case "right":
8466                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
8467                     break;
8468                case "t":
8469                case "top":
8470                case "up":
8471                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
8472                     break;
8473                case "b":
8474                case "bottom":
8475                case "down":
8476                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
8477                     break;
8478             }
8479             return this;
8480         },
8481
8482         /**
8483          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
8484          * @return {Roo.Element} this
8485          */
8486         clip : function(){
8487             if(!this.isClipped){
8488                this.isClipped = true;
8489                this.originalClip = {
8490                    "o": this.getStyle("overflow"),
8491                    "x": this.getStyle("overflow-x"),
8492                    "y": this.getStyle("overflow-y")
8493                };
8494                this.setStyle("overflow", "hidden");
8495                this.setStyle("overflow-x", "hidden");
8496                this.setStyle("overflow-y", "hidden");
8497             }
8498             return this;
8499         },
8500
8501         /**
8502          *  Return clipping (overflow) to original clipping before clip() was called
8503          * @return {Roo.Element} this
8504          */
8505         unclip : function(){
8506             if(this.isClipped){
8507                 this.isClipped = false;
8508                 var o = this.originalClip;
8509                 if(o.o){this.setStyle("overflow", o.o);}
8510                 if(o.x){this.setStyle("overflow-x", o.x);}
8511                 if(o.y){this.setStyle("overflow-y", o.y);}
8512             }
8513             return this;
8514         },
8515
8516
8517         /**
8518          * Gets the x,y coordinates specified by the anchor position on the element.
8519          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
8520          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
8521          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
8522          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
8523          * @return {Array} [x, y] An array containing the element's x and y coordinates
8524          */
8525         getAnchorXY : function(anchor, local, s){
8526             //Passing a different size is useful for pre-calculating anchors,
8527             //especially for anchored animations that change the el size.
8528
8529             var w, h, vp = false;
8530             if(!s){
8531                 var d = this.dom;
8532                 if(d == document.body || d == document){
8533                     vp = true;
8534                     w = D.getViewWidth(); h = D.getViewHeight();
8535                 }else{
8536                     w = this.getWidth(); h = this.getHeight();
8537                 }
8538             }else{
8539                 w = s.width;  h = s.height;
8540             }
8541             var x = 0, y = 0, r = Math.round;
8542             switch((anchor || "tl").toLowerCase()){
8543                 case "c":
8544                     x = r(w*.5);
8545                     y = r(h*.5);
8546                 break;
8547                 case "t":
8548                     x = r(w*.5);
8549                     y = 0;
8550                 break;
8551                 case "l":
8552                     x = 0;
8553                     y = r(h*.5);
8554                 break;
8555                 case "r":
8556                     x = w;
8557                     y = r(h*.5);
8558                 break;
8559                 case "b":
8560                     x = r(w*.5);
8561                     y = h;
8562                 break;
8563                 case "tl":
8564                     x = 0;
8565                     y = 0;
8566                 break;
8567                 case "bl":
8568                     x = 0;
8569                     y = h;
8570                 break;
8571                 case "br":
8572                     x = w;
8573                     y = h;
8574                 break;
8575                 case "tr":
8576                     x = w;
8577                     y = 0;
8578                 break;
8579             }
8580             if(local === true){
8581                 return [x, y];
8582             }
8583             if(vp){
8584                 var sc = this.getScroll();
8585                 return [x + sc.left, y + sc.top];
8586             }
8587             //Add the element's offset xy
8588             var o = this.getXY();
8589             return [x+o[0], y+o[1]];
8590         },
8591
8592         /**
8593          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
8594          * supported position values.
8595          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8596          * @param {String} position The position to align to.
8597          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8598          * @return {Array} [x, y]
8599          */
8600         getAlignToXY : function(el, p, o)
8601         {
8602             el = Roo.get(el);
8603             var d = this.dom;
8604             if(!el.dom){
8605                 throw "Element.alignTo with an element that doesn't exist";
8606             }
8607             var c = false; //constrain to viewport
8608             var p1 = "", p2 = "";
8609             o = o || [0,0];
8610
8611             if(!p){
8612                 p = "tl-bl";
8613             }else if(p == "?"){
8614                 p = "tl-bl?";
8615             }else if(p.indexOf("-") == -1){
8616                 p = "tl-" + p;
8617             }
8618             p = p.toLowerCase();
8619             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
8620             if(!m){
8621                throw "Element.alignTo with an invalid alignment " + p;
8622             }
8623             p1 = m[1]; p2 = m[2]; c = !!m[3];
8624
8625             //Subtract the aligned el's internal xy from the target's offset xy
8626             //plus custom offset to get the aligned el's new offset xy
8627             var a1 = this.getAnchorXY(p1, true);
8628             var a2 = el.getAnchorXY(p2, false);
8629             var x = a2[0] - a1[0] + o[0];
8630             var y = a2[1] - a1[1] + o[1];
8631             if(c){
8632                 //constrain the aligned el to viewport if necessary
8633                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
8634                 // 5px of margin for ie
8635                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
8636
8637                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
8638                 //perpendicular to the vp border, allow the aligned el to slide on that border,
8639                 //otherwise swap the aligned el to the opposite border of the target.
8640                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
8641                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
8642                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t")  );
8643                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
8644
8645                var doc = document;
8646                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
8647                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
8648
8649                if((x+w) > dw + scrollX){
8650                     x = swapX ? r.left-w : dw+scrollX-w;
8651                 }
8652                if(x < scrollX){
8653                    x = swapX ? r.right : scrollX;
8654                }
8655                if((y+h) > dh + scrollY){
8656                     y = swapY ? r.top-h : dh+scrollY-h;
8657                 }
8658                if (y < scrollY){
8659                    y = swapY ? r.bottom : scrollY;
8660                }
8661             }
8662             return [x,y];
8663         },
8664
8665         // private
8666         getConstrainToXY : function(){
8667             var os = {top:0, left:0, bottom:0, right: 0};
8668
8669             return function(el, local, offsets, proposedXY){
8670                 el = Roo.get(el);
8671                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
8672
8673                 var vw, vh, vx = 0, vy = 0;
8674                 if(el.dom == document.body || el.dom == document){
8675                     vw = Roo.lib.Dom.getViewWidth();
8676                     vh = Roo.lib.Dom.getViewHeight();
8677                 }else{
8678                     vw = el.dom.clientWidth;
8679                     vh = el.dom.clientHeight;
8680                     if(!local){
8681                         var vxy = el.getXY();
8682                         vx = vxy[0];
8683                         vy = vxy[1];
8684                     }
8685                 }
8686
8687                 var s = el.getScroll();
8688
8689                 vx += offsets.left + s.left;
8690                 vy += offsets.top + s.top;
8691
8692                 vw -= offsets.right;
8693                 vh -= offsets.bottom;
8694
8695                 var vr = vx+vw;
8696                 var vb = vy+vh;
8697
8698                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
8699                 var x = xy[0], y = xy[1];
8700                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
8701
8702                 // only move it if it needs it
8703                 var moved = false;
8704
8705                 // first validate right/bottom
8706                 if((x + w) > vr){
8707                     x = vr - w;
8708                     moved = true;
8709                 }
8710                 if((y + h) > vb){
8711                     y = vb - h;
8712                     moved = true;
8713                 }
8714                 // then make sure top/left isn't negative
8715                 if(x < vx){
8716                     x = vx;
8717                     moved = true;
8718                 }
8719                 if(y < vy){
8720                     y = vy;
8721                     moved = true;
8722                 }
8723                 return moved ? [x, y] : false;
8724             };
8725         }(),
8726
8727         // private
8728         adjustForConstraints : function(xy, parent, offsets){
8729             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
8730         },
8731
8732         /**
8733          * Aligns this element with another element relative to the specified anchor points. If the other element is the
8734          * document it aligns it to the viewport.
8735          * The position parameter is optional, and can be specified in any one of the following formats:
8736          * <ul>
8737          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
8738          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
8739          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
8740          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
8741          *   <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
8742          *       element's anchor point, and the second value is used as the target's anchor point.</li>
8743          * </ul>
8744          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
8745          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
8746          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
8747          * that specified in order to enforce the viewport constraints.
8748          * Following are all of the supported anchor positions:
8749     <pre>
8750     Value  Description
8751     -----  -----------------------------
8752     tl     The top left corner (default)
8753     t      The center of the top edge
8754     tr     The top right corner
8755     l      The center of the left edge
8756     c      In the center of the element
8757     r      The center of the right edge
8758     bl     The bottom left corner
8759     b      The center of the bottom edge
8760     br     The bottom right corner
8761     </pre>
8762     Example Usage:
8763     <pre><code>
8764     // align el to other-el using the default positioning ("tl-bl", non-constrained)
8765     el.alignTo("other-el");
8766
8767     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
8768     el.alignTo("other-el", "tr?");
8769
8770     // align the bottom right corner of el with the center left edge of other-el
8771     el.alignTo("other-el", "br-l?");
8772
8773     // align the center of el with the bottom left corner of other-el and
8774     // adjust the x position by -6 pixels (and the y position by 0)
8775     el.alignTo("other-el", "c-bl", [-6, 0]);
8776     </code></pre>
8777          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8778          * @param {String} position The position to align to.
8779          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8780          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8781          * @return {Roo.Element} this
8782          */
8783         alignTo : function(element, position, offsets, animate){
8784             var xy = this.getAlignToXY(element, position, offsets);
8785             this.setXY(xy, this.preanim(arguments, 3));
8786             return this;
8787         },
8788
8789         /**
8790          * Anchors an element to another element and realigns it when the window is resized.
8791          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8792          * @param {String} position The position to align to.
8793          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8794          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
8795          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
8796          * is a number, it is used as the buffer delay (defaults to 50ms).
8797          * @param {Function} callback The function to call after the animation finishes
8798          * @return {Roo.Element} this
8799          */
8800         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
8801             var action = function(){
8802                 this.alignTo(el, alignment, offsets, animate);
8803                 Roo.callback(callback, this);
8804             };
8805             Roo.EventManager.onWindowResize(action, this);
8806             var tm = typeof monitorScroll;
8807             if(tm != 'undefined'){
8808                 Roo.EventManager.on(window, 'scroll', action, this,
8809                     {buffer: tm == 'number' ? monitorScroll : 50});
8810             }
8811             action.call(this); // align immediately
8812             return this;
8813         },
8814         /**
8815          * Clears any opacity settings from this element. Required in some cases for IE.
8816          * @return {Roo.Element} this
8817          */
8818         clearOpacity : function(){
8819             if (window.ActiveXObject) {
8820                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
8821                     this.dom.style.filter = "";
8822                 }
8823             } else {
8824                 this.dom.style.opacity = "";
8825                 this.dom.style["-moz-opacity"] = "";
8826                 this.dom.style["-khtml-opacity"] = "";
8827             }
8828             return this;
8829         },
8830
8831         /**
8832          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8833          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8834          * @return {Roo.Element} this
8835          */
8836         hide : function(animate){
8837             this.setVisible(false, this.preanim(arguments, 0));
8838             return this;
8839         },
8840
8841         /**
8842         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8843         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8844          * @return {Roo.Element} this
8845          */
8846         show : function(animate){
8847             this.setVisible(true, this.preanim(arguments, 0));
8848             return this;
8849         },
8850
8851         /**
8852          * @private Test if size has a unit, otherwise appends the default
8853          */
8854         addUnits : function(size){
8855             return Roo.Element.addUnits(size, this.defaultUnit);
8856         },
8857
8858         /**
8859          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
8860          * @return {Roo.Element} this
8861          */
8862         beginMeasure : function(){
8863             var el = this.dom;
8864             if(el.offsetWidth || el.offsetHeight){
8865                 return this; // offsets work already
8866             }
8867             var changed = [];
8868             var p = this.dom, b = document.body; // start with this element
8869             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
8870                 var pe = Roo.get(p);
8871                 if(pe.getStyle('display') == 'none'){
8872                     changed.push({el: p, visibility: pe.getStyle("visibility")});
8873                     p.style.visibility = "hidden";
8874                     p.style.display = "block";
8875                 }
8876                 p = p.parentNode;
8877             }
8878             this._measureChanged = changed;
8879             return this;
8880
8881         },
8882
8883         /**
8884          * Restores displays to before beginMeasure was called
8885          * @return {Roo.Element} this
8886          */
8887         endMeasure : function(){
8888             var changed = this._measureChanged;
8889             if(changed){
8890                 for(var i = 0, len = changed.length; i < len; i++) {
8891                     var r = changed[i];
8892                     r.el.style.visibility = r.visibility;
8893                     r.el.style.display = "none";
8894                 }
8895                 this._measureChanged = null;
8896             }
8897             return this;
8898         },
8899
8900         /**
8901         * Update the innerHTML of this element, optionally searching for and processing scripts
8902         * @param {String} html The new HTML
8903         * @param {Boolean} loadScripts (optional) true to look for and process scripts
8904         * @param {Function} callback For async script loading you can be noticed when the update completes
8905         * @return {Roo.Element} this
8906          */
8907         update : function(html, loadScripts, callback){
8908             if(typeof html == "undefined"){
8909                 html = "";
8910             }
8911             if(loadScripts !== true){
8912                 this.dom.innerHTML = html;
8913                 if(typeof callback == "function"){
8914                     callback();
8915                 }
8916                 return this;
8917             }
8918             var id = Roo.id();
8919             var dom = this.dom;
8920
8921             html += '<span id="' + id + '"></span>';
8922
8923             E.onAvailable(id, function(){
8924                 var hd = document.getElementsByTagName("head")[0];
8925                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
8926                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
8927                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
8928
8929                 var match;
8930                 while(match = re.exec(html)){
8931                     var attrs = match[1];
8932                     var srcMatch = attrs ? attrs.match(srcRe) : false;
8933                     if(srcMatch && srcMatch[2]){
8934                        var s = document.createElement("script");
8935                        s.src = srcMatch[2];
8936                        var typeMatch = attrs.match(typeRe);
8937                        if(typeMatch && typeMatch[2]){
8938                            s.type = typeMatch[2];
8939                        }
8940                        hd.appendChild(s);
8941                     }else if(match[2] && match[2].length > 0){
8942                         if(window.execScript) {
8943                            window.execScript(match[2]);
8944                         } else {
8945                             /**
8946                              * eval:var:id
8947                              * eval:var:dom
8948                              * eval:var:html
8949                              * 
8950                              */
8951                            window.eval(match[2]);
8952                         }
8953                     }
8954                 }
8955                 var el = document.getElementById(id);
8956                 if(el){el.parentNode.removeChild(el);}
8957                 if(typeof callback == "function"){
8958                     callback();
8959                 }
8960             });
8961             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
8962             return this;
8963         },
8964
8965         /**
8966          * Direct access to the UpdateManager update() method (takes the same parameters).
8967          * @param {String/Function} url The url for this request or a function to call to get the url
8968          * @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}
8969          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
8970          * @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.
8971          * @return {Roo.Element} this
8972          */
8973         load : function(){
8974             var um = this.getUpdateManager();
8975             um.update.apply(um, arguments);
8976             return this;
8977         },
8978
8979         /**
8980         * Gets this element's UpdateManager
8981         * @return {Roo.UpdateManager} The UpdateManager
8982         */
8983         getUpdateManager : function(){
8984             if(!this.updateManager){
8985                 this.updateManager = new Roo.UpdateManager(this);
8986             }
8987             return this.updateManager;
8988         },
8989
8990         /**
8991          * Disables text selection for this element (normalized across browsers)
8992          * @return {Roo.Element} this
8993          */
8994         unselectable : function(){
8995             this.dom.unselectable = "on";
8996             this.swallowEvent("selectstart", true);
8997             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
8998             this.addClass("x-unselectable");
8999             return this;
9000         },
9001
9002         /**
9003         * Calculates the x, y to center this element on the screen
9004         * @return {Array} The x, y values [x, y]
9005         */
9006         getCenterXY : function(){
9007             return this.getAlignToXY(document, 'c-c');
9008         },
9009
9010         /**
9011         * Centers the Element in either the viewport, or another Element.
9012         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
9013         */
9014         center : function(centerIn){
9015             this.alignTo(centerIn || document, 'c-c');
9016             return this;
9017         },
9018
9019         /**
9020          * Tests various css rules/browsers to determine if this element uses a border box
9021          * @return {Boolean}
9022          */
9023         isBorderBox : function(){
9024             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
9025         },
9026
9027         /**
9028          * Return a box {x, y, width, height} that can be used to set another elements
9029          * size/location to match this element.
9030          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
9031          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
9032          * @return {Object} box An object in the format {x, y, width, height}
9033          */
9034         getBox : function(contentBox, local){
9035             var xy;
9036             if(!local){
9037                 xy = this.getXY();
9038             }else{
9039                 var left = parseInt(this.getStyle("left"), 10) || 0;
9040                 var top = parseInt(this.getStyle("top"), 10) || 0;
9041                 xy = [left, top];
9042             }
9043             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
9044             if(!contentBox){
9045                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
9046             }else{
9047                 var l = this.getBorderWidth("l")+this.getPadding("l");
9048                 var r = this.getBorderWidth("r")+this.getPadding("r");
9049                 var t = this.getBorderWidth("t")+this.getPadding("t");
9050                 var b = this.getBorderWidth("b")+this.getPadding("b");
9051                 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)};
9052             }
9053             bx.right = bx.x + bx.width;
9054             bx.bottom = bx.y + bx.height;
9055             return bx;
9056         },
9057
9058         /**
9059          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
9060          for more information about the sides.
9061          * @param {String} sides
9062          * @return {Number}
9063          */
9064         getFrameWidth : function(sides, onlyContentBox){
9065             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
9066         },
9067
9068         /**
9069          * 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.
9070          * @param {Object} box The box to fill {x, y, width, height}
9071          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
9072          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9073          * @return {Roo.Element} this
9074          */
9075         setBox : function(box, adjust, animate){
9076             var w = box.width, h = box.height;
9077             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
9078                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
9079                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
9080             }
9081             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
9082             return this;
9083         },
9084
9085         /**
9086          * Forces the browser to repaint this element
9087          * @return {Roo.Element} this
9088          */
9089          repaint : function(){
9090             var dom = this.dom;
9091             this.addClass("x-repaint");
9092             setTimeout(function(){
9093                 Roo.get(dom).removeClass("x-repaint");
9094             }, 1);
9095             return this;
9096         },
9097
9098         /**
9099          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
9100          * then it returns the calculated width of the sides (see getPadding)
9101          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
9102          * @return {Object/Number}
9103          */
9104         getMargins : function(side){
9105             if(!side){
9106                 return {
9107                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
9108                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
9109                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
9110                     right: parseInt(this.getStyle("margin-right"), 10) || 0
9111                 };
9112             }else{
9113                 return this.addStyles(side, El.margins);
9114              }
9115         },
9116
9117         // private
9118         addStyles : function(sides, styles){
9119             var val = 0, v, w;
9120             for(var i = 0, len = sides.length; i < len; i++){
9121                 v = this.getStyle(styles[sides.charAt(i)]);
9122                 if(v){
9123                      w = parseInt(v, 10);
9124                      if(w){ val += w; }
9125                 }
9126             }
9127             return val;
9128         },
9129
9130         /**
9131          * Creates a proxy element of this element
9132          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
9133          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
9134          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
9135          * @return {Roo.Element} The new proxy element
9136          */
9137         createProxy : function(config, renderTo, matchBox){
9138             if(renderTo){
9139                 renderTo = Roo.getDom(renderTo);
9140             }else{
9141                 renderTo = document.body;
9142             }
9143             config = typeof config == "object" ?
9144                 config : {tag : "div", cls: config};
9145             var proxy = Roo.DomHelper.append(renderTo, config, true);
9146             if(matchBox){
9147                proxy.setBox(this.getBox());
9148             }
9149             return proxy;
9150         },
9151
9152         /**
9153          * Puts a mask over this element to disable user interaction. Requires core.css.
9154          * This method can only be applied to elements which accept child nodes.
9155          * @param {String} msg (optional) A message to display in the mask
9156          * @param {String} msgCls (optional) A css class to apply to the msg element
9157          * @return {Element} The mask  element
9158          */
9159         mask : function(msg, msgCls)
9160         {
9161             if(this.getStyle("position") == "static" && this.dom.tagName !== 'BODY'){
9162                 this.setStyle("position", "relative");
9163             }
9164             if(!this._mask){
9165                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
9166             }
9167             
9168             this.addClass("x-masked");
9169             this._mask.setDisplayed(true);
9170             
9171             // we wander
9172             var z = 0;
9173             var dom = this.dom;
9174             while (dom && dom.style) {
9175                 if (!isNaN(parseInt(dom.style.zIndex))) {
9176                     z = Math.max(z, parseInt(dom.style.zIndex));
9177                 }
9178                 dom = dom.parentNode;
9179             }
9180             // if we are masking the body - then it hides everything..
9181             if (this.dom == document.body) {
9182                 z = 1000000;
9183                 this._mask.setWidth(Roo.lib.Dom.getDocumentWidth());
9184                 this._mask.setHeight(Roo.lib.Dom.getDocumentHeight());
9185             }
9186            
9187             if(typeof msg == 'string'){
9188                 if(!this._maskMsg){
9189                     this._maskMsg = Roo.DomHelper.append(this.dom, {
9190                         cls: "roo-el-mask-msg", 
9191                         cn: [
9192                             {
9193                                 tag: 'i',
9194                                 cls: 'fa fa-spinner fa-spin'
9195                             },
9196                             {
9197                                 tag: 'div'
9198                             }   
9199                         ]
9200                     }, true);
9201                 }
9202                 var mm = this._maskMsg;
9203                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
9204                 if (mm.dom.lastChild) { // weird IE issue?
9205                     mm.dom.lastChild.innerHTML = msg;
9206                 }
9207                 mm.setDisplayed(true);
9208                 mm.center(this);
9209                 mm.setStyle('z-index', z + 102);
9210             }
9211             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
9212                 this._mask.setHeight(this.getHeight());
9213             }
9214             this._mask.setStyle('z-index', z + 100);
9215             
9216             return this._mask;
9217         },
9218
9219         /**
9220          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
9221          * it is cached for reuse.
9222          */
9223         unmask : function(removeEl){
9224             if(this._mask){
9225                 if(removeEl === true){
9226                     this._mask.remove();
9227                     delete this._mask;
9228                     if(this._maskMsg){
9229                         this._maskMsg.remove();
9230                         delete this._maskMsg;
9231                     }
9232                 }else{
9233                     this._mask.setDisplayed(false);
9234                     if(this._maskMsg){
9235                         this._maskMsg.setDisplayed(false);
9236                     }
9237                 }
9238             }
9239             this.removeClass("x-masked");
9240         },
9241
9242         /**
9243          * Returns true if this element is masked
9244          * @return {Boolean}
9245          */
9246         isMasked : function(){
9247             return this._mask && this._mask.isVisible();
9248         },
9249
9250         /**
9251          * Creates an iframe shim for this element to keep selects and other windowed objects from
9252          * showing through.
9253          * @return {Roo.Element} The new shim element
9254          */
9255         createShim : function(){
9256             var el = document.createElement('iframe');
9257             el.frameBorder = 'no';
9258             el.className = 'roo-shim';
9259             if(Roo.isIE && Roo.isSecure){
9260                 el.src = Roo.SSL_SECURE_URL;
9261             }
9262             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
9263             shim.autoBoxAdjust = false;
9264             return shim;
9265         },
9266
9267         /**
9268          * Removes this element from the DOM and deletes it from the cache
9269          */
9270         remove : function(){
9271             if(this.dom.parentNode){
9272                 this.dom.parentNode.removeChild(this.dom);
9273             }
9274             delete El.cache[this.dom.id];
9275         },
9276
9277         /**
9278          * Sets up event handlers to add and remove a css class when the mouse is over this element
9279          * @param {String} className
9280          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
9281          * mouseout events for children elements
9282          * @return {Roo.Element} this
9283          */
9284         addClassOnOver : function(className, preventFlicker){
9285             this.on("mouseover", function(){
9286                 Roo.fly(this, '_internal').addClass(className);
9287             }, this.dom);
9288             var removeFn = function(e){
9289                 if(preventFlicker !== true || !e.within(this, true)){
9290                     Roo.fly(this, '_internal').removeClass(className);
9291                 }
9292             };
9293             this.on("mouseout", removeFn, this.dom);
9294             return this;
9295         },
9296
9297         /**
9298          * Sets up event handlers to add and remove a css class when this element has the focus
9299          * @param {String} className
9300          * @return {Roo.Element} this
9301          */
9302         addClassOnFocus : function(className){
9303             this.on("focus", function(){
9304                 Roo.fly(this, '_internal').addClass(className);
9305             }, this.dom);
9306             this.on("blur", function(){
9307                 Roo.fly(this, '_internal').removeClass(className);
9308             }, this.dom);
9309             return this;
9310         },
9311         /**
9312          * 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)
9313          * @param {String} className
9314          * @return {Roo.Element} this
9315          */
9316         addClassOnClick : function(className){
9317             var dom = this.dom;
9318             this.on("mousedown", function(){
9319                 Roo.fly(dom, '_internal').addClass(className);
9320                 var d = Roo.get(document);
9321                 var fn = function(){
9322                     Roo.fly(dom, '_internal').removeClass(className);
9323                     d.removeListener("mouseup", fn);
9324                 };
9325                 d.on("mouseup", fn);
9326             });
9327             return this;
9328         },
9329
9330         /**
9331          * Stops the specified event from bubbling and optionally prevents the default action
9332          * @param {String} eventName
9333          * @param {Boolean} preventDefault (optional) true to prevent the default action too
9334          * @return {Roo.Element} this
9335          */
9336         swallowEvent : function(eventName, preventDefault){
9337             var fn = function(e){
9338                 e.stopPropagation();
9339                 if(preventDefault){
9340                     e.preventDefault();
9341                 }
9342             };
9343             if(eventName instanceof Array){
9344                 for(var i = 0, len = eventName.length; i < len; i++){
9345                      this.on(eventName[i], fn);
9346                 }
9347                 return this;
9348             }
9349             this.on(eventName, fn);
9350             return this;
9351         },
9352
9353         /**
9354          * @private
9355          */
9356       fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
9357
9358         /**
9359          * Sizes this element to its parent element's dimensions performing
9360          * neccessary box adjustments.
9361          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
9362          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
9363          * @return {Roo.Element} this
9364          */
9365         fitToParent : function(monitorResize, targetParent) {
9366           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
9367           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
9368           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
9369             return;
9370           }
9371           var p = Roo.get(targetParent || this.dom.parentNode);
9372           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
9373           if (monitorResize === true) {
9374             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
9375             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
9376           }
9377           return this;
9378         },
9379
9380         /**
9381          * Gets the next sibling, skipping text nodes
9382          * @return {HTMLElement} The next sibling or null
9383          */
9384         getNextSibling : function(){
9385             var n = this.dom.nextSibling;
9386             while(n && n.nodeType != 1){
9387                 n = n.nextSibling;
9388             }
9389             return n;
9390         },
9391
9392         /**
9393          * Gets the previous sibling, skipping text nodes
9394          * @return {HTMLElement} The previous sibling or null
9395          */
9396         getPrevSibling : function(){
9397             var n = this.dom.previousSibling;
9398             while(n && n.nodeType != 1){
9399                 n = n.previousSibling;
9400             }
9401             return n;
9402         },
9403
9404
9405         /**
9406          * Appends the passed element(s) to this element
9407          * @param {String/HTMLElement/Array/Element/CompositeElement} el
9408          * @return {Roo.Element} this
9409          */
9410         appendChild: function(el){
9411             el = Roo.get(el);
9412             el.appendTo(this);
9413             return this;
9414         },
9415
9416         /**
9417          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
9418          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
9419          * automatically generated with the specified attributes.
9420          * @param {HTMLElement} insertBefore (optional) a child element of this element
9421          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
9422          * @return {Roo.Element} The new child element
9423          */
9424         createChild: function(config, insertBefore, returnDom){
9425             config = config || {tag:'div'};
9426             if(insertBefore){
9427                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
9428             }
9429             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
9430         },
9431
9432         /**
9433          * Appends this element to the passed element
9434          * @param {String/HTMLElement/Element} el The new parent element
9435          * @return {Roo.Element} this
9436          */
9437         appendTo: function(el){
9438             el = Roo.getDom(el);
9439             el.appendChild(this.dom);
9440             return this;
9441         },
9442
9443         /**
9444          * Inserts this element before the passed element in the DOM
9445          * @param {String/HTMLElement/Element} el The element to insert before
9446          * @return {Roo.Element} this
9447          */
9448         insertBefore: function(el){
9449             el = Roo.getDom(el);
9450             el.parentNode.insertBefore(this.dom, el);
9451             return this;
9452         },
9453
9454         /**
9455          * Inserts this element after the passed element in the DOM
9456          * @param {String/HTMLElement/Element} el The element to insert after
9457          * @return {Roo.Element} this
9458          */
9459         insertAfter: function(el){
9460             el = Roo.getDom(el);
9461             el.parentNode.insertBefore(this.dom, el.nextSibling);
9462             return this;
9463         },
9464
9465         /**
9466          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
9467          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9468          * @return {Roo.Element} The new child
9469          */
9470         insertFirst: function(el, returnDom){
9471             el = el || {};
9472             if(typeof el == 'object' && !el.nodeType){ // dh config
9473                 return this.createChild(el, this.dom.firstChild, returnDom);
9474             }else{
9475                 el = Roo.getDom(el);
9476                 this.dom.insertBefore(el, this.dom.firstChild);
9477                 return !returnDom ? Roo.get(el) : el;
9478             }
9479         },
9480
9481         /**
9482          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
9483          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9484          * @param {String} where (optional) 'before' or 'after' defaults to before
9485          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9486          * @return {Roo.Element} the inserted Element
9487          */
9488         insertSibling: function(el, where, returnDom){
9489             where = where ? where.toLowerCase() : 'before';
9490             el = el || {};
9491             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
9492
9493             if(typeof el == 'object' && !el.nodeType){ // dh config
9494                 if(where == 'after' && !this.dom.nextSibling){
9495                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
9496                 }else{
9497                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
9498                 }
9499
9500             }else{
9501                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
9502                             where == 'before' ? this.dom : this.dom.nextSibling);
9503                 if(!returnDom){
9504                     rt = Roo.get(rt);
9505                 }
9506             }
9507             return rt;
9508         },
9509
9510         /**
9511          * Creates and wraps this element with another element
9512          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
9513          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9514          * @return {HTMLElement/Element} The newly created wrapper element
9515          */
9516         wrap: function(config, returnDom){
9517             if(!config){
9518                 config = {tag: "div"};
9519             }
9520             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
9521             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
9522             return newEl;
9523         },
9524
9525         /**
9526          * Replaces the passed element with this element
9527          * @param {String/HTMLElement/Element} el The element to replace
9528          * @return {Roo.Element} this
9529          */
9530         replace: function(el){
9531             el = Roo.get(el);
9532             this.insertBefore(el);
9533             el.remove();
9534             return this;
9535         },
9536
9537         /**
9538          * Inserts an html fragment into this element
9539          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
9540          * @param {String} html The HTML fragment
9541          * @param {Boolean} returnEl True to return an Roo.Element
9542          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
9543          */
9544         insertHtml : function(where, html, returnEl){
9545             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
9546             return returnEl ? Roo.get(el) : el;
9547         },
9548
9549         /**
9550          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
9551          * @param {Object} o The object with the attributes
9552          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
9553          * @return {Roo.Element} this
9554          */
9555         set : function(o, useSet){
9556             var el = this.dom;
9557             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
9558             for(var attr in o){
9559                 if(attr == "style" || typeof o[attr] == "function")  { continue; }
9560                 if(attr=="cls"){
9561                     el.className = o["cls"];
9562                 }else{
9563                     if(useSet) {
9564                         el.setAttribute(attr, o[attr]);
9565                     } else {
9566                         el[attr] = o[attr];
9567                     }
9568                 }
9569             }
9570             if(o.style){
9571                 Roo.DomHelper.applyStyles(el, o.style);
9572             }
9573             return this;
9574         },
9575
9576         /**
9577          * Convenience method for constructing a KeyMap
9578          * @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:
9579          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9580          * @param {Function} fn The function to call
9581          * @param {Object} scope (optional) The scope of the function
9582          * @return {Roo.KeyMap} The KeyMap created
9583          */
9584         addKeyListener : function(key, fn, scope){
9585             var config;
9586             if(typeof key != "object" || key instanceof Array){
9587                 config = {
9588                     key: key,
9589                     fn: fn,
9590                     scope: scope
9591                 };
9592             }else{
9593                 config = {
9594                     key : key.key,
9595                     shift : key.shift,
9596                     ctrl : key.ctrl,
9597                     alt : key.alt,
9598                     fn: fn,
9599                     scope: scope
9600                 };
9601             }
9602             return new Roo.KeyMap(this, config);
9603         },
9604
9605         /**
9606          * Creates a KeyMap for this element
9607          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
9608          * @return {Roo.KeyMap} The KeyMap created
9609          */
9610         addKeyMap : function(config){
9611             return new Roo.KeyMap(this, config);
9612         },
9613
9614         /**
9615          * Returns true if this element is scrollable.
9616          * @return {Boolean}
9617          */
9618          isScrollable : function(){
9619             var dom = this.dom;
9620             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
9621         },
9622
9623         /**
9624          * 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().
9625          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
9626          * @param {Number} value The new scroll value
9627          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9628          * @return {Element} this
9629          */
9630
9631         scrollTo : function(side, value, animate){
9632             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
9633             if(!animate || !A){
9634                 this.dom[prop] = value;
9635             }else{
9636                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
9637                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
9638             }
9639             return this;
9640         },
9641
9642         /**
9643          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
9644          * within this element's scrollable range.
9645          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9646          * @param {Number} distance How far to scroll the element in pixels
9647          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9648          * @return {Boolean} Returns true if a scroll was triggered or false if the element
9649          * was scrolled as far as it could go.
9650          */
9651          scroll : function(direction, distance, animate){
9652              if(!this.isScrollable()){
9653                  return;
9654              }
9655              var el = this.dom;
9656              var l = el.scrollLeft, t = el.scrollTop;
9657              var w = el.scrollWidth, h = el.scrollHeight;
9658              var cw = el.clientWidth, ch = el.clientHeight;
9659              direction = direction.toLowerCase();
9660              var scrolled = false;
9661              var a = this.preanim(arguments, 2);
9662              switch(direction){
9663                  case "l":
9664                  case "left":
9665                      if(w - l > cw){
9666                          var v = Math.min(l + distance, w-cw);
9667                          this.scrollTo("left", v, a);
9668                          scrolled = true;
9669                      }
9670                      break;
9671                 case "r":
9672                 case "right":
9673                      if(l > 0){
9674                          var v = Math.max(l - distance, 0);
9675                          this.scrollTo("left", v, a);
9676                          scrolled = true;
9677                      }
9678                      break;
9679                 case "t":
9680                 case "top":
9681                 case "up":
9682                      if(t > 0){
9683                          var v = Math.max(t - distance, 0);
9684                          this.scrollTo("top", v, a);
9685                          scrolled = true;
9686                      }
9687                      break;
9688                 case "b":
9689                 case "bottom":
9690                 case "down":
9691                      if(h - t > ch){
9692                          var v = Math.min(t + distance, h-ch);
9693                          this.scrollTo("top", v, a);
9694                          scrolled = true;
9695                      }
9696                      break;
9697              }
9698              return scrolled;
9699         },
9700
9701         /**
9702          * Translates the passed page coordinates into left/top css values for this element
9703          * @param {Number/Array} x The page x or an array containing [x, y]
9704          * @param {Number} y The page y
9705          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
9706          */
9707         translatePoints : function(x, y){
9708             if(typeof x == 'object' || x instanceof Array){
9709                 y = x[1]; x = x[0];
9710             }
9711             var p = this.getStyle('position');
9712             var o = this.getXY();
9713
9714             var l = parseInt(this.getStyle('left'), 10);
9715             var t = parseInt(this.getStyle('top'), 10);
9716
9717             if(isNaN(l)){
9718                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
9719             }
9720             if(isNaN(t)){
9721                 t = (p == "relative") ? 0 : this.dom.offsetTop;
9722             }
9723
9724             return {left: (x - o[0] + l), top: (y - o[1] + t)};
9725         },
9726
9727         /**
9728          * Returns the current scroll position of the element.
9729          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
9730          */
9731         getScroll : function(){
9732             var d = this.dom, doc = document;
9733             if(d == doc || d == doc.body){
9734                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
9735                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
9736                 return {left: l, top: t};
9737             }else{
9738                 return {left: d.scrollLeft, top: d.scrollTop};
9739             }
9740         },
9741
9742         /**
9743          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
9744          * are convert to standard 6 digit hex color.
9745          * @param {String} attr The css attribute
9746          * @param {String} defaultValue The default value to use when a valid color isn't found
9747          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
9748          * YUI color anims.
9749          */
9750         getColor : function(attr, defaultValue, prefix){
9751             var v = this.getStyle(attr);
9752             if(!v || v == "transparent" || v == "inherit") {
9753                 return defaultValue;
9754             }
9755             var color = typeof prefix == "undefined" ? "#" : prefix;
9756             if(v.substr(0, 4) == "rgb("){
9757                 var rvs = v.slice(4, v.length -1).split(",");
9758                 for(var i = 0; i < 3; i++){
9759                     var h = parseInt(rvs[i]).toString(16);
9760                     if(h < 16){
9761                         h = "0" + h;
9762                     }
9763                     color += h;
9764                 }
9765             } else {
9766                 if(v.substr(0, 1) == "#"){
9767                     if(v.length == 4) {
9768                         for(var i = 1; i < 4; i++){
9769                             var c = v.charAt(i);
9770                             color +=  c + c;
9771                         }
9772                     }else if(v.length == 7){
9773                         color += v.substr(1);
9774                     }
9775                 }
9776             }
9777             return(color.length > 5 ? color.toLowerCase() : defaultValue);
9778         },
9779
9780         /**
9781          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
9782          * gradient background, rounded corners and a 4-way shadow.
9783          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
9784          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
9785          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
9786          * @return {Roo.Element} this
9787          */
9788         boxWrap : function(cls){
9789             cls = cls || 'x-box';
9790             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
9791             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
9792             return el;
9793         },
9794
9795         /**
9796          * Returns the value of a namespaced attribute from the element's underlying DOM node.
9797          * @param {String} namespace The namespace in which to look for the attribute
9798          * @param {String} name The attribute name
9799          * @return {String} The attribute value
9800          */
9801         getAttributeNS : Roo.isIE ? function(ns, name){
9802             var d = this.dom;
9803             var type = typeof d[ns+":"+name];
9804             if(type != 'undefined' && type != 'unknown'){
9805                 return d[ns+":"+name];
9806             }
9807             return d[name];
9808         } : function(ns, name){
9809             var d = this.dom;
9810             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
9811         },
9812         
9813         
9814         /**
9815          * Sets or Returns the value the dom attribute value
9816          * @param {String|Object} name The attribute name (or object to set multiple attributes)
9817          * @param {String} value (optional) The value to set the attribute to
9818          * @return {String} The attribute value
9819          */
9820         attr : function(name){
9821             if (arguments.length > 1) {
9822                 this.dom.setAttribute(name, arguments[1]);
9823                 return arguments[1];
9824             }
9825             if (typeof(name) == 'object') {
9826                 for(var i in name) {
9827                     this.attr(i, name[i]);
9828                 }
9829                 return name;
9830             }
9831             
9832             
9833             if (!this.dom.hasAttribute(name)) {
9834                 return undefined;
9835             }
9836             return this.dom.getAttribute(name);
9837         }
9838         
9839         
9840         
9841     };
9842
9843     var ep = El.prototype;
9844
9845     /**
9846      * Appends an event handler (Shorthand for addListener)
9847      * @param {String}   eventName     The type of event to append
9848      * @param {Function} fn        The method the event invokes
9849      * @param {Object} scope       (optional) The scope (this object) of the fn
9850      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9851      * @method
9852      */
9853     ep.on = ep.addListener;
9854         // backwards compat
9855     ep.mon = ep.addListener;
9856
9857     /**
9858      * Removes an event handler from this element (shorthand for removeListener)
9859      * @param {String} eventName the type of event to remove
9860      * @param {Function} fn the method the event invokes
9861      * @return {Roo.Element} this
9862      * @method
9863      */
9864     ep.un = ep.removeListener;
9865
9866     /**
9867      * true to automatically adjust width and height settings for box-model issues (default to true)
9868      */
9869     ep.autoBoxAdjust = true;
9870
9871     // private
9872     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
9873
9874     // private
9875     El.addUnits = function(v, defaultUnit){
9876         if(v === "" || v == "auto"){
9877             return v;
9878         }
9879         if(v === undefined){
9880             return '';
9881         }
9882         if(typeof v == "number" || !El.unitPattern.test(v)){
9883             return v + (defaultUnit || 'px');
9884         }
9885         return v;
9886     };
9887
9888     // special markup used throughout Roo when box wrapping elements
9889     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>';
9890     /**
9891      * Visibility mode constant - Use visibility to hide element
9892      * @static
9893      * @type Number
9894      */
9895     El.VISIBILITY = 1;
9896     /**
9897      * Visibility mode constant - Use display to hide element
9898      * @static
9899      * @type Number
9900      */
9901     El.DISPLAY = 2;
9902
9903     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
9904     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
9905     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
9906
9907
9908
9909     /**
9910      * @private
9911      */
9912     El.cache = {};
9913
9914     var docEl;
9915
9916     /**
9917      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9918      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9919      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9920      * @return {Element} The Element object
9921      * @static
9922      */
9923     El.get = function(el){
9924         var ex, elm, id;
9925         if(!el){ return null; }
9926         if(typeof el == "string"){ // element id
9927             if(!(elm = document.getElementById(el))){
9928                 return null;
9929             }
9930             if(ex = El.cache[el]){
9931                 ex.dom = elm;
9932             }else{
9933                 ex = El.cache[el] = new El(elm);
9934             }
9935             return ex;
9936         }else if(el.tagName){ // dom element
9937             if(!(id = el.id)){
9938                 id = Roo.id(el);
9939             }
9940             if(ex = El.cache[id]){
9941                 ex.dom = el;
9942             }else{
9943                 ex = El.cache[id] = new El(el);
9944             }
9945             return ex;
9946         }else if(el instanceof El){
9947             if(el != docEl){
9948                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
9949                                                               // catch case where it hasn't been appended
9950                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
9951             }
9952             return el;
9953         }else if(el.isComposite){
9954             return el;
9955         }else if(el instanceof Array){
9956             return El.select(el);
9957         }else if(el == document){
9958             // create a bogus element object representing the document object
9959             if(!docEl){
9960                 var f = function(){};
9961                 f.prototype = El.prototype;
9962                 docEl = new f();
9963                 docEl.dom = document;
9964             }
9965             return docEl;
9966         }
9967         return null;
9968     };
9969
9970     // private
9971     El.uncache = function(el){
9972         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
9973             if(a[i]){
9974                 delete El.cache[a[i].id || a[i]];
9975             }
9976         }
9977     };
9978
9979     // private
9980     // Garbage collection - uncache elements/purge listeners on orphaned elements
9981     // so we don't hold a reference and cause the browser to retain them
9982     El.garbageCollect = function(){
9983         if(!Roo.enableGarbageCollector){
9984             clearInterval(El.collectorThread);
9985             return;
9986         }
9987         for(var eid in El.cache){
9988             var el = El.cache[eid], d = el.dom;
9989             // -------------------------------------------------------
9990             // Determining what is garbage:
9991             // -------------------------------------------------------
9992             // !d
9993             // dom node is null, definitely garbage
9994             // -------------------------------------------------------
9995             // !d.parentNode
9996             // no parentNode == direct orphan, definitely garbage
9997             // -------------------------------------------------------
9998             // !d.offsetParent && !document.getElementById(eid)
9999             // display none elements have no offsetParent so we will
10000             // also try to look it up by it's id. However, check
10001             // offsetParent first so we don't do unneeded lookups.
10002             // This enables collection of elements that are not orphans
10003             // directly, but somewhere up the line they have an orphan
10004             // parent.
10005             // -------------------------------------------------------
10006             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
10007                 delete El.cache[eid];
10008                 if(d && Roo.enableListenerCollection){
10009                     E.purgeElement(d);
10010                 }
10011             }
10012         }
10013     }
10014     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
10015
10016
10017     // dom is optional
10018     El.Flyweight = function(dom){
10019         this.dom = dom;
10020     };
10021     El.Flyweight.prototype = El.prototype;
10022
10023     El._flyweights = {};
10024     /**
10025      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
10026      * the dom node can be overwritten by other code.
10027      * @param {String/HTMLElement} el The dom node or id
10028      * @param {String} named (optional) Allows for creation of named reusable flyweights to
10029      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
10030      * @static
10031      * @return {Element} The shared Element object
10032      */
10033     El.fly = function(el, named){
10034         named = named || '_global';
10035         el = Roo.getDom(el);
10036         if(!el){
10037             return null;
10038         }
10039         if(!El._flyweights[named]){
10040             El._flyweights[named] = new El.Flyweight();
10041         }
10042         El._flyweights[named].dom = el;
10043         return El._flyweights[named];
10044     };
10045
10046     /**
10047      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
10048      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
10049      * Shorthand of {@link Roo.Element#get}
10050      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
10051      * @return {Element} The Element object
10052      * @member Roo
10053      * @method get
10054      */
10055     Roo.get = El.get;
10056     /**
10057      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
10058      * the dom node can be overwritten by other code.
10059      * Shorthand of {@link Roo.Element#fly}
10060      * @param {String/HTMLElement} el The dom node or id
10061      * @param {String} named (optional) Allows for creation of named reusable flyweights to
10062      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
10063      * @static
10064      * @return {Element} The shared Element object
10065      * @member Roo
10066      * @method fly
10067      */
10068     Roo.fly = El.fly;
10069
10070     // speedy lookup for elements never to box adjust
10071     var noBoxAdjust = Roo.isStrict ? {
10072         select:1
10073     } : {
10074         input:1, select:1, textarea:1
10075     };
10076     if(Roo.isIE || Roo.isGecko){
10077         noBoxAdjust['button'] = 1;
10078     }
10079
10080
10081     Roo.EventManager.on(window, 'unload', function(){
10082         delete El.cache;
10083         delete El._flyweights;
10084     });
10085 })();
10086
10087
10088
10089
10090 if(Roo.DomQuery){
10091     Roo.Element.selectorFunction = Roo.DomQuery.select;
10092 }
10093
10094 Roo.Element.select = function(selector, unique, root){
10095     var els;
10096     if(typeof selector == "string"){
10097         els = Roo.Element.selectorFunction(selector, root);
10098     }else if(selector.length !== undefined){
10099         els = selector;
10100     }else{
10101         throw "Invalid selector";
10102     }
10103     if(unique === true){
10104         return new Roo.CompositeElement(els);
10105     }else{
10106         return new Roo.CompositeElementLite(els);
10107     }
10108 };
10109 /**
10110  * Selects elements based on the passed CSS selector to enable working on them as 1.
10111  * @param {String/Array} selector The CSS selector or an array of elements
10112  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
10113  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
10114  * @return {CompositeElementLite/CompositeElement}
10115  * @member Roo
10116  * @method select
10117  */
10118 Roo.select = Roo.Element.select;
10119
10120
10121
10122
10123
10124
10125
10126
10127
10128
10129
10130
10131
10132
10133 /*
10134  * Based on:
10135  * Ext JS Library 1.1.1
10136  * Copyright(c) 2006-2007, Ext JS, LLC.
10137  *
10138  * Originally Released Under LGPL - original licence link has changed is not relivant.
10139  *
10140  * Fork - LGPL
10141  * <script type="text/javascript">
10142  */
10143
10144
10145
10146 //Notifies Element that fx methods are available
10147 Roo.enableFx = true;
10148
10149 /**
10150  * @class Roo.Fx
10151  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
10152  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
10153  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
10154  * Element effects to work.</p><br/>
10155  *
10156  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
10157  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
10158  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
10159  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
10160  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
10161  * expected results and should be done with care.</p><br/>
10162  *
10163  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
10164  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
10165 <pre>
10166 Value  Description
10167 -----  -----------------------------
10168 tl     The top left corner
10169 t      The center of the top edge
10170 tr     The top right corner
10171 l      The center of the left edge
10172 r      The center of the right edge
10173 bl     The bottom left corner
10174 b      The center of the bottom edge
10175 br     The bottom right corner
10176 </pre>
10177  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
10178  * below are common options that can be passed to any Fx method.</b>
10179  * @cfg {Function} callback A function called when the effect is finished
10180  * @cfg {Object} scope The scope of the effect function
10181  * @cfg {String} easing A valid Easing value for the effect
10182  * @cfg {String} afterCls A css class to apply after the effect
10183  * @cfg {Number} duration The length of time (in seconds) that the effect should last
10184  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
10185  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
10186  * effects that end with the element being visually hidden, ignored otherwise)
10187  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
10188  * a function which returns such a specification that will be applied to the Element after the effect finishes
10189  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
10190  * @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
10191  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
10192  */
10193 Roo.Fx = {
10194         /**
10195          * Slides the element into view.  An anchor point can be optionally passed to set the point of
10196          * origin for the slide effect.  This function automatically handles wrapping the element with
10197          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10198          * Usage:
10199          *<pre><code>
10200 // default: slide the element in from the top
10201 el.slideIn();
10202
10203 // custom: slide the element in from the right with a 2-second duration
10204 el.slideIn('r', { duration: 2 });
10205
10206 // common config options shown with default values
10207 el.slideIn('t', {
10208     easing: 'easeOut',
10209     duration: .5
10210 });
10211 </code></pre>
10212          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10213          * @param {Object} options (optional) Object literal with any of the Fx config options
10214          * @return {Roo.Element} The Element
10215          */
10216     slideIn : function(anchor, o){
10217         var el = this.getFxEl();
10218         o = o || {};
10219
10220         el.queueFx(o, function(){
10221
10222             anchor = anchor || "t";
10223
10224             // fix display to visibility
10225             this.fixDisplay();
10226
10227             // restore values after effect
10228             var r = this.getFxRestore();
10229             var b = this.getBox();
10230             // fixed size for slide
10231             this.setSize(b);
10232
10233             // wrap if needed
10234             var wrap = this.fxWrap(r.pos, o, "hidden");
10235
10236             var st = this.dom.style;
10237             st.visibility = "visible";
10238             st.position = "absolute";
10239
10240             // clear out temp styles after slide and unwrap
10241             var after = function(){
10242                 el.fxUnwrap(wrap, r.pos, o);
10243                 st.width = r.width;
10244                 st.height = r.height;
10245                 el.afterFx(o);
10246             };
10247             // time to calc the positions
10248             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
10249
10250             switch(anchor.toLowerCase()){
10251                 case "t":
10252                     wrap.setSize(b.width, 0);
10253                     st.left = st.bottom = "0";
10254                     a = {height: bh};
10255                 break;
10256                 case "l":
10257                     wrap.setSize(0, b.height);
10258                     st.right = st.top = "0";
10259                     a = {width: bw};
10260                 break;
10261                 case "r":
10262                     wrap.setSize(0, b.height);
10263                     wrap.setX(b.right);
10264                     st.left = st.top = "0";
10265                     a = {width: bw, points: pt};
10266                 break;
10267                 case "b":
10268                     wrap.setSize(b.width, 0);
10269                     wrap.setY(b.bottom);
10270                     st.left = st.top = "0";
10271                     a = {height: bh, points: pt};
10272                 break;
10273                 case "tl":
10274                     wrap.setSize(0, 0);
10275                     st.right = st.bottom = "0";
10276                     a = {width: bw, height: bh};
10277                 break;
10278                 case "bl":
10279                     wrap.setSize(0, 0);
10280                     wrap.setY(b.y+b.height);
10281                     st.right = st.top = "0";
10282                     a = {width: bw, height: bh, points: pt};
10283                 break;
10284                 case "br":
10285                     wrap.setSize(0, 0);
10286                     wrap.setXY([b.right, b.bottom]);
10287                     st.left = st.top = "0";
10288                     a = {width: bw, height: bh, points: pt};
10289                 break;
10290                 case "tr":
10291                     wrap.setSize(0, 0);
10292                     wrap.setX(b.x+b.width);
10293                     st.left = st.bottom = "0";
10294                     a = {width: bw, height: bh, points: pt};
10295                 break;
10296             }
10297             this.dom.style.visibility = "visible";
10298             wrap.show();
10299
10300             arguments.callee.anim = wrap.fxanim(a,
10301                 o,
10302                 'motion',
10303                 .5,
10304                 'easeOut', after);
10305         });
10306         return this;
10307     },
10308     
10309         /**
10310          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
10311          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
10312          * 'hidden') but block elements will still take up space in the document.  The element must be removed
10313          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
10314          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10315          * Usage:
10316          *<pre><code>
10317 // default: slide the element out to the top
10318 el.slideOut();
10319
10320 // custom: slide the element out to the right with a 2-second duration
10321 el.slideOut('r', { duration: 2 });
10322
10323 // common config options shown with default values
10324 el.slideOut('t', {
10325     easing: 'easeOut',
10326     duration: .5,
10327     remove: false,
10328     useDisplay: false
10329 });
10330 </code></pre>
10331          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10332          * @param {Object} options (optional) Object literal with any of the Fx config options
10333          * @return {Roo.Element} The Element
10334          */
10335     slideOut : function(anchor, o){
10336         var el = this.getFxEl();
10337         o = o || {};
10338
10339         el.queueFx(o, function(){
10340
10341             anchor = anchor || "t";
10342
10343             // restore values after effect
10344             var r = this.getFxRestore();
10345             
10346             var b = this.getBox();
10347             // fixed size for slide
10348             this.setSize(b);
10349
10350             // wrap if needed
10351             var wrap = this.fxWrap(r.pos, o, "visible");
10352
10353             var st = this.dom.style;
10354             st.visibility = "visible";
10355             st.position = "absolute";
10356
10357             wrap.setSize(b);
10358
10359             var after = function(){
10360                 if(o.useDisplay){
10361                     el.setDisplayed(false);
10362                 }else{
10363                     el.hide();
10364                 }
10365
10366                 el.fxUnwrap(wrap, r.pos, o);
10367
10368                 st.width = r.width;
10369                 st.height = r.height;
10370
10371                 el.afterFx(o);
10372             };
10373
10374             var a, zero = {to: 0};
10375             switch(anchor.toLowerCase()){
10376                 case "t":
10377                     st.left = st.bottom = "0";
10378                     a = {height: zero};
10379                 break;
10380                 case "l":
10381                     st.right = st.top = "0";
10382                     a = {width: zero};
10383                 break;
10384                 case "r":
10385                     st.left = st.top = "0";
10386                     a = {width: zero, points: {to:[b.right, b.y]}};
10387                 break;
10388                 case "b":
10389                     st.left = st.top = "0";
10390                     a = {height: zero, points: {to:[b.x, b.bottom]}};
10391                 break;
10392                 case "tl":
10393                     st.right = st.bottom = "0";
10394                     a = {width: zero, height: zero};
10395                 break;
10396                 case "bl":
10397                     st.right = st.top = "0";
10398                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
10399                 break;
10400                 case "br":
10401                     st.left = st.top = "0";
10402                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
10403                 break;
10404                 case "tr":
10405                     st.left = st.bottom = "0";
10406                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
10407                 break;
10408             }
10409
10410             arguments.callee.anim = wrap.fxanim(a,
10411                 o,
10412                 'motion',
10413                 .5,
10414                 "easeOut", after);
10415         });
10416         return this;
10417     },
10418
10419         /**
10420          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
10421          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
10422          * The element must be removed from the DOM using the 'remove' config option if desired.
10423          * Usage:
10424          *<pre><code>
10425 // default
10426 el.puff();
10427
10428 // common config options shown with default values
10429 el.puff({
10430     easing: 'easeOut',
10431     duration: .5,
10432     remove: false,
10433     useDisplay: false
10434 });
10435 </code></pre>
10436          * @param {Object} options (optional) Object literal with any of the Fx config options
10437          * @return {Roo.Element} The Element
10438          */
10439     puff : function(o){
10440         var el = this.getFxEl();
10441         o = o || {};
10442
10443         el.queueFx(o, function(){
10444             this.clearOpacity();
10445             this.show();
10446
10447             // restore values after effect
10448             var r = this.getFxRestore();
10449             var st = this.dom.style;
10450
10451             var after = function(){
10452                 if(o.useDisplay){
10453                     el.setDisplayed(false);
10454                 }else{
10455                     el.hide();
10456                 }
10457
10458                 el.clearOpacity();
10459
10460                 el.setPositioning(r.pos);
10461                 st.width = r.width;
10462                 st.height = r.height;
10463                 st.fontSize = '';
10464                 el.afterFx(o);
10465             };
10466
10467             var width = this.getWidth();
10468             var height = this.getHeight();
10469
10470             arguments.callee.anim = this.fxanim({
10471                     width : {to: this.adjustWidth(width * 2)},
10472                     height : {to: this.adjustHeight(height * 2)},
10473                     points : {by: [-(width * .5), -(height * .5)]},
10474                     opacity : {to: 0},
10475                     fontSize: {to:200, unit: "%"}
10476                 },
10477                 o,
10478                 'motion',
10479                 .5,
10480                 "easeOut", after);
10481         });
10482         return this;
10483     },
10484
10485         /**
10486          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
10487          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
10488          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
10489          * Usage:
10490          *<pre><code>
10491 // default
10492 el.switchOff();
10493
10494 // all config options shown with default values
10495 el.switchOff({
10496     easing: 'easeIn',
10497     duration: .3,
10498     remove: false,
10499     useDisplay: false
10500 });
10501 </code></pre>
10502          * @param {Object} options (optional) Object literal with any of the Fx config options
10503          * @return {Roo.Element} The Element
10504          */
10505     switchOff : function(o){
10506         var el = this.getFxEl();
10507         o = o || {};
10508
10509         el.queueFx(o, function(){
10510             this.clearOpacity();
10511             this.clip();
10512
10513             // restore values after effect
10514             var r = this.getFxRestore();
10515             var st = this.dom.style;
10516
10517             var after = function(){
10518                 if(o.useDisplay){
10519                     el.setDisplayed(false);
10520                 }else{
10521                     el.hide();
10522                 }
10523
10524                 el.clearOpacity();
10525                 el.setPositioning(r.pos);
10526                 st.width = r.width;
10527                 st.height = r.height;
10528
10529                 el.afterFx(o);
10530             };
10531
10532             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
10533                 this.clearOpacity();
10534                 (function(){
10535                     this.fxanim({
10536                         height:{to:1},
10537                         points:{by:[0, this.getHeight() * .5]}
10538                     }, o, 'motion', 0.3, 'easeIn', after);
10539                 }).defer(100, this);
10540             });
10541         });
10542         return this;
10543     },
10544
10545     /**
10546      * Highlights the Element by setting a color (applies to the background-color by default, but can be
10547      * changed using the "attr" config option) and then fading back to the original color. If no original
10548      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
10549      * Usage:
10550 <pre><code>
10551 // default: highlight background to yellow
10552 el.highlight();
10553
10554 // custom: highlight foreground text to blue for 2 seconds
10555 el.highlight("0000ff", { attr: 'color', duration: 2 });
10556
10557 // common config options shown with default values
10558 el.highlight("ffff9c", {
10559     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
10560     endColor: (current color) or "ffffff",
10561     easing: 'easeIn',
10562     duration: 1
10563 });
10564 </code></pre>
10565      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
10566      * @param {Object} options (optional) Object literal with any of the Fx config options
10567      * @return {Roo.Element} The Element
10568      */ 
10569     highlight : function(color, o){
10570         var el = this.getFxEl();
10571         o = o || {};
10572
10573         el.queueFx(o, function(){
10574             color = color || "ffff9c";
10575             attr = o.attr || "backgroundColor";
10576
10577             this.clearOpacity();
10578             this.show();
10579
10580             var origColor = this.getColor(attr);
10581             var restoreColor = this.dom.style[attr];
10582             endColor = (o.endColor || origColor) || "ffffff";
10583
10584             var after = function(){
10585                 el.dom.style[attr] = restoreColor;
10586                 el.afterFx(o);
10587             };
10588
10589             var a = {};
10590             a[attr] = {from: color, to: endColor};
10591             arguments.callee.anim = this.fxanim(a,
10592                 o,
10593                 'color',
10594                 1,
10595                 'easeIn', after);
10596         });
10597         return this;
10598     },
10599
10600    /**
10601     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
10602     * Usage:
10603 <pre><code>
10604 // default: a single light blue ripple
10605 el.frame();
10606
10607 // custom: 3 red ripples lasting 3 seconds total
10608 el.frame("ff0000", 3, { duration: 3 });
10609
10610 // common config options shown with default values
10611 el.frame("C3DAF9", 1, {
10612     duration: 1 //duration of entire animation (not each individual ripple)
10613     // Note: Easing is not configurable and will be ignored if included
10614 });
10615 </code></pre>
10616     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
10617     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
10618     * @param {Object} options (optional) Object literal with any of the Fx config options
10619     * @return {Roo.Element} The Element
10620     */
10621     frame : function(color, count, o){
10622         var el = this.getFxEl();
10623         o = o || {};
10624
10625         el.queueFx(o, function(){
10626             color = color || "#C3DAF9";
10627             if(color.length == 6){
10628                 color = "#" + color;
10629             }
10630             count = count || 1;
10631             duration = o.duration || 1;
10632             this.show();
10633
10634             var b = this.getBox();
10635             var animFn = function(){
10636                 var proxy = this.createProxy({
10637
10638                      style:{
10639                         visbility:"hidden",
10640                         position:"absolute",
10641                         "z-index":"35000", // yee haw
10642                         border:"0px solid " + color
10643                      }
10644                   });
10645                 var scale = Roo.isBorderBox ? 2 : 1;
10646                 proxy.animate({
10647                     top:{from:b.y, to:b.y - 20},
10648                     left:{from:b.x, to:b.x - 20},
10649                     borderWidth:{from:0, to:10},
10650                     opacity:{from:1, to:0},
10651                     height:{from:b.height, to:(b.height + (20*scale))},
10652                     width:{from:b.width, to:(b.width + (20*scale))}
10653                 }, duration, function(){
10654                     proxy.remove();
10655                 });
10656                 if(--count > 0){
10657                      animFn.defer((duration/2)*1000, this);
10658                 }else{
10659                     el.afterFx(o);
10660                 }
10661             };
10662             animFn.call(this);
10663         });
10664         return this;
10665     },
10666
10667    /**
10668     * Creates a pause before any subsequent queued effects begin.  If there are
10669     * no effects queued after the pause it will have no effect.
10670     * Usage:
10671 <pre><code>
10672 el.pause(1);
10673 </code></pre>
10674     * @param {Number} seconds The length of time to pause (in seconds)
10675     * @return {Roo.Element} The Element
10676     */
10677     pause : function(seconds){
10678         var el = this.getFxEl();
10679         var o = {};
10680
10681         el.queueFx(o, function(){
10682             setTimeout(function(){
10683                 el.afterFx(o);
10684             }, seconds * 1000);
10685         });
10686         return this;
10687     },
10688
10689    /**
10690     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
10691     * using the "endOpacity" config option.
10692     * Usage:
10693 <pre><code>
10694 // default: fade in from opacity 0 to 100%
10695 el.fadeIn();
10696
10697 // custom: fade in from opacity 0 to 75% over 2 seconds
10698 el.fadeIn({ endOpacity: .75, duration: 2});
10699
10700 // common config options shown with default values
10701 el.fadeIn({
10702     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
10703     easing: 'easeOut',
10704     duration: .5
10705 });
10706 </code></pre>
10707     * @param {Object} options (optional) Object literal with any of the Fx config options
10708     * @return {Roo.Element} The Element
10709     */
10710     fadeIn : function(o){
10711         var el = this.getFxEl();
10712         o = o || {};
10713         el.queueFx(o, function(){
10714             this.setOpacity(0);
10715             this.fixDisplay();
10716             this.dom.style.visibility = 'visible';
10717             var to = o.endOpacity || 1;
10718             arguments.callee.anim = this.fxanim({opacity:{to:to}},
10719                 o, null, .5, "easeOut", function(){
10720                 if(to == 1){
10721                     this.clearOpacity();
10722                 }
10723                 el.afterFx(o);
10724             });
10725         });
10726         return this;
10727     },
10728
10729    /**
10730     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
10731     * using the "endOpacity" config option.
10732     * Usage:
10733 <pre><code>
10734 // default: fade out from the element's current opacity to 0
10735 el.fadeOut();
10736
10737 // custom: fade out from the element's current opacity to 25% over 2 seconds
10738 el.fadeOut({ endOpacity: .25, duration: 2});
10739
10740 // common config options shown with default values
10741 el.fadeOut({
10742     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
10743     easing: 'easeOut',
10744     duration: .5
10745     remove: false,
10746     useDisplay: false
10747 });
10748 </code></pre>
10749     * @param {Object} options (optional) Object literal with any of the Fx config options
10750     * @return {Roo.Element} The Element
10751     */
10752     fadeOut : function(o){
10753         var el = this.getFxEl();
10754         o = o || {};
10755         el.queueFx(o, function(){
10756             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
10757                 o, null, .5, "easeOut", function(){
10758                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
10759                      this.dom.style.display = "none";
10760                 }else{
10761                      this.dom.style.visibility = "hidden";
10762                 }
10763                 this.clearOpacity();
10764                 el.afterFx(o);
10765             });
10766         });
10767         return this;
10768     },
10769
10770    /**
10771     * Animates the transition of an element's dimensions from a starting height/width
10772     * to an ending height/width.
10773     * Usage:
10774 <pre><code>
10775 // change height and width to 100x100 pixels
10776 el.scale(100, 100);
10777
10778 // common config options shown with default values.  The height and width will default to
10779 // the element's existing values if passed as null.
10780 el.scale(
10781     [element's width],
10782     [element's height], {
10783     easing: 'easeOut',
10784     duration: .35
10785 });
10786 </code></pre>
10787     * @param {Number} width  The new width (pass undefined to keep the original width)
10788     * @param {Number} height  The new height (pass undefined to keep the original height)
10789     * @param {Object} options (optional) Object literal with any of the Fx config options
10790     * @return {Roo.Element} The Element
10791     */
10792     scale : function(w, h, o){
10793         this.shift(Roo.apply({}, o, {
10794             width: w,
10795             height: h
10796         }));
10797         return this;
10798     },
10799
10800    /**
10801     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
10802     * Any of these properties not specified in the config object will not be changed.  This effect 
10803     * requires that at least one new dimension, position or opacity setting must be passed in on
10804     * the config object in order for the function to have any effect.
10805     * Usage:
10806 <pre><code>
10807 // slide the element horizontally to x position 200 while changing the height and opacity
10808 el.shift({ x: 200, height: 50, opacity: .8 });
10809
10810 // common config options shown with default values.
10811 el.shift({
10812     width: [element's width],
10813     height: [element's height],
10814     x: [element's x position],
10815     y: [element's y position],
10816     opacity: [element's opacity],
10817     easing: 'easeOut',
10818     duration: .35
10819 });
10820 </code></pre>
10821     * @param {Object} options  Object literal with any of the Fx config options
10822     * @return {Roo.Element} The Element
10823     */
10824     shift : function(o){
10825         var el = this.getFxEl();
10826         o = o || {};
10827         el.queueFx(o, function(){
10828             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
10829             if(w !== undefined){
10830                 a.width = {to: this.adjustWidth(w)};
10831             }
10832             if(h !== undefined){
10833                 a.height = {to: this.adjustHeight(h)};
10834             }
10835             if(x !== undefined || y !== undefined){
10836                 a.points = {to: [
10837                     x !== undefined ? x : this.getX(),
10838                     y !== undefined ? y : this.getY()
10839                 ]};
10840             }
10841             if(op !== undefined){
10842                 a.opacity = {to: op};
10843             }
10844             if(o.xy !== undefined){
10845                 a.points = {to: o.xy};
10846             }
10847             arguments.callee.anim = this.fxanim(a,
10848                 o, 'motion', .35, "easeOut", function(){
10849                 el.afterFx(o);
10850             });
10851         });
10852         return this;
10853     },
10854
10855         /**
10856          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
10857          * ending point of the effect.
10858          * Usage:
10859          *<pre><code>
10860 // default: slide the element downward while fading out
10861 el.ghost();
10862
10863 // custom: slide the element out to the right with a 2-second duration
10864 el.ghost('r', { duration: 2 });
10865
10866 // common config options shown with default values
10867 el.ghost('b', {
10868     easing: 'easeOut',
10869     duration: .5
10870     remove: false,
10871     useDisplay: false
10872 });
10873 </code></pre>
10874          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
10875          * @param {Object} options (optional) Object literal with any of the Fx config options
10876          * @return {Roo.Element} The Element
10877          */
10878     ghost : function(anchor, o){
10879         var el = this.getFxEl();
10880         o = o || {};
10881
10882         el.queueFx(o, function(){
10883             anchor = anchor || "b";
10884
10885             // restore values after effect
10886             var r = this.getFxRestore();
10887             var w = this.getWidth(),
10888                 h = this.getHeight();
10889
10890             var st = this.dom.style;
10891
10892             var after = function(){
10893                 if(o.useDisplay){
10894                     el.setDisplayed(false);
10895                 }else{
10896                     el.hide();
10897                 }
10898
10899                 el.clearOpacity();
10900                 el.setPositioning(r.pos);
10901                 st.width = r.width;
10902                 st.height = r.height;
10903
10904                 el.afterFx(o);
10905             };
10906
10907             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
10908             switch(anchor.toLowerCase()){
10909                 case "t":
10910                     pt.by = [0, -h];
10911                 break;
10912                 case "l":
10913                     pt.by = [-w, 0];
10914                 break;
10915                 case "r":
10916                     pt.by = [w, 0];
10917                 break;
10918                 case "b":
10919                     pt.by = [0, h];
10920                 break;
10921                 case "tl":
10922                     pt.by = [-w, -h];
10923                 break;
10924                 case "bl":
10925                     pt.by = [-w, h];
10926                 break;
10927                 case "br":
10928                     pt.by = [w, h];
10929                 break;
10930                 case "tr":
10931                     pt.by = [w, -h];
10932                 break;
10933             }
10934
10935             arguments.callee.anim = this.fxanim(a,
10936                 o,
10937                 'motion',
10938                 .5,
10939                 "easeOut", after);
10940         });
10941         return this;
10942     },
10943
10944         /**
10945          * Ensures that all effects queued after syncFx is called on the element are
10946          * run concurrently.  This is the opposite of {@link #sequenceFx}.
10947          * @return {Roo.Element} The Element
10948          */
10949     syncFx : function(){
10950         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10951             block : false,
10952             concurrent : true,
10953             stopFx : false
10954         });
10955         return this;
10956     },
10957
10958         /**
10959          * Ensures that all effects queued after sequenceFx is called on the element are
10960          * run in sequence.  This is the opposite of {@link #syncFx}.
10961          * @return {Roo.Element} The Element
10962          */
10963     sequenceFx : function(){
10964         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10965             block : false,
10966             concurrent : false,
10967             stopFx : false
10968         });
10969         return this;
10970     },
10971
10972         /* @private */
10973     nextFx : function(){
10974         var ef = this.fxQueue[0];
10975         if(ef){
10976             ef.call(this);
10977         }
10978     },
10979
10980         /**
10981          * Returns true if the element has any effects actively running or queued, else returns false.
10982          * @return {Boolean} True if element has active effects, else false
10983          */
10984     hasActiveFx : function(){
10985         return this.fxQueue && this.fxQueue[0];
10986     },
10987
10988         /**
10989          * Stops any running effects and clears the element's internal effects queue if it contains
10990          * any additional effects that haven't started yet.
10991          * @return {Roo.Element} The Element
10992          */
10993     stopFx : function(){
10994         if(this.hasActiveFx()){
10995             var cur = this.fxQueue[0];
10996             if(cur && cur.anim && cur.anim.isAnimated()){
10997                 this.fxQueue = [cur]; // clear out others
10998                 cur.anim.stop(true);
10999             }
11000         }
11001         return this;
11002     },
11003
11004         /* @private */
11005     beforeFx : function(o){
11006         if(this.hasActiveFx() && !o.concurrent){
11007            if(o.stopFx){
11008                this.stopFx();
11009                return true;
11010            }
11011            return false;
11012         }
11013         return true;
11014     },
11015
11016         /**
11017          * Returns true if the element is currently blocking so that no other effect can be queued
11018          * until this effect is finished, else returns false if blocking is not set.  This is commonly
11019          * used to ensure that an effect initiated by a user action runs to completion prior to the
11020          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
11021          * @return {Boolean} True if blocking, else false
11022          */
11023     hasFxBlock : function(){
11024         var q = this.fxQueue;
11025         return q && q[0] && q[0].block;
11026     },
11027
11028         /* @private */
11029     queueFx : function(o, fn){
11030         if(!this.fxQueue){
11031             this.fxQueue = [];
11032         }
11033         if(!this.hasFxBlock()){
11034             Roo.applyIf(o, this.fxDefaults);
11035             if(!o.concurrent){
11036                 var run = this.beforeFx(o);
11037                 fn.block = o.block;
11038                 this.fxQueue.push(fn);
11039                 if(run){
11040                     this.nextFx();
11041                 }
11042             }else{
11043                 fn.call(this);
11044             }
11045         }
11046         return this;
11047     },
11048
11049         /* @private */
11050     fxWrap : function(pos, o, vis){
11051         var wrap;
11052         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
11053             var wrapXY;
11054             if(o.fixPosition){
11055                 wrapXY = this.getXY();
11056             }
11057             var div = document.createElement("div");
11058             div.style.visibility = vis;
11059             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
11060             wrap.setPositioning(pos);
11061             if(wrap.getStyle("position") == "static"){
11062                 wrap.position("relative");
11063             }
11064             this.clearPositioning('auto');
11065             wrap.clip();
11066             wrap.dom.appendChild(this.dom);
11067             if(wrapXY){
11068                 wrap.setXY(wrapXY);
11069             }
11070         }
11071         return wrap;
11072     },
11073
11074         /* @private */
11075     fxUnwrap : function(wrap, pos, o){
11076         this.clearPositioning();
11077         this.setPositioning(pos);
11078         if(!o.wrap){
11079             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
11080             wrap.remove();
11081         }
11082     },
11083
11084         /* @private */
11085     getFxRestore : function(){
11086         var st = this.dom.style;
11087         return {pos: this.getPositioning(), width: st.width, height : st.height};
11088     },
11089
11090         /* @private */
11091     afterFx : function(o){
11092         if(o.afterStyle){
11093             this.applyStyles(o.afterStyle);
11094         }
11095         if(o.afterCls){
11096             this.addClass(o.afterCls);
11097         }
11098         if(o.remove === true){
11099             this.remove();
11100         }
11101         Roo.callback(o.callback, o.scope, [this]);
11102         if(!o.concurrent){
11103             this.fxQueue.shift();
11104             this.nextFx();
11105         }
11106     },
11107
11108         /* @private */
11109     getFxEl : function(){ // support for composite element fx
11110         return Roo.get(this.dom);
11111     },
11112
11113         /* @private */
11114     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
11115         animType = animType || 'run';
11116         opt = opt || {};
11117         var anim = Roo.lib.Anim[animType](
11118             this.dom, args,
11119             (opt.duration || defaultDur) || .35,
11120             (opt.easing || defaultEase) || 'easeOut',
11121             function(){
11122                 Roo.callback(cb, this);
11123             },
11124             this
11125         );
11126         opt.anim = anim;
11127         return anim;
11128     }
11129 };
11130
11131 // backwords compat
11132 Roo.Fx.resize = Roo.Fx.scale;
11133
11134 //When included, Roo.Fx is automatically applied to Element so that all basic
11135 //effects are available directly via the Element API
11136 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
11137  * Based on:
11138  * Ext JS Library 1.1.1
11139  * Copyright(c) 2006-2007, Ext JS, LLC.
11140  *
11141  * Originally Released Under LGPL - original licence link has changed is not relivant.
11142  *
11143  * Fork - LGPL
11144  * <script type="text/javascript">
11145  */
11146
11147
11148 /**
11149  * @class Roo.CompositeElement
11150  * Standard composite class. Creates a Roo.Element for every element in the collection.
11151  * <br><br>
11152  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11153  * actions will be performed on all the elements in this collection.</b>
11154  * <br><br>
11155  * All methods return <i>this</i> and can be chained.
11156  <pre><code>
11157  var els = Roo.select("#some-el div.some-class", true);
11158  // or select directly from an existing element
11159  var el = Roo.get('some-el');
11160  el.select('div.some-class', true);
11161
11162  els.setWidth(100); // all elements become 100 width
11163  els.hide(true); // all elements fade out and hide
11164  // or
11165  els.setWidth(100).hide(true);
11166  </code></pre>
11167  */
11168 Roo.CompositeElement = function(els){
11169     this.elements = [];
11170     this.addElements(els);
11171 };
11172 Roo.CompositeElement.prototype = {
11173     isComposite: true,
11174     addElements : function(els){
11175         if(!els) {
11176             return this;
11177         }
11178         if(typeof els == "string"){
11179             els = Roo.Element.selectorFunction(els);
11180         }
11181         var yels = this.elements;
11182         var index = yels.length-1;
11183         for(var i = 0, len = els.length; i < len; i++) {
11184                 yels[++index] = Roo.get(els[i]);
11185         }
11186         return this;
11187     },
11188
11189     /**
11190     * Clears this composite and adds the elements returned by the passed selector.
11191     * @param {String/Array} els A string CSS selector, an array of elements or an element
11192     * @return {CompositeElement} this
11193     */
11194     fill : function(els){
11195         this.elements = [];
11196         this.add(els);
11197         return this;
11198     },
11199
11200     /**
11201     * Filters this composite to only elements that match the passed selector.
11202     * @param {String} selector A string CSS selector
11203     * @param {Boolean} inverse return inverse filter (not matches)
11204     * @return {CompositeElement} this
11205     */
11206     filter : function(selector, inverse){
11207         var els = [];
11208         inverse = inverse || false;
11209         this.each(function(el){
11210             var match = inverse ? !el.is(selector) : el.is(selector);
11211             if(match){
11212                 els[els.length] = el.dom;
11213             }
11214         });
11215         this.fill(els);
11216         return this;
11217     },
11218
11219     invoke : function(fn, args){
11220         var els = this.elements;
11221         for(var i = 0, len = els.length; i < len; i++) {
11222                 Roo.Element.prototype[fn].apply(els[i], args);
11223         }
11224         return this;
11225     },
11226     /**
11227     * Adds elements to this composite.
11228     * @param {String/Array} els A string CSS selector, an array of elements or an element
11229     * @return {CompositeElement} this
11230     */
11231     add : function(els){
11232         if(typeof els == "string"){
11233             this.addElements(Roo.Element.selectorFunction(els));
11234         }else if(els.length !== undefined){
11235             this.addElements(els);
11236         }else{
11237             this.addElements([els]);
11238         }
11239         return this;
11240     },
11241     /**
11242     * Calls the passed function passing (el, this, index) for each element in this composite.
11243     * @param {Function} fn The function to call
11244     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11245     * @return {CompositeElement} this
11246     */
11247     each : function(fn, scope){
11248         var els = this.elements;
11249         for(var i = 0, len = els.length; i < len; i++){
11250             if(fn.call(scope || els[i], els[i], this, i) === false) {
11251                 break;
11252             }
11253         }
11254         return this;
11255     },
11256
11257     /**
11258      * Returns the Element object at the specified index
11259      * @param {Number} index
11260      * @return {Roo.Element}
11261      */
11262     item : function(index){
11263         return this.elements[index] || null;
11264     },
11265
11266     /**
11267      * Returns the first Element
11268      * @return {Roo.Element}
11269      */
11270     first : function(){
11271         return this.item(0);
11272     },
11273
11274     /**
11275      * Returns the last Element
11276      * @return {Roo.Element}
11277      */
11278     last : function(){
11279         return this.item(this.elements.length-1);
11280     },
11281
11282     /**
11283      * Returns the number of elements in this composite
11284      * @return Number
11285      */
11286     getCount : function(){
11287         return this.elements.length;
11288     },
11289
11290     /**
11291      * Returns true if this composite contains the passed element
11292      * @return Boolean
11293      */
11294     contains : function(el){
11295         return this.indexOf(el) !== -1;
11296     },
11297
11298     /**
11299      * Returns true if this composite contains the passed element
11300      * @return Boolean
11301      */
11302     indexOf : function(el){
11303         return this.elements.indexOf(Roo.get(el));
11304     },
11305
11306
11307     /**
11308     * Removes the specified element(s).
11309     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
11310     * or an array of any of those.
11311     * @param {Boolean} removeDom (optional) True to also remove the element from the document
11312     * @return {CompositeElement} this
11313     */
11314     removeElement : function(el, removeDom){
11315         if(el instanceof Array){
11316             for(var i = 0, len = el.length; i < len; i++){
11317                 this.removeElement(el[i]);
11318             }
11319             return this;
11320         }
11321         var index = typeof el == 'number' ? el : this.indexOf(el);
11322         if(index !== -1){
11323             if(removeDom){
11324                 var d = this.elements[index];
11325                 if(d.dom){
11326                     d.remove();
11327                 }else{
11328                     d.parentNode.removeChild(d);
11329                 }
11330             }
11331             this.elements.splice(index, 1);
11332         }
11333         return this;
11334     },
11335
11336     /**
11337     * Replaces the specified element with the passed element.
11338     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
11339     * to replace.
11340     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
11341     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
11342     * @return {CompositeElement} this
11343     */
11344     replaceElement : function(el, replacement, domReplace){
11345         var index = typeof el == 'number' ? el : this.indexOf(el);
11346         if(index !== -1){
11347             if(domReplace){
11348                 this.elements[index].replaceWith(replacement);
11349             }else{
11350                 this.elements.splice(index, 1, Roo.get(replacement))
11351             }
11352         }
11353         return this;
11354     },
11355
11356     /**
11357      * Removes all elements.
11358      */
11359     clear : function(){
11360         this.elements = [];
11361     }
11362 };
11363 (function(){
11364     Roo.CompositeElement.createCall = function(proto, fnName){
11365         if(!proto[fnName]){
11366             proto[fnName] = function(){
11367                 return this.invoke(fnName, arguments);
11368             };
11369         }
11370     };
11371     for(var fnName in Roo.Element.prototype){
11372         if(typeof Roo.Element.prototype[fnName] == "function"){
11373             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
11374         }
11375     };
11376 })();
11377 /*
11378  * Based on:
11379  * Ext JS Library 1.1.1
11380  * Copyright(c) 2006-2007, Ext JS, LLC.
11381  *
11382  * Originally Released Under LGPL - original licence link has changed is not relivant.
11383  *
11384  * Fork - LGPL
11385  * <script type="text/javascript">
11386  */
11387
11388 /**
11389  * @class Roo.CompositeElementLite
11390  * @extends Roo.CompositeElement
11391  * Flyweight composite class. Reuses the same Roo.Element for element operations.
11392  <pre><code>
11393  var els = Roo.select("#some-el div.some-class");
11394  // or select directly from an existing element
11395  var el = Roo.get('some-el');
11396  el.select('div.some-class');
11397
11398  els.setWidth(100); // all elements become 100 width
11399  els.hide(true); // all elements fade out and hide
11400  // or
11401  els.setWidth(100).hide(true);
11402  </code></pre><br><br>
11403  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11404  * actions will be performed on all the elements in this collection.</b>
11405  */
11406 Roo.CompositeElementLite = function(els){
11407     Roo.CompositeElementLite.superclass.constructor.call(this, els);
11408     this.el = new Roo.Element.Flyweight();
11409 };
11410 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
11411     addElements : function(els){
11412         if(els){
11413             if(els instanceof Array){
11414                 this.elements = this.elements.concat(els);
11415             }else{
11416                 var yels = this.elements;
11417                 var index = yels.length-1;
11418                 for(var i = 0, len = els.length; i < len; i++) {
11419                     yels[++index] = els[i];
11420                 }
11421             }
11422         }
11423         return this;
11424     },
11425     invoke : function(fn, args){
11426         var els = this.elements;
11427         var el = this.el;
11428         for(var i = 0, len = els.length; i < len; i++) {
11429             el.dom = els[i];
11430                 Roo.Element.prototype[fn].apply(el, args);
11431         }
11432         return this;
11433     },
11434     /**
11435      * Returns a flyweight Element of the dom element object at the specified index
11436      * @param {Number} index
11437      * @return {Roo.Element}
11438      */
11439     item : function(index){
11440         if(!this.elements[index]){
11441             return null;
11442         }
11443         this.el.dom = this.elements[index];
11444         return this.el;
11445     },
11446
11447     // fixes scope with flyweight
11448     addListener : function(eventName, handler, scope, opt){
11449         var els = this.elements;
11450         for(var i = 0, len = els.length; i < len; i++) {
11451             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
11452         }
11453         return this;
11454     },
11455
11456     /**
11457     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
11458     * passed is the flyweight (shared) Roo.Element instance, so if you require a
11459     * a reference to the dom node, use el.dom.</b>
11460     * @param {Function} fn The function to call
11461     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11462     * @return {CompositeElement} this
11463     */
11464     each : function(fn, scope){
11465         var els = this.elements;
11466         var el = this.el;
11467         for(var i = 0, len = els.length; i < len; i++){
11468             el.dom = els[i];
11469                 if(fn.call(scope || el, el, this, i) === false){
11470                 break;
11471             }
11472         }
11473         return this;
11474     },
11475
11476     indexOf : function(el){
11477         return this.elements.indexOf(Roo.getDom(el));
11478     },
11479
11480     replaceElement : function(el, replacement, domReplace){
11481         var index = typeof el == 'number' ? el : this.indexOf(el);
11482         if(index !== -1){
11483             replacement = Roo.getDom(replacement);
11484             if(domReplace){
11485                 var d = this.elements[index];
11486                 d.parentNode.insertBefore(replacement, d);
11487                 d.parentNode.removeChild(d);
11488             }
11489             this.elements.splice(index, 1, replacement);
11490         }
11491         return this;
11492     }
11493 });
11494 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
11495
11496 /*
11497  * Based on:
11498  * Ext JS Library 1.1.1
11499  * Copyright(c) 2006-2007, Ext JS, LLC.
11500  *
11501  * Originally Released Under LGPL - original licence link has changed is not relivant.
11502  *
11503  * Fork - LGPL
11504  * <script type="text/javascript">
11505  */
11506
11507  
11508
11509 /**
11510  * @class Roo.data.Connection
11511  * @extends Roo.util.Observable
11512  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
11513  * either to a configured URL, or to a URL specified at request time. 
11514  * 
11515  * Requests made by this class are asynchronous, and will return immediately. No data from
11516  * the server will be available to the statement immediately following the {@link #request} call.
11517  * To process returned data, use a callback in the request options object, or an event listener.
11518  * 
11519  * Note: If you are doing a file upload, you will not get a normal response object sent back to
11520  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
11521  * The response object is created using the innerHTML of the IFRAME's document as the responseText
11522  * property and, if present, the IFRAME's XML document as the responseXML property.
11523  * 
11524  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
11525  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
11526  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
11527  * standard DOM methods.
11528  * @constructor
11529  * @param {Object} config a configuration object.
11530  */
11531 Roo.data.Connection = function(config){
11532     Roo.apply(this, config);
11533     this.addEvents({
11534         /**
11535          * @event beforerequest
11536          * Fires before a network request is made to retrieve a data object.
11537          * @param {Connection} conn This Connection object.
11538          * @param {Object} options The options config object passed to the {@link #request} method.
11539          */
11540         "beforerequest" : true,
11541         /**
11542          * @event requestcomplete
11543          * Fires if the request was successfully completed.
11544          * @param {Connection} conn This Connection object.
11545          * @param {Object} response The XHR object containing the response data.
11546          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11547          * @param {Object} options The options config object passed to the {@link #request} method.
11548          */
11549         "requestcomplete" : true,
11550         /**
11551          * @event requestexception
11552          * Fires if an error HTTP status was returned from the server.
11553          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
11554          * @param {Connection} conn This Connection object.
11555          * @param {Object} response The XHR object containing the response data.
11556          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11557          * @param {Object} options The options config object passed to the {@link #request} method.
11558          */
11559         "requestexception" : true
11560     });
11561     Roo.data.Connection.superclass.constructor.call(this);
11562 };
11563
11564 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
11565     /**
11566      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11567      */
11568     /**
11569      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11570      * extra parameters to each request made by this object. (defaults to undefined)
11571      */
11572     /**
11573      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11574      *  to each request made by this object. (defaults to undefined)
11575      */
11576     /**
11577      * @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)
11578      */
11579     /**
11580      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11581      */
11582     timeout : 30000,
11583     /**
11584      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11585      * @type Boolean
11586      */
11587     autoAbort:false,
11588
11589     /**
11590      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11591      * @type Boolean
11592      */
11593     disableCaching: true,
11594
11595     /**
11596      * Sends an HTTP request to a remote server.
11597      * @param {Object} options An object which may contain the following properties:<ul>
11598      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
11599      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
11600      * request, a url encoded string or a function to call to get either.</li>
11601      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
11602      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
11603      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
11604      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
11605      * <li>options {Object} The parameter to the request call.</li>
11606      * <li>success {Boolean} True if the request succeeded.</li>
11607      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11608      * </ul></li>
11609      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
11610      * The callback is passed the following parameters:<ul>
11611      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11612      * <li>options {Object} The parameter to the request call.</li>
11613      * </ul></li>
11614      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
11615      * The callback is passed the following parameters:<ul>
11616      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11617      * <li>options {Object} The parameter to the request call.</li>
11618      * </ul></li>
11619      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
11620      * for the callback function. Defaults to the browser window.</li>
11621      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
11622      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
11623      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
11624      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
11625      * params for the post data. Any params will be appended to the URL.</li>
11626      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
11627      * </ul>
11628      * @return {Number} transactionId
11629      */
11630     request : function(o){
11631         if(this.fireEvent("beforerequest", this, o) !== false){
11632             var p = o.params;
11633
11634             if(typeof p == "function"){
11635                 p = p.call(o.scope||window, o);
11636             }
11637             if(typeof p == "object"){
11638                 p = Roo.urlEncode(o.params);
11639             }
11640             if(this.extraParams){
11641                 var extras = Roo.urlEncode(this.extraParams);
11642                 p = p ? (p + '&' + extras) : extras;
11643             }
11644
11645             var url = o.url || this.url;
11646             if(typeof url == 'function'){
11647                 url = url.call(o.scope||window, o);
11648             }
11649
11650             if(o.form){
11651                 var form = Roo.getDom(o.form);
11652                 url = url || form.action;
11653
11654                 var enctype = form.getAttribute("enctype");
11655                 
11656                 if (o.formData) {
11657                     return this.doFormDataUpload(o, url);
11658                 }
11659                 
11660                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
11661                     return this.doFormUpload(o, p, url);
11662                 }
11663                 var f = Roo.lib.Ajax.serializeForm(form);
11664                 p = p ? (p + '&' + f) : f;
11665             }
11666             
11667             if (!o.form && o.formData) {
11668                 o.formData = o.formData === true ? new FormData() : o.formData;
11669                 for (var k in o.params) {
11670                     o.formData.append(k,o.params[k]);
11671                 }
11672                     
11673                 return this.doFormDataUpload(o, url);
11674             }
11675             
11676
11677             var hs = o.headers;
11678             if(this.defaultHeaders){
11679                 hs = Roo.apply(hs || {}, this.defaultHeaders);
11680                 if(!o.headers){
11681                     o.headers = hs;
11682                 }
11683             }
11684
11685             var cb = {
11686                 success: this.handleResponse,
11687                 failure: this.handleFailure,
11688                 scope: this,
11689                 argument: {options: o},
11690                 timeout : o.timeout || this.timeout
11691             };
11692
11693             var method = o.method||this.method||(p ? "POST" : "GET");
11694
11695             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
11696                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
11697             }
11698
11699             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11700                 if(o.autoAbort){
11701                     this.abort();
11702                 }
11703             }else if(this.autoAbort !== false){
11704                 this.abort();
11705             }
11706
11707             if((method == 'GET' && p) || o.xmlData){
11708                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11709                 p = '';
11710             }
11711             Roo.lib.Ajax.useDefaultHeader = typeof(o.headers) == 'undefined' || typeof(o.headers['Content-Type']) == 'undefined';
11712             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
11713             Roo.lib.Ajax.useDefaultHeader == true;
11714             return this.transId;
11715         }else{
11716             Roo.callback(o.callback, o.scope, [o, null, null]);
11717             return null;
11718         }
11719     },
11720
11721     /**
11722      * Determine whether this object has a request outstanding.
11723      * @param {Number} transactionId (Optional) defaults to the last transaction
11724      * @return {Boolean} True if there is an outstanding request.
11725      */
11726     isLoading : function(transId){
11727         if(transId){
11728             return Roo.lib.Ajax.isCallInProgress(transId);
11729         }else{
11730             return this.transId ? true : false;
11731         }
11732     },
11733
11734     /**
11735      * Aborts any outstanding request.
11736      * @param {Number} transactionId (Optional) defaults to the last transaction
11737      */
11738     abort : function(transId){
11739         if(transId || this.isLoading()){
11740             Roo.lib.Ajax.abort(transId || this.transId);
11741         }
11742     },
11743
11744     // private
11745     handleResponse : function(response){
11746         this.transId = false;
11747         var options = response.argument.options;
11748         response.argument = options ? options.argument : null;
11749         this.fireEvent("requestcomplete", this, response, options);
11750         Roo.callback(options.success, options.scope, [response, options]);
11751         Roo.callback(options.callback, options.scope, [options, true, response]);
11752     },
11753
11754     // private
11755     handleFailure : function(response, e){
11756         this.transId = false;
11757         var options = response.argument.options;
11758         response.argument = options ? options.argument : null;
11759         this.fireEvent("requestexception", this, response, options, e);
11760         Roo.callback(options.failure, options.scope, [response, options]);
11761         Roo.callback(options.callback, options.scope, [options, false, response]);
11762     },
11763
11764     // private
11765     doFormUpload : function(o, ps, url){
11766         var id = Roo.id();
11767         var frame = document.createElement('iframe');
11768         frame.id = id;
11769         frame.name = id;
11770         frame.className = 'x-hidden';
11771         if(Roo.isIE){
11772             frame.src = Roo.SSL_SECURE_URL;
11773         }
11774         document.body.appendChild(frame);
11775
11776         if(Roo.isIE){
11777            document.frames[id].name = id;
11778         }
11779
11780         var form = Roo.getDom(o.form);
11781         form.target = id;
11782         form.method = 'POST';
11783         form.enctype = form.encoding = 'multipart/form-data';
11784         if(url){
11785             form.action = url;
11786         }
11787
11788         var hiddens, hd;
11789         if(ps){ // add dynamic params
11790             hiddens = [];
11791             ps = Roo.urlDecode(ps, false);
11792             for(var k in ps){
11793                 if(ps.hasOwnProperty(k)){
11794                     hd = document.createElement('input');
11795                     hd.type = 'hidden';
11796                     hd.name = k;
11797                     hd.value = ps[k];
11798                     form.appendChild(hd);
11799                     hiddens.push(hd);
11800                 }
11801             }
11802         }
11803
11804         function cb(){
11805             var r = {  // bogus response object
11806                 responseText : '',
11807                 responseXML : null
11808             };
11809
11810             r.argument = o ? o.argument : null;
11811
11812             try { //
11813                 var doc;
11814                 if(Roo.isIE){
11815                     doc = frame.contentWindow.document;
11816                 }else {
11817                     doc = (frame.contentDocument || window.frames[id].document);
11818                 }
11819                 if(doc && doc.body){
11820                     r.responseText = doc.body.innerHTML;
11821                 }
11822                 if(doc && doc.XMLDocument){
11823                     r.responseXML = doc.XMLDocument;
11824                 }else {
11825                     r.responseXML = doc;
11826                 }
11827             }
11828             catch(e) {
11829                 // ignore
11830             }
11831
11832             Roo.EventManager.removeListener(frame, 'load', cb, this);
11833
11834             this.fireEvent("requestcomplete", this, r, o);
11835             Roo.callback(o.success, o.scope, [r, o]);
11836             Roo.callback(o.callback, o.scope, [o, true, r]);
11837
11838             setTimeout(function(){document.body.removeChild(frame);}, 100);
11839         }
11840
11841         Roo.EventManager.on(frame, 'load', cb, this);
11842         form.submit();
11843
11844         if(hiddens){ // remove dynamic params
11845             for(var i = 0, len = hiddens.length; i < len; i++){
11846                 form.removeChild(hiddens[i]);
11847             }
11848         }
11849     },
11850     // this is a 'formdata version???'
11851     
11852     
11853     doFormDataUpload : function(o,  url)
11854     {
11855         var formData;
11856         if (o.form) {
11857             var form =  Roo.getDom(o.form);
11858             form.enctype = form.encoding = 'multipart/form-data';
11859             formData = o.formData === true ? new FormData(form) : o.formData;
11860         } else {
11861             formData = o.formData === true ? new FormData() : o.formData;
11862         }
11863         
11864       
11865         var cb = {
11866             success: this.handleResponse,
11867             failure: this.handleFailure,
11868             scope: this,
11869             argument: {options: o},
11870             timeout : o.timeout || this.timeout
11871         };
11872  
11873         if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11874             if(o.autoAbort){
11875                 this.abort();
11876             }
11877         }else if(this.autoAbort !== false){
11878             this.abort();
11879         }
11880
11881         //Roo.lib.Ajax.defaultPostHeader = null;
11882         Roo.lib.Ajax.useDefaultHeader = false;
11883         this.transId = Roo.lib.Ajax.request( "POST", url, cb,  formData, o);
11884         Roo.lib.Ajax.useDefaultHeader = true;
11885  
11886          
11887     }
11888     
11889 });
11890 /*
11891  * Based on:
11892  * Ext JS Library 1.1.1
11893  * Copyright(c) 2006-2007, Ext JS, LLC.
11894  *
11895  * Originally Released Under LGPL - original licence link has changed is not relivant.
11896  *
11897  * Fork - LGPL
11898  * <script type="text/javascript">
11899  */
11900  
11901 /**
11902  * Global Ajax request class.
11903  * 
11904  * @class Roo.Ajax
11905  * @extends Roo.data.Connection
11906  * @static
11907  * 
11908  * @cfg {String} url  The default URL to be used for requests to the server. (defaults to undefined)
11909  * @cfg {Object} extraParams  An object containing properties which are used as extra parameters to each request made by this object. (defaults to undefined)
11910  * @cfg {Object} defaultHeaders  An object containing request headers which are added to each request made by this object. (defaults to undefined)
11911  * @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)
11912  * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11913  * @cfg {Boolean} autoAbort (Optional) Whether a new request should abort any pending requests. (defaults to false)
11914  * @cfg {Boolean} disableCaching (Optional)   True to add a unique cache-buster param to GET requests. (defaults to true)
11915  */
11916 Roo.Ajax = new Roo.data.Connection({
11917     // fix up the docs
11918     /**
11919      * @scope Roo.Ajax
11920      * @type {Boolear} 
11921      */
11922     autoAbort : false,
11923
11924     /**
11925      * Serialize the passed form into a url encoded string
11926      * @scope Roo.Ajax
11927      * @param {String/HTMLElement} form
11928      * @return {String}
11929      */
11930     serializeForm : function(form){
11931         return Roo.lib.Ajax.serializeForm(form);
11932     }
11933 });/*
11934  * Based on:
11935  * Ext JS Library 1.1.1
11936  * Copyright(c) 2006-2007, Ext JS, LLC.
11937  *
11938  * Originally Released Under LGPL - original licence link has changed is not relivant.
11939  *
11940  * Fork - LGPL
11941  * <script type="text/javascript">
11942  */
11943
11944  
11945 /**
11946  * @class Roo.UpdateManager
11947  * @extends Roo.util.Observable
11948  * Provides AJAX-style update for Element object.<br><br>
11949  * Usage:<br>
11950  * <pre><code>
11951  * // Get it from a Roo.Element object
11952  * var el = Roo.get("foo");
11953  * var mgr = el.getUpdateManager();
11954  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
11955  * ...
11956  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
11957  * <br>
11958  * // or directly (returns the same UpdateManager instance)
11959  * var mgr = new Roo.UpdateManager("myElementId");
11960  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
11961  * mgr.on("update", myFcnNeedsToKnow);
11962  * <br>
11963    // short handed call directly from the element object
11964    Roo.get("foo").load({
11965         url: "bar.php",
11966         scripts:true,
11967         params: "for=bar",
11968         text: "Loading Foo..."
11969    });
11970  * </code></pre>
11971  * @constructor
11972  * Create new UpdateManager directly.
11973  * @param {String/HTMLElement/Roo.Element} el The element to update
11974  * @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).
11975  */
11976 Roo.UpdateManager = function(el, forceNew){
11977     el = Roo.get(el);
11978     if(!forceNew && el.updateManager){
11979         return el.updateManager;
11980     }
11981     /**
11982      * The Element object
11983      * @type Roo.Element
11984      */
11985     this.el = el;
11986     /**
11987      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
11988      * @type String
11989      */
11990     this.defaultUrl = null;
11991
11992     this.addEvents({
11993         /**
11994          * @event beforeupdate
11995          * Fired before an update is made, return false from your handler and the update is cancelled.
11996          * @param {Roo.Element} el
11997          * @param {String/Object/Function} url
11998          * @param {String/Object} params
11999          */
12000         "beforeupdate": true,
12001         /**
12002          * @event update
12003          * Fired after successful update is made.
12004          * @param {Roo.Element} el
12005          * @param {Object} oResponseObject The response Object
12006          */
12007         "update": true,
12008         /**
12009          * @event failure
12010          * Fired on update failure.
12011          * @param {Roo.Element} el
12012          * @param {Object} oResponseObject The response Object
12013          */
12014         "failure": true
12015     });
12016     var d = Roo.UpdateManager.defaults;
12017     /**
12018      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
12019      * @type String
12020      */
12021     this.sslBlankUrl = d.sslBlankUrl;
12022     /**
12023      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
12024      * @type Boolean
12025      */
12026     this.disableCaching = d.disableCaching;
12027     /**
12028      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12029      * @type String
12030      */
12031     this.indicatorText = d.indicatorText;
12032     /**
12033      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
12034      * @type String
12035      */
12036     this.showLoadIndicator = d.showLoadIndicator;
12037     /**
12038      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
12039      * @type Number
12040      */
12041     this.timeout = d.timeout;
12042
12043     /**
12044      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
12045      * @type Boolean
12046      */
12047     this.loadScripts = d.loadScripts;
12048
12049     /**
12050      * Transaction object of current executing transaction
12051      */
12052     this.transaction = null;
12053
12054     /**
12055      * @private
12056      */
12057     this.autoRefreshProcId = null;
12058     /**
12059      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
12060      * @type Function
12061      */
12062     this.refreshDelegate = this.refresh.createDelegate(this);
12063     /**
12064      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
12065      * @type Function
12066      */
12067     this.updateDelegate = this.update.createDelegate(this);
12068     /**
12069      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
12070      * @type Function
12071      */
12072     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
12073     /**
12074      * @private
12075      */
12076     this.successDelegate = this.processSuccess.createDelegate(this);
12077     /**
12078      * @private
12079      */
12080     this.failureDelegate = this.processFailure.createDelegate(this);
12081
12082     if(!this.renderer){
12083      /**
12084       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
12085       */
12086     this.renderer = new Roo.UpdateManager.BasicRenderer();
12087     }
12088     
12089     Roo.UpdateManager.superclass.constructor.call(this);
12090 };
12091
12092 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
12093     /**
12094      * Get the Element this UpdateManager is bound to
12095      * @return {Roo.Element} The element
12096      */
12097     getEl : function(){
12098         return this.el;
12099     },
12100     /**
12101      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
12102      * @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:
12103 <pre><code>
12104 um.update({<br/>
12105     url: "your-url.php",<br/>
12106     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
12107     callback: yourFunction,<br/>
12108     scope: yourObject, //(optional scope)  <br/>
12109     discardUrl: false, <br/>
12110     nocache: false,<br/>
12111     text: "Loading...",<br/>
12112     timeout: 30,<br/>
12113     scripts: false<br/>
12114 });
12115 </code></pre>
12116      * The only required property is url. The optional properties nocache, text and scripts
12117      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
12118      * @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}
12119      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
12120      * @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.
12121      */
12122     update : function(url, params, callback, discardUrl){
12123         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
12124             var method = this.method,
12125                 cfg;
12126             if(typeof url == "object"){ // must be config object
12127                 cfg = url;
12128                 url = cfg.url;
12129                 params = params || cfg.params;
12130                 callback = callback || cfg.callback;
12131                 discardUrl = discardUrl || cfg.discardUrl;
12132                 if(callback && cfg.scope){
12133                     callback = callback.createDelegate(cfg.scope);
12134                 }
12135                 if(typeof cfg.method != "undefined"){method = cfg.method;};
12136                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
12137                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
12138                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
12139                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
12140             }
12141             this.showLoading();
12142             if(!discardUrl){
12143                 this.defaultUrl = url;
12144             }
12145             if(typeof url == "function"){
12146                 url = url.call(this);
12147             }
12148
12149             method = method || (params ? "POST" : "GET");
12150             if(method == "GET"){
12151                 url = this.prepareUrl(url);
12152             }
12153
12154             var o = Roo.apply(cfg ||{}, {
12155                 url : url,
12156                 params: params,
12157                 success: this.successDelegate,
12158                 failure: this.failureDelegate,
12159                 callback: undefined,
12160                 timeout: (this.timeout*1000),
12161                 argument: {"url": url, "form": null, "callback": callback, "params": params}
12162             });
12163             Roo.log("updated manager called with timeout of " + o.timeout);
12164             this.transaction = Roo.Ajax.request(o);
12165         }
12166     },
12167
12168     /**
12169      * 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.
12170      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
12171      * @param {String/HTMLElement} form The form Id or form element
12172      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
12173      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
12174      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
12175      */
12176     formUpdate : function(form, url, reset, callback){
12177         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
12178             if(typeof url == "function"){
12179                 url = url.call(this);
12180             }
12181             form = Roo.getDom(form);
12182             this.transaction = Roo.Ajax.request({
12183                 form: form,
12184                 url:url,
12185                 success: this.successDelegate,
12186                 failure: this.failureDelegate,
12187                 timeout: (this.timeout*1000),
12188                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
12189             });
12190             this.showLoading.defer(1, this);
12191         }
12192     },
12193
12194     /**
12195      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
12196      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
12197      */
12198     refresh : function(callback){
12199         if(this.defaultUrl == null){
12200             return;
12201         }
12202         this.update(this.defaultUrl, null, callback, true);
12203     },
12204
12205     /**
12206      * Set this element to auto refresh.
12207      * @param {Number} interval How often to update (in seconds).
12208      * @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)
12209      * @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}
12210      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
12211      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
12212      */
12213     startAutoRefresh : function(interval, url, params, callback, refreshNow){
12214         if(refreshNow){
12215             this.update(url || this.defaultUrl, params, callback, true);
12216         }
12217         if(this.autoRefreshProcId){
12218             clearInterval(this.autoRefreshProcId);
12219         }
12220         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
12221     },
12222
12223     /**
12224      * Stop auto refresh on this element.
12225      */
12226      stopAutoRefresh : function(){
12227         if(this.autoRefreshProcId){
12228             clearInterval(this.autoRefreshProcId);
12229             delete this.autoRefreshProcId;
12230         }
12231     },
12232
12233     isAutoRefreshing : function(){
12234        return this.autoRefreshProcId ? true : false;
12235     },
12236     /**
12237      * Called to update the element to "Loading" state. Override to perform custom action.
12238      */
12239     showLoading : function(){
12240         if(this.showLoadIndicator){
12241             this.el.update(this.indicatorText);
12242         }
12243     },
12244
12245     /**
12246      * Adds unique parameter to query string if disableCaching = true
12247      * @private
12248      */
12249     prepareUrl : function(url){
12250         if(this.disableCaching){
12251             var append = "_dc=" + (new Date().getTime());
12252             if(url.indexOf("?") !== -1){
12253                 url += "&" + append;
12254             }else{
12255                 url += "?" + append;
12256             }
12257         }
12258         return url;
12259     },
12260
12261     /**
12262      * @private
12263      */
12264     processSuccess : function(response){
12265         this.transaction = null;
12266         if(response.argument.form && response.argument.reset){
12267             try{ // put in try/catch since some older FF releases had problems with this
12268                 response.argument.form.reset();
12269             }catch(e){}
12270         }
12271         if(this.loadScripts){
12272             this.renderer.render(this.el, response, this,
12273                 this.updateComplete.createDelegate(this, [response]));
12274         }else{
12275             this.renderer.render(this.el, response, this);
12276             this.updateComplete(response);
12277         }
12278     },
12279
12280     updateComplete : function(response){
12281         this.fireEvent("update", this.el, response);
12282         if(typeof response.argument.callback == "function"){
12283             response.argument.callback(this.el, true, response);
12284         }
12285     },
12286
12287     /**
12288      * @private
12289      */
12290     processFailure : function(response){
12291         this.transaction = null;
12292         this.fireEvent("failure", this.el, response);
12293         if(typeof response.argument.callback == "function"){
12294             response.argument.callback(this.el, false, response);
12295         }
12296     },
12297
12298     /**
12299      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
12300      * @param {Object} renderer The object implementing the render() method
12301      */
12302     setRenderer : function(renderer){
12303         this.renderer = renderer;
12304     },
12305
12306     getRenderer : function(){
12307        return this.renderer;
12308     },
12309
12310     /**
12311      * Set the defaultUrl used for updates
12312      * @param {String/Function} defaultUrl The url or a function to call to get the url
12313      */
12314     setDefaultUrl : function(defaultUrl){
12315         this.defaultUrl = defaultUrl;
12316     },
12317
12318     /**
12319      * Aborts the executing transaction
12320      */
12321     abort : function(){
12322         if(this.transaction){
12323             Roo.Ajax.abort(this.transaction);
12324         }
12325     },
12326
12327     /**
12328      * Returns true if an update is in progress
12329      * @return {Boolean}
12330      */
12331     isUpdating : function(){
12332         if(this.transaction){
12333             return Roo.Ajax.isLoading(this.transaction);
12334         }
12335         return false;
12336     }
12337 });
12338
12339 /**
12340  * @class Roo.UpdateManager.defaults
12341  * @static (not really - but it helps the doc tool)
12342  * The defaults collection enables customizing the default properties of UpdateManager
12343  */
12344    Roo.UpdateManager.defaults = {
12345        /**
12346          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
12347          * @type Number
12348          */
12349          timeout : 30,
12350
12351          /**
12352          * True to process scripts by default (Defaults to false).
12353          * @type Boolean
12354          */
12355         loadScripts : false,
12356
12357         /**
12358         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
12359         * @type String
12360         */
12361         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
12362         /**
12363          * Whether to append unique parameter on get request to disable caching (Defaults to false).
12364          * @type Boolean
12365          */
12366         disableCaching : false,
12367         /**
12368          * Whether to show indicatorText when loading (Defaults to true).
12369          * @type Boolean
12370          */
12371         showLoadIndicator : true,
12372         /**
12373          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12374          * @type String
12375          */
12376         indicatorText : '<div class="loading-indicator">Loading...</div>'
12377    };
12378
12379 /**
12380  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
12381  *Usage:
12382  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
12383  * @param {String/HTMLElement/Roo.Element} el The element to update
12384  * @param {String} url The url
12385  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
12386  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
12387  * @static
12388  * @deprecated
12389  * @member Roo.UpdateManager
12390  */
12391 Roo.UpdateManager.updateElement = function(el, url, params, options){
12392     var um = Roo.get(el, true).getUpdateManager();
12393     Roo.apply(um, options);
12394     um.update(url, params, options ? options.callback : null);
12395 };
12396 // alias for backwards compat
12397 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
12398 /**
12399  * @class Roo.UpdateManager.BasicRenderer
12400  * Default Content renderer. Updates the elements innerHTML with the responseText.
12401  */
12402 Roo.UpdateManager.BasicRenderer = function(){};
12403
12404 Roo.UpdateManager.BasicRenderer.prototype = {
12405     /**
12406      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
12407      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
12408      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
12409      * @param {Roo.Element} el The element being rendered
12410      * @param {Object} response The YUI Connect response object
12411      * @param {UpdateManager} updateManager The calling update manager
12412      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
12413      */
12414      render : function(el, response, updateManager, callback){
12415         el.update(response.responseText, updateManager.loadScripts, callback);
12416     }
12417 };
12418 /*
12419  * Based on:
12420  * Roo JS
12421  * (c)) Alan Knowles
12422  * Licence : LGPL
12423  */
12424
12425
12426 /**
12427  * @class Roo.DomTemplate
12428  * @extends Roo.Template
12429  * An effort at a dom based template engine..
12430  *
12431  * Similar to XTemplate, except it uses dom parsing to create the template..
12432  *
12433  * Supported features:
12434  *
12435  *  Tags:
12436
12437 <pre><code>
12438       {a_variable} - output encoded.
12439       {a_variable.format:("Y-m-d")} - call a method on the variable
12440       {a_variable:raw} - unencoded output
12441       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
12442       {a_variable:this.method_on_template(...)} - call a method on the template object.
12443  
12444 </code></pre>
12445  *  The tpl tag:
12446 <pre><code>
12447         &lt;div roo-for="a_variable or condition.."&gt;&lt;/div&gt;
12448         &lt;div roo-if="a_variable or condition"&gt;&lt;/div&gt;
12449         &lt;div roo-exec="some javascript"&gt;&lt;/div&gt;
12450         &lt;div roo-name="named_template"&gt;&lt;/div&gt; 
12451   
12452 </code></pre>
12453  *      
12454  */
12455 Roo.DomTemplate = function()
12456 {
12457      Roo.DomTemplate.superclass.constructor.apply(this, arguments);
12458      if (this.html) {
12459         this.compile();
12460      }
12461 };
12462
12463
12464 Roo.extend(Roo.DomTemplate, Roo.Template, {
12465     /**
12466      * id counter for sub templates.
12467      */
12468     id : 0,
12469     /**
12470      * flag to indicate if dom parser is inside a pre,
12471      * it will strip whitespace if not.
12472      */
12473     inPre : false,
12474     
12475     /**
12476      * The various sub templates
12477      */
12478     tpls : false,
12479     
12480     
12481     
12482     /**
12483      *
12484      * basic tag replacing syntax
12485      * WORD:WORD()
12486      *
12487      * // you can fake an object call by doing this
12488      *  x.t:(test,tesT) 
12489      * 
12490      */
12491     re : /(\{|\%7B)([\w-\.]+)(?:\:([\w\.]*)(?:\(([^)]*?)?\))?)?(\}|\%7D)/g,
12492     //re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
12493     
12494     iterChild : function (node, method) {
12495         
12496         var oldPre = this.inPre;
12497         if (node.tagName == 'PRE') {
12498             this.inPre = true;
12499         }
12500         for( var i = 0; i < node.childNodes.length; i++) {
12501             method.call(this, node.childNodes[i]);
12502         }
12503         this.inPre = oldPre;
12504     },
12505     
12506     
12507     
12508     /**
12509      * compile the template
12510      *
12511      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
12512      *
12513      */
12514     compile: function()
12515     {
12516         var s = this.html;
12517         
12518         // covert the html into DOM...
12519         var doc = false;
12520         var div =false;
12521         try {
12522             doc = document.implementation.createHTMLDocument("");
12523             doc.documentElement.innerHTML =   this.html  ;
12524             div = doc.documentElement;
12525         } catch (e) {
12526             // old IE... - nasty -- it causes all sorts of issues.. with
12527             // images getting pulled from server..
12528             div = document.createElement('div');
12529             div.innerHTML = this.html;
12530         }
12531         //doc.documentElement.innerHTML = htmlBody
12532          
12533         
12534         
12535         this.tpls = [];
12536         var _t = this;
12537         this.iterChild(div, function(n) {_t.compileNode(n, true); });
12538         
12539         var tpls = this.tpls;
12540         
12541         // create a top level template from the snippet..
12542         
12543         //Roo.log(div.innerHTML);
12544         
12545         var tpl = {
12546             uid : 'master',
12547             id : this.id++,
12548             attr : false,
12549             value : false,
12550             body : div.innerHTML,
12551             
12552             forCall : false,
12553             execCall : false,
12554             dom : div,
12555             isTop : true
12556             
12557         };
12558         tpls.unshift(tpl);
12559         
12560         
12561         // compile them...
12562         this.tpls = [];
12563         Roo.each(tpls, function(tp){
12564             this.compileTpl(tp);
12565             this.tpls[tp.id] = tp;
12566         }, this);
12567         
12568         this.master = tpls[0];
12569         return this;
12570         
12571         
12572     },
12573     
12574     compileNode : function(node, istop) {
12575         // test for
12576         //Roo.log(node);
12577         
12578         
12579         // skip anything not a tag..
12580         if (node.nodeType != 1) {
12581             if (node.nodeType == 3 && !this.inPre) {
12582                 // reduce white space..
12583                 node.nodeValue = node.nodeValue.replace(/\s+/g, ' '); 
12584                 
12585             }
12586             return;
12587         }
12588         
12589         var tpl = {
12590             uid : false,
12591             id : false,
12592             attr : false,
12593             value : false,
12594             body : '',
12595             
12596             forCall : false,
12597             execCall : false,
12598             dom : false,
12599             isTop : istop
12600             
12601             
12602         };
12603         
12604         
12605         switch(true) {
12606             case (node.hasAttribute('roo-for')): tpl.attr = 'for'; break;
12607             case (node.hasAttribute('roo-if')): tpl.attr = 'if'; break;
12608             case (node.hasAttribute('roo-name')): tpl.attr = 'name'; break;
12609             case (node.hasAttribute('roo-exec')): tpl.attr = 'exec'; break;
12610             // no default..
12611         }
12612         
12613         
12614         if (!tpl.attr) {
12615             // just itterate children..
12616             this.iterChild(node,this.compileNode);
12617             return;
12618         }
12619         tpl.uid = this.id++;
12620         tpl.value = node.getAttribute('roo-' +  tpl.attr);
12621         node.removeAttribute('roo-'+ tpl.attr);
12622         if (tpl.attr != 'name') {
12623             var placeholder = document.createTextNode('{domtpl' + tpl.uid + '}');
12624             node.parentNode.replaceChild(placeholder,  node);
12625         } else {
12626             
12627             var placeholder =  document.createElement('span');
12628             placeholder.className = 'roo-tpl-' + tpl.value;
12629             node.parentNode.replaceChild(placeholder,  node);
12630         }
12631         
12632         // parent now sees '{domtplXXXX}
12633         this.iterChild(node,this.compileNode);
12634         
12635         // we should now have node body...
12636         var div = document.createElement('div');
12637         div.appendChild(node);
12638         tpl.dom = node;
12639         // this has the unfortunate side effect of converting tagged attributes
12640         // eg. href="{...}" into %7C...%7D
12641         // this has been fixed by searching for those combo's although it's a bit hacky..
12642         
12643         
12644         tpl.body = div.innerHTML;
12645         
12646         
12647          
12648         tpl.id = tpl.uid;
12649         switch(tpl.attr) {
12650             case 'for' :
12651                 switch (tpl.value) {
12652                     case '.':  tpl.forCall = new Function('values', 'parent', 'with(values){ return values; }'); break;
12653                     case '..': tpl.forCall= new Function('values', 'parent', 'with(values){ return parent; }'); break;
12654                     default:   tpl.forCall= new Function('values', 'parent', 'with(values){ return '+tpl.value+'; }');
12655                 }
12656                 break;
12657             
12658             case 'exec':
12659                 tpl.execCall = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12660                 break;
12661             
12662             case 'if':     
12663                 tpl.ifCall = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12664                 break;
12665             
12666             case 'name':
12667                 tpl.id  = tpl.value; // replace non characters???
12668                 break;
12669             
12670         }
12671         
12672         
12673         this.tpls.push(tpl);
12674         
12675         
12676         
12677     },
12678     
12679     
12680     
12681     
12682     /**
12683      * Compile a segment of the template into a 'sub-template'
12684      *
12685      * 
12686      * 
12687      *
12688      */
12689     compileTpl : function(tpl)
12690     {
12691         var fm = Roo.util.Format;
12692         var useF = this.disableFormats !== true;
12693         
12694         var sep = Roo.isGecko ? "+\n" : ",\n";
12695         
12696         var undef = function(str) {
12697             Roo.debug && Roo.log("Property not found :"  + str);
12698             return '';
12699         };
12700           
12701         //Roo.log(tpl.body);
12702         
12703         
12704         
12705         var fn = function(m, lbrace, name, format, args)
12706         {
12707             //Roo.log("ARGS");
12708             //Roo.log(arguments);
12709             args = args ? args.replace(/\\'/g,"'") : args;
12710             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
12711             if (typeof(format) == 'undefined') {
12712                 format =  'htmlEncode'; 
12713             }
12714             if (format == 'raw' ) {
12715                 format = false;
12716             }
12717             
12718             if(name.substr(0, 6) == 'domtpl'){
12719                 return "'"+ sep +'this.applySubTemplate('+name.substr(6)+', values, parent)'+sep+"'";
12720             }
12721             
12722             // build an array of options to determine if value is undefined..
12723             
12724             // basically get 'xxxx.yyyy' then do
12725             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
12726             //    (function () { Roo.log("Property not found"); return ''; })() :
12727             //    ......
12728             
12729             var udef_ar = [];
12730             var lookfor = '';
12731             Roo.each(name.split('.'), function(st) {
12732                 lookfor += (lookfor.length ? '.': '') + st;
12733                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
12734             });
12735             
12736             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
12737             
12738             
12739             if(format && useF){
12740                 
12741                 args = args ? ',' + args : "";
12742                  
12743                 if(format.substr(0, 5) != "this."){
12744                     format = "fm." + format + '(';
12745                 }else{
12746                     format = 'this.call("'+ format.substr(5) + '", ';
12747                     args = ", values";
12748                 }
12749                 
12750                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
12751             }
12752              
12753             if (args && args.length) {
12754                 // called with xxyx.yuu:(test,test)
12755                 // change to ()
12756                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
12757             }
12758             // raw.. - :raw modifier..
12759             return "'"+ sep + udef_st  + name + ")"+sep+"'";
12760             
12761         };
12762         var body;
12763         // branched to use + in gecko and [].join() in others
12764         if(Roo.isGecko){
12765             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
12766                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
12767                     "';};};";
12768         }else{
12769             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
12770             body.push(tpl.body.replace(/(\r\n|\n)/g,
12771                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
12772             body.push("'].join('');};};");
12773             body = body.join('');
12774         }
12775         
12776         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
12777        
12778         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
12779         eval(body);
12780         
12781         return this;
12782     },
12783      
12784     /**
12785      * same as applyTemplate, except it's done to one of the subTemplates
12786      * when using named templates, you can do:
12787      *
12788      * var str = pl.applySubTemplate('your-name', values);
12789      *
12790      * 
12791      * @param {Number} id of the template
12792      * @param {Object} values to apply to template
12793      * @param {Object} parent (normaly the instance of this object)
12794      */
12795     applySubTemplate : function(id, values, parent)
12796     {
12797         
12798         
12799         var t = this.tpls[id];
12800         
12801         
12802         try { 
12803             if(t.ifCall && !t.ifCall.call(this, values, parent)){
12804                 Roo.debug && Roo.log('if call on ' + t.value + ' return false');
12805                 return '';
12806             }
12807         } catch(e) {
12808             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-if="' + t.value + '" - ' + e.toString());
12809             Roo.log(values);
12810           
12811             return '';
12812         }
12813         try { 
12814             
12815             if(t.execCall && t.execCall.call(this, values, parent)){
12816                 return '';
12817             }
12818         } catch(e) {
12819             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12820             Roo.log(values);
12821             return '';
12822         }
12823         
12824         try {
12825             var vs = t.forCall ? t.forCall.call(this, values, parent) : values;
12826             parent = t.target ? values : parent;
12827             if(t.forCall && vs instanceof Array){
12828                 var buf = [];
12829                 for(var i = 0, len = vs.length; i < len; i++){
12830                     try {
12831                         buf[buf.length] = t.compiled.call(this, vs[i], parent);
12832                     } catch (e) {
12833                         Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12834                         Roo.log(e.body);
12835                         //Roo.log(t.compiled);
12836                         Roo.log(vs[i]);
12837                     }   
12838                 }
12839                 return buf.join('');
12840             }
12841         } catch (e) {
12842             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12843             Roo.log(values);
12844             return '';
12845         }
12846         try {
12847             return t.compiled.call(this, vs, parent);
12848         } catch (e) {
12849             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12850             Roo.log(e.body);
12851             //Roo.log(t.compiled);
12852             Roo.log(values);
12853             return '';
12854         }
12855     },
12856
12857    
12858
12859     applyTemplate : function(values){
12860         return this.master.compiled.call(this, values, {});
12861         //var s = this.subs;
12862     },
12863
12864     apply : function(){
12865         return this.applyTemplate.apply(this, arguments);
12866     }
12867
12868  });
12869
12870 Roo.DomTemplate.from = function(el){
12871     el = Roo.getDom(el);
12872     return new Roo.Domtemplate(el.value || el.innerHTML);
12873 };/*
12874  * Based on:
12875  * Ext JS Library 1.1.1
12876  * Copyright(c) 2006-2007, Ext JS, LLC.
12877  *
12878  * Originally Released Under LGPL - original licence link has changed is not relivant.
12879  *
12880  * Fork - LGPL
12881  * <script type="text/javascript">
12882  */
12883
12884 /**
12885  * @class Roo.util.DelayedTask
12886  * Provides a convenient method of performing setTimeout where a new
12887  * timeout cancels the old timeout. An example would be performing validation on a keypress.
12888  * You can use this class to buffer
12889  * the keypress events for a certain number of milliseconds, and perform only if they stop
12890  * for that amount of time.
12891  * @constructor The parameters to this constructor serve as defaults and are not required.
12892  * @param {Function} fn (optional) The default function to timeout
12893  * @param {Object} scope (optional) The default scope of that timeout
12894  * @param {Array} args (optional) The default Array of arguments
12895  */
12896 Roo.util.DelayedTask = function(fn, scope, args){
12897     var id = null, d, t;
12898
12899     var call = function(){
12900         var now = new Date().getTime();
12901         if(now - t >= d){
12902             clearInterval(id);
12903             id = null;
12904             fn.apply(scope, args || []);
12905         }
12906     };
12907     /**
12908      * Cancels any pending timeout and queues a new one
12909      * @param {Number} delay The milliseconds to delay
12910      * @param {Function} newFn (optional) Overrides function passed to constructor
12911      * @param {Object} newScope (optional) Overrides scope passed to constructor
12912      * @param {Array} newArgs (optional) Overrides args passed to constructor
12913      */
12914     this.delay = function(delay, newFn, newScope, newArgs){
12915         if(id && delay != d){
12916             this.cancel();
12917         }
12918         d = delay;
12919         t = new Date().getTime();
12920         fn = newFn || fn;
12921         scope = newScope || scope;
12922         args = newArgs || args;
12923         if(!id){
12924             id = setInterval(call, d);
12925         }
12926     };
12927
12928     /**
12929      * Cancel the last queued timeout
12930      */
12931     this.cancel = function(){
12932         if(id){
12933             clearInterval(id);
12934             id = null;
12935         }
12936     };
12937 };/*
12938  * Based on:
12939  * Ext JS Library 1.1.1
12940  * Copyright(c) 2006-2007, Ext JS, LLC.
12941  *
12942  * Originally Released Under LGPL - original licence link has changed is not relivant.
12943  *
12944  * Fork - LGPL
12945  * <script type="text/javascript">
12946  */
12947  
12948  
12949 Roo.util.TaskRunner = function(interval){
12950     interval = interval || 10;
12951     var tasks = [], removeQueue = [];
12952     var id = 0;
12953     var running = false;
12954
12955     var stopThread = function(){
12956         running = false;
12957         clearInterval(id);
12958         id = 0;
12959     };
12960
12961     var startThread = function(){
12962         if(!running){
12963             running = true;
12964             id = setInterval(runTasks, interval);
12965         }
12966     };
12967
12968     var removeTask = function(task){
12969         removeQueue.push(task);
12970         if(task.onStop){
12971             task.onStop();
12972         }
12973     };
12974
12975     var runTasks = function(){
12976         if(removeQueue.length > 0){
12977             for(var i = 0, len = removeQueue.length; i < len; i++){
12978                 tasks.remove(removeQueue[i]);
12979             }
12980             removeQueue = [];
12981             if(tasks.length < 1){
12982                 stopThread();
12983                 return;
12984             }
12985         }
12986         var now = new Date().getTime();
12987         for(var i = 0, len = tasks.length; i < len; ++i){
12988             var t = tasks[i];
12989             var itime = now - t.taskRunTime;
12990             if(t.interval <= itime){
12991                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
12992                 t.taskRunTime = now;
12993                 if(rt === false || t.taskRunCount === t.repeat){
12994                     removeTask(t);
12995                     return;
12996                 }
12997             }
12998             if(t.duration && t.duration <= (now - t.taskStartTime)){
12999                 removeTask(t);
13000             }
13001         }
13002     };
13003
13004     /**
13005      * Queues a new task.
13006      * @param {Object} task
13007      */
13008     this.start = function(task){
13009         tasks.push(task);
13010         task.taskStartTime = new Date().getTime();
13011         task.taskRunTime = 0;
13012         task.taskRunCount = 0;
13013         startThread();
13014         return task;
13015     };
13016
13017     this.stop = function(task){
13018         removeTask(task);
13019         return task;
13020     };
13021
13022     this.stopAll = function(){
13023         stopThread();
13024         for(var i = 0, len = tasks.length; i < len; i++){
13025             if(tasks[i].onStop){
13026                 tasks[i].onStop();
13027             }
13028         }
13029         tasks = [];
13030         removeQueue = [];
13031     };
13032 };
13033
13034 Roo.TaskMgr = new Roo.util.TaskRunner();/*
13035  * Based on:
13036  * Ext JS Library 1.1.1
13037  * Copyright(c) 2006-2007, Ext JS, LLC.
13038  *
13039  * Originally Released Under LGPL - original licence link has changed is not relivant.
13040  *
13041  * Fork - LGPL
13042  * <script type="text/javascript">
13043  */
13044
13045  
13046 /**
13047  * @class Roo.util.MixedCollection
13048  * @extends Roo.util.Observable
13049  * A Collection class that maintains both numeric indexes and keys and exposes events.
13050  * @constructor
13051  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
13052  * collection (defaults to false)
13053  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
13054  * and return the key value for that item.  This is used when available to look up the key on items that
13055  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
13056  * equivalent to providing an implementation for the {@link #getKey} method.
13057  */
13058 Roo.util.MixedCollection = function(allowFunctions, keyFn){
13059     this.items = [];
13060     this.map = {};
13061     this.keys = [];
13062     this.length = 0;
13063     this.addEvents({
13064         /**
13065          * @event clear
13066          * Fires when the collection is cleared.
13067          */
13068         "clear" : true,
13069         /**
13070          * @event add
13071          * Fires when an item is added to the collection.
13072          * @param {Number} index The index at which the item was added.
13073          * @param {Object} o The item added.
13074          * @param {String} key The key associated with the added item.
13075          */
13076         "add" : true,
13077         /**
13078          * @event replace
13079          * Fires when an item is replaced in the collection.
13080          * @param {String} key he key associated with the new added.
13081          * @param {Object} old The item being replaced.
13082          * @param {Object} new The new item.
13083          */
13084         "replace" : true,
13085         /**
13086          * @event remove
13087          * Fires when an item is removed from the collection.
13088          * @param {Object} o The item being removed.
13089          * @param {String} key (optional) The key associated with the removed item.
13090          */
13091         "remove" : true,
13092         "sort" : true
13093     });
13094     this.allowFunctions = allowFunctions === true;
13095     if(keyFn){
13096         this.getKey = keyFn;
13097     }
13098     Roo.util.MixedCollection.superclass.constructor.call(this);
13099 };
13100
13101 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
13102     allowFunctions : false,
13103     
13104 /**
13105  * Adds an item to the collection.
13106  * @param {String} key The key to associate with the item
13107  * @param {Object} o The item to add.
13108  * @return {Object} The item added.
13109  */
13110     add : function(key, o){
13111         if(arguments.length == 1){
13112             o = arguments[0];
13113             key = this.getKey(o);
13114         }
13115         if(typeof key == "undefined" || key === null){
13116             this.length++;
13117             this.items.push(o);
13118             this.keys.push(null);
13119         }else{
13120             var old = this.map[key];
13121             if(old){
13122                 return this.replace(key, o);
13123             }
13124             this.length++;
13125             this.items.push(o);
13126             this.map[key] = o;
13127             this.keys.push(key);
13128         }
13129         this.fireEvent("add", this.length-1, o, key);
13130         return o;
13131     },
13132        
13133 /**
13134   * MixedCollection has a generic way to fetch keys if you implement getKey.
13135 <pre><code>
13136 // normal way
13137 var mc = new Roo.util.MixedCollection();
13138 mc.add(someEl.dom.id, someEl);
13139 mc.add(otherEl.dom.id, otherEl);
13140 //and so on
13141
13142 // using getKey
13143 var mc = new Roo.util.MixedCollection();
13144 mc.getKey = function(el){
13145    return el.dom.id;
13146 };
13147 mc.add(someEl);
13148 mc.add(otherEl);
13149
13150 // or via the constructor
13151 var mc = new Roo.util.MixedCollection(false, function(el){
13152    return el.dom.id;
13153 });
13154 mc.add(someEl);
13155 mc.add(otherEl);
13156 </code></pre>
13157  * @param o {Object} The item for which to find the key.
13158  * @return {Object} The key for the passed item.
13159  */
13160     getKey : function(o){
13161          return o.id; 
13162     },
13163    
13164 /**
13165  * Replaces an item in the collection.
13166  * @param {String} key The key associated with the item to replace, or the item to replace.
13167  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
13168  * @return {Object}  The new item.
13169  */
13170     replace : function(key, o){
13171         if(arguments.length == 1){
13172             o = arguments[0];
13173             key = this.getKey(o);
13174         }
13175         var old = this.item(key);
13176         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
13177              return this.add(key, o);
13178         }
13179         var index = this.indexOfKey(key);
13180         this.items[index] = o;
13181         this.map[key] = o;
13182         this.fireEvent("replace", key, old, o);
13183         return o;
13184     },
13185    
13186 /**
13187  * Adds all elements of an Array or an Object to the collection.
13188  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
13189  * an Array of values, each of which are added to the collection.
13190  */
13191     addAll : function(objs){
13192         if(arguments.length > 1 || objs instanceof Array){
13193             var args = arguments.length > 1 ? arguments : objs;
13194             for(var i = 0, len = args.length; i < len; i++){
13195                 this.add(args[i]);
13196             }
13197         }else{
13198             for(var key in objs){
13199                 if(this.allowFunctions || typeof objs[key] != "function"){
13200                     this.add(key, objs[key]);
13201                 }
13202             }
13203         }
13204     },
13205    
13206 /**
13207  * Executes the specified function once for every item in the collection, passing each
13208  * item as the first and only parameter. returning false from the function will stop the iteration.
13209  * @param {Function} fn The function to execute for each item.
13210  * @param {Object} scope (optional) The scope in which to execute the function.
13211  */
13212     each : function(fn, scope){
13213         var items = [].concat(this.items); // each safe for removal
13214         for(var i = 0, len = items.length; i < len; i++){
13215             if(fn.call(scope || items[i], items[i], i, len) === false){
13216                 break;
13217             }
13218         }
13219     },
13220    
13221 /**
13222  * Executes the specified function once for every key in the collection, passing each
13223  * key, and its associated item as the first two parameters.
13224  * @param {Function} fn The function to execute for each item.
13225  * @param {Object} scope (optional) The scope in which to execute the function.
13226  */
13227     eachKey : function(fn, scope){
13228         for(var i = 0, len = this.keys.length; i < len; i++){
13229             fn.call(scope || window, this.keys[i], this.items[i], i, len);
13230         }
13231     },
13232    
13233 /**
13234  * Returns the first item in the collection which elicits a true return value from the
13235  * passed selection function.
13236  * @param {Function} fn The selection function to execute for each item.
13237  * @param {Object} scope (optional) The scope in which to execute the function.
13238  * @return {Object} The first item in the collection which returned true from the selection function.
13239  */
13240     find : function(fn, scope){
13241         for(var i = 0, len = this.items.length; i < len; i++){
13242             if(fn.call(scope || window, this.items[i], this.keys[i])){
13243                 return this.items[i];
13244             }
13245         }
13246         return null;
13247     },
13248    
13249 /**
13250  * Inserts an item at the specified index in the collection.
13251  * @param {Number} index The index to insert the item at.
13252  * @param {String} key The key to associate with the new item, or the item itself.
13253  * @param {Object} o  (optional) If the second parameter was a key, the new item.
13254  * @return {Object} The item inserted.
13255  */
13256     insert : function(index, key, o){
13257         if(arguments.length == 2){
13258             o = arguments[1];
13259             key = this.getKey(o);
13260         }
13261         if(index >= this.length){
13262             return this.add(key, o);
13263         }
13264         this.length++;
13265         this.items.splice(index, 0, o);
13266         if(typeof key != "undefined" && key != null){
13267             this.map[key] = o;
13268         }
13269         this.keys.splice(index, 0, key);
13270         this.fireEvent("add", index, o, key);
13271         return o;
13272     },
13273    
13274 /**
13275  * Removed an item from the collection.
13276  * @param {Object} o The item to remove.
13277  * @return {Object} The item removed.
13278  */
13279     remove : function(o){
13280         return this.removeAt(this.indexOf(o));
13281     },
13282    
13283 /**
13284  * Remove an item from a specified index in the collection.
13285  * @param {Number} index The index within the collection of the item to remove.
13286  */
13287     removeAt : function(index){
13288         if(index < this.length && index >= 0){
13289             this.length--;
13290             var o = this.items[index];
13291             this.items.splice(index, 1);
13292             var key = this.keys[index];
13293             if(typeof key != "undefined"){
13294                 delete this.map[key];
13295             }
13296             this.keys.splice(index, 1);
13297             this.fireEvent("remove", o, key);
13298         }
13299     },
13300    
13301 /**
13302  * Removed an item associated with the passed key fom the collection.
13303  * @param {String} key The key of the item to remove.
13304  */
13305     removeKey : function(key){
13306         return this.removeAt(this.indexOfKey(key));
13307     },
13308    
13309 /**
13310  * Returns the number of items in the collection.
13311  * @return {Number} the number of items in the collection.
13312  */
13313     getCount : function(){
13314         return this.length; 
13315     },
13316    
13317 /**
13318  * Returns index within the collection of the passed Object.
13319  * @param {Object} o The item to find the index of.
13320  * @return {Number} index of the item.
13321  */
13322     indexOf : function(o){
13323         if(!this.items.indexOf){
13324             for(var i = 0, len = this.items.length; i < len; i++){
13325                 if(this.items[i] == o) {
13326                     return i;
13327                 }
13328             }
13329             return -1;
13330         }else{
13331             return this.items.indexOf(o);
13332         }
13333     },
13334    
13335 /**
13336  * Returns index within the collection of the passed key.
13337  * @param {String} key The key to find the index of.
13338  * @return {Number} index of the key.
13339  */
13340     indexOfKey : function(key){
13341         if(!this.keys.indexOf){
13342             for(var i = 0, len = this.keys.length; i < len; i++){
13343                 if(this.keys[i] == key) {
13344                     return i;
13345                 }
13346             }
13347             return -1;
13348         }else{
13349             return this.keys.indexOf(key);
13350         }
13351     },
13352    
13353 /**
13354  * Returns the item associated with the passed key OR index. Key has priority over index.
13355  * @param {String/Number} key The key or index of the item.
13356  * @return {Object} The item associated with the passed key.
13357  */
13358     item : function(key){
13359         if (key === 'length') {
13360             return null;
13361         }
13362         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
13363         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
13364     },
13365     
13366 /**
13367  * Returns the item at the specified index.
13368  * @param {Number} index The index of the item.
13369  * @return {Object}
13370  */
13371     itemAt : function(index){
13372         return this.items[index];
13373     },
13374     
13375 /**
13376  * Returns the item associated with the passed key.
13377  * @param {String/Number} key The key of the item.
13378  * @return {Object} The item associated with the passed key.
13379  */
13380     key : function(key){
13381         return this.map[key];
13382     },
13383    
13384 /**
13385  * Returns true if the collection contains the passed Object as an item.
13386  * @param {Object} o  The Object to look for in the collection.
13387  * @return {Boolean} True if the collection contains the Object as an item.
13388  */
13389     contains : function(o){
13390         return this.indexOf(o) != -1;
13391     },
13392    
13393 /**
13394  * Returns true if the collection contains the passed Object as a key.
13395  * @param {String} key The key to look for in the collection.
13396  * @return {Boolean} True if the collection contains the Object as a key.
13397  */
13398     containsKey : function(key){
13399         return typeof this.map[key] != "undefined";
13400     },
13401    
13402 /**
13403  * Removes all items from the collection.
13404  */
13405     clear : function(){
13406         this.length = 0;
13407         this.items = [];
13408         this.keys = [];
13409         this.map = {};
13410         this.fireEvent("clear");
13411     },
13412    
13413 /**
13414  * Returns the first item in the collection.
13415  * @return {Object} the first item in the collection..
13416  */
13417     first : function(){
13418         return this.items[0]; 
13419     },
13420    
13421 /**
13422  * Returns the last item in the collection.
13423  * @return {Object} the last item in the collection..
13424  */
13425     last : function(){
13426         return this.items[this.length-1];   
13427     },
13428     
13429     _sort : function(property, dir, fn){
13430         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
13431         fn = fn || function(a, b){
13432             return a-b;
13433         };
13434         var c = [], k = this.keys, items = this.items;
13435         for(var i = 0, len = items.length; i < len; i++){
13436             c[c.length] = {key: k[i], value: items[i], index: i};
13437         }
13438         c.sort(function(a, b){
13439             var v = fn(a[property], b[property]) * dsc;
13440             if(v == 0){
13441                 v = (a.index < b.index ? -1 : 1);
13442             }
13443             return v;
13444         });
13445         for(var i = 0, len = c.length; i < len; i++){
13446             items[i] = c[i].value;
13447             k[i] = c[i].key;
13448         }
13449         this.fireEvent("sort", this);
13450     },
13451     
13452     /**
13453      * Sorts this collection with the passed comparison function
13454      * @param {String} direction (optional) "ASC" or "DESC"
13455      * @param {Function} fn (optional) comparison function
13456      */
13457     sort : function(dir, fn){
13458         this._sort("value", dir, fn);
13459     },
13460     
13461     /**
13462      * Sorts this collection by keys
13463      * @param {String} direction (optional) "ASC" or "DESC"
13464      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
13465      */
13466     keySort : function(dir, fn){
13467         this._sort("key", dir, fn || function(a, b){
13468             return String(a).toUpperCase()-String(b).toUpperCase();
13469         });
13470     },
13471     
13472     /**
13473      * Returns a range of items in this collection
13474      * @param {Number} startIndex (optional) defaults to 0
13475      * @param {Number} endIndex (optional) default to the last item
13476      * @return {Array} An array of items
13477      */
13478     getRange : function(start, end){
13479         var items = this.items;
13480         if(items.length < 1){
13481             return [];
13482         }
13483         start = start || 0;
13484         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
13485         var r = [];
13486         if(start <= end){
13487             for(var i = start; i <= end; i++) {
13488                     r[r.length] = items[i];
13489             }
13490         }else{
13491             for(var i = start; i >= end; i--) {
13492                     r[r.length] = items[i];
13493             }
13494         }
13495         return r;
13496     },
13497         
13498     /**
13499      * Filter the <i>objects</i> in this collection by a specific property. 
13500      * Returns a new collection that has been filtered.
13501      * @param {String} property A property on your objects
13502      * @param {String/RegExp} value Either string that the property values 
13503      * should start with or a RegExp to test against the property
13504      * @return {MixedCollection} The new filtered collection
13505      */
13506     filter : function(property, value){
13507         if(!value.exec){ // not a regex
13508             value = String(value);
13509             if(value.length == 0){
13510                 return this.clone();
13511             }
13512             value = new RegExp("^" + Roo.escapeRe(value), "i");
13513         }
13514         return this.filterBy(function(o){
13515             return o && value.test(o[property]);
13516         });
13517         },
13518     
13519     /**
13520      * Filter by a function. * Returns a new collection that has been filtered.
13521      * The passed function will be called with each 
13522      * object in the collection. If the function returns true, the value is included 
13523      * otherwise it is filtered.
13524      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
13525      * @param {Object} scope (optional) The scope of the function (defaults to this) 
13526      * @return {MixedCollection} The new filtered collection
13527      */
13528     filterBy : function(fn, scope){
13529         var r = new Roo.util.MixedCollection();
13530         r.getKey = this.getKey;
13531         var k = this.keys, it = this.items;
13532         for(var i = 0, len = it.length; i < len; i++){
13533             if(fn.call(scope||this, it[i], k[i])){
13534                                 r.add(k[i], it[i]);
13535                         }
13536         }
13537         return r;
13538     },
13539     
13540     /**
13541      * Creates a duplicate of this collection
13542      * @return {MixedCollection}
13543      */
13544     clone : function(){
13545         var r = new Roo.util.MixedCollection();
13546         var k = this.keys, it = this.items;
13547         for(var i = 0, len = it.length; i < len; i++){
13548             r.add(k[i], it[i]);
13549         }
13550         r.getKey = this.getKey;
13551         return r;
13552     }
13553 });
13554 /**
13555  * Returns the item associated with the passed key or index.
13556  * @method
13557  * @param {String/Number} key The key or index of the item.
13558  * @return {Object} The item associated with the passed key.
13559  */
13560 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
13561  * Based on:
13562  * Ext JS Library 1.1.1
13563  * Copyright(c) 2006-2007, Ext JS, LLC.
13564  *
13565  * Originally Released Under LGPL - original licence link has changed is not relivant.
13566  *
13567  * Fork - LGPL
13568  * <script type="text/javascript">
13569  */
13570 /**
13571  * @class Roo.util.JSON
13572  * Modified version of Douglas Crockford"s json.js that doesn"t
13573  * mess with the Object prototype 
13574  * http://www.json.org/js.html
13575  * @singleton
13576  */
13577 Roo.util.JSON = new (function(){
13578     var useHasOwn = {}.hasOwnProperty ? true : false;
13579     
13580     // crashes Safari in some instances
13581     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
13582     
13583     var pad = function(n) {
13584         return n < 10 ? "0" + n : n;
13585     };
13586     
13587     var m = {
13588         "\b": '\\b',
13589         "\t": '\\t',
13590         "\n": '\\n',
13591         "\f": '\\f',
13592         "\r": '\\r',
13593         '"' : '\\"',
13594         "\\": '\\\\'
13595     };
13596
13597     var encodeString = function(s){
13598         if (/["\\\x00-\x1f]/.test(s)) {
13599             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
13600                 var c = m[b];
13601                 if(c){
13602                     return c;
13603                 }
13604                 c = b.charCodeAt();
13605                 return "\\u00" +
13606                     Math.floor(c / 16).toString(16) +
13607                     (c % 16).toString(16);
13608             }) + '"';
13609         }
13610         return '"' + s + '"';
13611     };
13612     
13613     var encodeArray = function(o){
13614         var a = ["["], b, i, l = o.length, v;
13615             for (i = 0; i < l; i += 1) {
13616                 v = o[i];
13617                 switch (typeof v) {
13618                     case "undefined":
13619                     case "function":
13620                     case "unknown":
13621                         break;
13622                     default:
13623                         if (b) {
13624                             a.push(',');
13625                         }
13626                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
13627                         b = true;
13628                 }
13629             }
13630             a.push("]");
13631             return a.join("");
13632     };
13633     
13634     var encodeDate = function(o){
13635         return '"' + o.getFullYear() + "-" +
13636                 pad(o.getMonth() + 1) + "-" +
13637                 pad(o.getDate()) + "T" +
13638                 pad(o.getHours()) + ":" +
13639                 pad(o.getMinutes()) + ":" +
13640                 pad(o.getSeconds()) + '"';
13641     };
13642     
13643     /**
13644      * Encodes an Object, Array or other value
13645      * @param {Mixed} o The variable to encode
13646      * @return {String} The JSON string
13647      */
13648     this.encode = function(o)
13649     {
13650         // should this be extended to fully wrap stringify..
13651         
13652         if(typeof o == "undefined" || o === null){
13653             return "null";
13654         }else if(o instanceof Array){
13655             return encodeArray(o);
13656         }else if(o instanceof Date){
13657             return encodeDate(o);
13658         }else if(typeof o == "string"){
13659             return encodeString(o);
13660         }else if(typeof o == "number"){
13661             return isFinite(o) ? String(o) : "null";
13662         }else if(typeof o == "boolean"){
13663             return String(o);
13664         }else {
13665             var a = ["{"], b, i, v;
13666             for (i in o) {
13667                 if(!useHasOwn || o.hasOwnProperty(i)) {
13668                     v = o[i];
13669                     switch (typeof v) {
13670                     case "undefined":
13671                     case "function":
13672                     case "unknown":
13673                         break;
13674                     default:
13675                         if(b){
13676                             a.push(',');
13677                         }
13678                         a.push(this.encode(i), ":",
13679                                 v === null ? "null" : this.encode(v));
13680                         b = true;
13681                     }
13682                 }
13683             }
13684             a.push("}");
13685             return a.join("");
13686         }
13687     };
13688     
13689     /**
13690      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
13691      * @param {String} json The JSON string
13692      * @return {Object} The resulting object
13693      */
13694     this.decode = function(json){
13695         
13696         return  /** eval:var:json */ eval("(" + json + ')');
13697     };
13698 })();
13699 /** 
13700  * Shorthand for {@link Roo.util.JSON#encode}
13701  * @member Roo encode 
13702  * @method */
13703 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
13704 /** 
13705  * Shorthand for {@link Roo.util.JSON#decode}
13706  * @member Roo decode 
13707  * @method */
13708 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
13709 /*
13710  * Based on:
13711  * Ext JS Library 1.1.1
13712  * Copyright(c) 2006-2007, Ext JS, LLC.
13713  *
13714  * Originally Released Under LGPL - original licence link has changed is not relivant.
13715  *
13716  * Fork - LGPL
13717  * <script type="text/javascript">
13718  */
13719  
13720 /**
13721  * @class Roo.util.Format
13722  * Reusable data formatting functions
13723  * @singleton
13724  */
13725 Roo.util.Format = function(){
13726     var trimRe = /^\s+|\s+$/g;
13727     return {
13728         /**
13729          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
13730          * @param {String} value The string to truncate
13731          * @param {Number} length The maximum length to allow before truncating
13732          * @return {String} The converted text
13733          */
13734         ellipsis : function(value, len){
13735             if(value && value.length > len){
13736                 return value.substr(0, len-3)+"...";
13737             }
13738             return value;
13739         },
13740
13741         /**
13742          * Checks a reference and converts it to empty string if it is undefined
13743          * @param {Mixed} value Reference to check
13744          * @return {Mixed} Empty string if converted, otherwise the original value
13745          */
13746         undef : function(value){
13747             return typeof value != "undefined" ? value : "";
13748         },
13749
13750         /**
13751          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
13752          * @param {String} value The string to encode
13753          * @return {String} The encoded text
13754          */
13755         htmlEncode : function(value){
13756             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
13757         },
13758
13759         /**
13760          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
13761          * @param {String} value The string to decode
13762          * @return {String} The decoded text
13763          */
13764         htmlDecode : function(value){
13765             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
13766         },
13767
13768         /**
13769          * Trims any whitespace from either side of a string
13770          * @param {String} value The text to trim
13771          * @return {String} The trimmed text
13772          */
13773         trim : function(value){
13774             return String(value).replace(trimRe, "");
13775         },
13776
13777         /**
13778          * Returns a substring from within an original string
13779          * @param {String} value The original text
13780          * @param {Number} start The start index of the substring
13781          * @param {Number} length The length of the substring
13782          * @return {String} The substring
13783          */
13784         substr : function(value, start, length){
13785             return String(value).substr(start, length);
13786         },
13787
13788         /**
13789          * Converts a string to all lower case letters
13790          * @param {String} value The text to convert
13791          * @return {String} The converted text
13792          */
13793         lowercase : function(value){
13794             return String(value).toLowerCase();
13795         },
13796
13797         /**
13798          * Converts a string to all upper case letters
13799          * @param {String} value The text to convert
13800          * @return {String} The converted text
13801          */
13802         uppercase : function(value){
13803             return String(value).toUpperCase();
13804         },
13805
13806         /**
13807          * Converts the first character only of a string to upper case
13808          * @param {String} value The text to convert
13809          * @return {String} The converted text
13810          */
13811         capitalize : function(value){
13812             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
13813         },
13814
13815         // private
13816         call : function(value, fn){
13817             if(arguments.length > 2){
13818                 var args = Array.prototype.slice.call(arguments, 2);
13819                 args.unshift(value);
13820                  
13821                 return /** eval:var:value */  eval(fn).apply(window, args);
13822             }else{
13823                 /** eval:var:value */
13824                 return /** eval:var:value */ eval(fn).call(window, value);
13825             }
13826         },
13827
13828        
13829         /**
13830          * safer version of Math.toFixed..??/
13831          * @param {Number/String} value The numeric value to format
13832          * @param {Number/String} value Decimal places 
13833          * @return {String} The formatted currency string
13834          */
13835         toFixed : function(v, n)
13836         {
13837             // why not use to fixed - precision is buggered???
13838             if (!n) {
13839                 return Math.round(v-0);
13840             }
13841             var fact = Math.pow(10,n+1);
13842             v = (Math.round((v-0)*fact))/fact;
13843             var z = (''+fact).substring(2);
13844             if (v == Math.floor(v)) {
13845                 return Math.floor(v) + '.' + z;
13846             }
13847             
13848             // now just padd decimals..
13849             var ps = String(v).split('.');
13850             var fd = (ps[1] + z);
13851             var r = fd.substring(0,n); 
13852             var rm = fd.substring(n); 
13853             if (rm < 5) {
13854                 return ps[0] + '.' + r;
13855             }
13856             r*=1; // turn it into a number;
13857             r++;
13858             if (String(r).length != n) {
13859                 ps[0]*=1;
13860                 ps[0]++;
13861                 r = String(r).substring(1); // chop the end off.
13862             }
13863             
13864             return ps[0] + '.' + r;
13865              
13866         },
13867         
13868         /**
13869          * Format a number as US currency
13870          * @param {Number/String} value The numeric value to format
13871          * @return {String} The formatted currency string
13872          */
13873         usMoney : function(v){
13874             return '$' + Roo.util.Format.number(v);
13875         },
13876         
13877         /**
13878          * Format a number
13879          * eventually this should probably emulate php's number_format
13880          * @param {Number/String} value The numeric value to format
13881          * @param {Number} decimals number of decimal places
13882          * @param {String} delimiter for thousands (default comma)
13883          * @return {String} The formatted currency string
13884          */
13885         number : function(v, decimals, thousandsDelimiter)
13886         {
13887             // multiply and round.
13888             decimals = typeof(decimals) == 'undefined' ? 2 : decimals;
13889             thousandsDelimiter = typeof(thousandsDelimiter) == 'undefined' ? ',' : thousandsDelimiter;
13890             
13891             var mul = Math.pow(10, decimals);
13892             var zero = String(mul).substring(1);
13893             v = (Math.round((v-0)*mul))/mul;
13894             
13895             // if it's '0' number.. then
13896             
13897             //v = (v == Math.floor(v)) ? v + "." + zero : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
13898             v = String(v);
13899             var ps = v.split('.');
13900             var whole = ps[0];
13901             
13902             var r = /(\d+)(\d{3})/;
13903             // add comma's
13904             
13905             if(thousandsDelimiter.length != 0) {
13906                 whole = whole.replace(/\B(?=(\d{3})+(?!\d))/g, thousandsDelimiter );
13907             } 
13908             
13909             var sub = ps[1] ?
13910                     // has decimals..
13911                     (decimals ?  ('.'+ ps[1] + zero.substring(ps[1].length)) : '') :
13912                     // does not have decimals
13913                     (decimals ? ('.' + zero) : '');
13914             
13915             
13916             return whole + sub ;
13917         },
13918         
13919         /**
13920          * Parse a value into a formatted date using the specified format pattern.
13921          * @param {Mixed} value The value to format
13922          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
13923          * @return {String} The formatted date string
13924          */
13925         date : function(v, format){
13926             if(!v){
13927                 return "";
13928             }
13929             if(!(v instanceof Date)){
13930                 v = new Date(Date.parse(v));
13931             }
13932             return v.dateFormat(format || Roo.util.Format.defaults.date);
13933         },
13934
13935         /**
13936          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
13937          * @param {String} format Any valid date format string
13938          * @return {Function} The date formatting function
13939          */
13940         dateRenderer : function(format){
13941             return function(v){
13942                 return Roo.util.Format.date(v, format);  
13943             };
13944         },
13945
13946         // private
13947         stripTagsRE : /<\/?[^>]+>/gi,
13948         
13949         /**
13950          * Strips all HTML tags
13951          * @param {Mixed} value The text from which to strip tags
13952          * @return {String} The stripped text
13953          */
13954         stripTags : function(v){
13955             return !v ? v : String(v).replace(this.stripTagsRE, "");
13956         }
13957     };
13958 }();
13959 Roo.util.Format.defaults = {
13960     date : 'd/M/Y'
13961 };/*
13962  * Based on:
13963  * Ext JS Library 1.1.1
13964  * Copyright(c) 2006-2007, Ext JS, LLC.
13965  *
13966  * Originally Released Under LGPL - original licence link has changed is not relivant.
13967  *
13968  * Fork - LGPL
13969  * <script type="text/javascript">
13970  */
13971
13972
13973  
13974
13975 /**
13976  * @class Roo.MasterTemplate
13977  * @extends Roo.Template
13978  * Provides a template that can have child templates. The syntax is:
13979 <pre><code>
13980 var t = new Roo.MasterTemplate(
13981         '&lt;select name="{name}"&gt;',
13982                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
13983         '&lt;/select&gt;'
13984 );
13985 t.add('options', {value: 'foo', text: 'bar'});
13986 // or you can add multiple child elements in one shot
13987 t.addAll('options', [
13988     {value: 'foo', text: 'bar'},
13989     {value: 'foo2', text: 'bar2'},
13990     {value: 'foo3', text: 'bar3'}
13991 ]);
13992 // then append, applying the master template values
13993 t.append('my-form', {name: 'my-select'});
13994 </code></pre>
13995 * A name attribute for the child template is not required if you have only one child
13996 * template or you want to refer to them by index.
13997  */
13998 Roo.MasterTemplate = function(){
13999     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
14000     this.originalHtml = this.html;
14001     var st = {};
14002     var m, re = this.subTemplateRe;
14003     re.lastIndex = 0;
14004     var subIndex = 0;
14005     while(m = re.exec(this.html)){
14006         var name = m[1], content = m[2];
14007         st[subIndex] = {
14008             name: name,
14009             index: subIndex,
14010             buffer: [],
14011             tpl : new Roo.Template(content)
14012         };
14013         if(name){
14014             st[name] = st[subIndex];
14015         }
14016         st[subIndex].tpl.compile();
14017         st[subIndex].tpl.call = this.call.createDelegate(this);
14018         subIndex++;
14019     }
14020     this.subCount = subIndex;
14021     this.subs = st;
14022 };
14023 Roo.extend(Roo.MasterTemplate, Roo.Template, {
14024     /**
14025     * The regular expression used to match sub templates
14026     * @type RegExp
14027     * @property
14028     */
14029     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
14030
14031     /**
14032      * Applies the passed values to a child template.
14033      * @param {String/Number} name (optional) The name or index of the child template
14034      * @param {Array/Object} values The values to be applied to the template
14035      * @return {MasterTemplate} this
14036      */
14037      add : function(name, values){
14038         if(arguments.length == 1){
14039             values = arguments[0];
14040             name = 0;
14041         }
14042         var s = this.subs[name];
14043         s.buffer[s.buffer.length] = s.tpl.apply(values);
14044         return this;
14045     },
14046
14047     /**
14048      * Applies all the passed values to a child template.
14049      * @param {String/Number} name (optional) The name or index of the child template
14050      * @param {Array} values The values to be applied to the template, this should be an array of objects.
14051      * @param {Boolean} reset (optional) True to reset the template first
14052      * @return {MasterTemplate} this
14053      */
14054     fill : function(name, values, reset){
14055         var a = arguments;
14056         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
14057             values = a[0];
14058             name = 0;
14059             reset = a[1];
14060         }
14061         if(reset){
14062             this.reset();
14063         }
14064         for(var i = 0, len = values.length; i < len; i++){
14065             this.add(name, values[i]);
14066         }
14067         return this;
14068     },
14069
14070     /**
14071      * Resets the template for reuse
14072      * @return {MasterTemplate} this
14073      */
14074      reset : function(){
14075         var s = this.subs;
14076         for(var i = 0; i < this.subCount; i++){
14077             s[i].buffer = [];
14078         }
14079         return this;
14080     },
14081
14082     applyTemplate : function(values){
14083         var s = this.subs;
14084         var replaceIndex = -1;
14085         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
14086             return s[++replaceIndex].buffer.join("");
14087         });
14088         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
14089     },
14090
14091     apply : function(){
14092         return this.applyTemplate.apply(this, arguments);
14093     },
14094
14095     compile : function(){return this;}
14096 });
14097
14098 /**
14099  * Alias for fill().
14100  * @method
14101  */
14102 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
14103  /**
14104  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
14105  * var tpl = Roo.MasterTemplate.from('element-id');
14106  * @param {String/HTMLElement} el
14107  * @param {Object} config
14108  * @static
14109  */
14110 Roo.MasterTemplate.from = function(el, config){
14111     el = Roo.getDom(el);
14112     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
14113 };/*
14114  * Based on:
14115  * Ext JS Library 1.1.1
14116  * Copyright(c) 2006-2007, Ext JS, LLC.
14117  *
14118  * Originally Released Under LGPL - original licence link has changed is not relivant.
14119  *
14120  * Fork - LGPL
14121  * <script type="text/javascript">
14122  */
14123
14124  
14125 /**
14126  * @class Roo.util.CSS
14127  * Utility class for manipulating CSS rules
14128  * @singleton
14129  */
14130 Roo.util.CSS = function(){
14131         var rules = null;
14132         var doc = document;
14133
14134     var camelRe = /(-[a-z])/gi;
14135     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
14136
14137    return {
14138    /**
14139     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
14140     * tag and appended to the HEAD of the document.
14141     * @param {String|Object} cssText The text containing the css rules
14142     * @param {String} id An id to add to the stylesheet for later removal
14143     * @return {StyleSheet}
14144     */
14145     createStyleSheet : function(cssText, id){
14146         var ss;
14147         var head = doc.getElementsByTagName("head")[0];
14148         var nrules = doc.createElement("style");
14149         nrules.setAttribute("type", "text/css");
14150         if(id){
14151             nrules.setAttribute("id", id);
14152         }
14153         if (typeof(cssText) != 'string') {
14154             // support object maps..
14155             // not sure if this a good idea.. 
14156             // perhaps it should be merged with the general css handling
14157             // and handle js style props.
14158             var cssTextNew = [];
14159             for(var n in cssText) {
14160                 var citems = [];
14161                 for(var k in cssText[n]) {
14162                     citems.push( k + ' : ' +cssText[n][k] + ';' );
14163                 }
14164                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
14165                 
14166             }
14167             cssText = cssTextNew.join("\n");
14168             
14169         }
14170        
14171        
14172        if(Roo.isIE){
14173            head.appendChild(nrules);
14174            ss = nrules.styleSheet;
14175            ss.cssText = cssText;
14176        }else{
14177            try{
14178                 nrules.appendChild(doc.createTextNode(cssText));
14179            }catch(e){
14180                nrules.cssText = cssText; 
14181            }
14182            head.appendChild(nrules);
14183            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
14184        }
14185        this.cacheStyleSheet(ss);
14186        return ss;
14187    },
14188
14189    /**
14190     * Removes a style or link tag by id
14191     * @param {String} id The id of the tag
14192     */
14193    removeStyleSheet : function(id){
14194        var existing = doc.getElementById(id);
14195        if(existing){
14196            existing.parentNode.removeChild(existing);
14197        }
14198    },
14199
14200    /**
14201     * Dynamically swaps an existing stylesheet reference for a new one
14202     * @param {String} id The id of an existing link tag to remove
14203     * @param {String} url The href of the new stylesheet to include
14204     */
14205    swapStyleSheet : function(id, url){
14206        this.removeStyleSheet(id);
14207        var ss = doc.createElement("link");
14208        ss.setAttribute("rel", "stylesheet");
14209        ss.setAttribute("type", "text/css");
14210        ss.setAttribute("id", id);
14211        ss.setAttribute("href", url);
14212        doc.getElementsByTagName("head")[0].appendChild(ss);
14213    },
14214    
14215    /**
14216     * Refresh the rule cache if you have dynamically added stylesheets
14217     * @return {Object} An object (hash) of rules indexed by selector
14218     */
14219    refreshCache : function(){
14220        return this.getRules(true);
14221    },
14222
14223    // private
14224    cacheStyleSheet : function(stylesheet){
14225        if(!rules){
14226            rules = {};
14227        }
14228        try{// try catch for cross domain access issue
14229            var ssRules = stylesheet.cssRules || stylesheet.rules;
14230            for(var j = ssRules.length-1; j >= 0; --j){
14231                rules[ssRules[j].selectorText] = ssRules[j];
14232            }
14233        }catch(e){}
14234    },
14235    
14236    /**
14237     * Gets all css rules for the document
14238     * @param {Boolean} refreshCache true to refresh the internal cache
14239     * @return {Object} An object (hash) of rules indexed by selector
14240     */
14241    getRules : function(refreshCache){
14242                 if(rules == null || refreshCache){
14243                         rules = {};
14244                         var ds = doc.styleSheets;
14245                         for(var i =0, len = ds.length; i < len; i++){
14246                             try{
14247                         this.cacheStyleSheet(ds[i]);
14248                     }catch(e){} 
14249                 }
14250                 }
14251                 return rules;
14252         },
14253         
14254         /**
14255     * Gets an an individual CSS rule by selector(s)
14256     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
14257     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
14258     * @return {CSSRule} The CSS rule or null if one is not found
14259     */
14260    getRule : function(selector, refreshCache){
14261                 var rs = this.getRules(refreshCache);
14262                 if(!(selector instanceof Array)){
14263                     return rs[selector];
14264                 }
14265                 for(var i = 0; i < selector.length; i++){
14266                         if(rs[selector[i]]){
14267                                 return rs[selector[i]];
14268                         }
14269                 }
14270                 return null;
14271         },
14272         
14273         
14274         /**
14275     * Updates a rule property
14276     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
14277     * @param {String} property The css property
14278     * @param {String} value The new value for the property
14279     * @return {Boolean} true If a rule was found and updated
14280     */
14281    updateRule : function(selector, property, value){
14282                 if(!(selector instanceof Array)){
14283                         var rule = this.getRule(selector);
14284                         if(rule){
14285                                 rule.style[property.replace(camelRe, camelFn)] = value;
14286                                 return true;
14287                         }
14288                 }else{
14289                         for(var i = 0; i < selector.length; i++){
14290                                 if(this.updateRule(selector[i], property, value)){
14291                                         return true;
14292                                 }
14293                         }
14294                 }
14295                 return false;
14296         }
14297    };   
14298 }();/*
14299  * Based on:
14300  * Ext JS Library 1.1.1
14301  * Copyright(c) 2006-2007, Ext JS, LLC.
14302  *
14303  * Originally Released Under LGPL - original licence link has changed is not relivant.
14304  *
14305  * Fork - LGPL
14306  * <script type="text/javascript">
14307  */
14308
14309  
14310
14311 /**
14312  * @class Roo.util.ClickRepeater
14313  * @extends Roo.util.Observable
14314  * 
14315  * A wrapper class which can be applied to any element. Fires a "click" event while the
14316  * mouse is pressed. The interval between firings may be specified in the config but
14317  * defaults to 10 milliseconds.
14318  * 
14319  * Optionally, a CSS class may be applied to the element during the time it is pressed.
14320  * 
14321  * @cfg {String/HTMLElement/Element} el The element to act as a button.
14322  * @cfg {Number} delay The initial delay before the repeating event begins firing.
14323  * Similar to an autorepeat key delay.
14324  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
14325  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
14326  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
14327  *           "interval" and "delay" are ignored. "immediate" is honored.
14328  * @cfg {Boolean} preventDefault True to prevent the default click event
14329  * @cfg {Boolean} stopDefault True to stop the default click event
14330  * 
14331  * @history
14332  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
14333  *     2007-02-02 jvs Renamed to ClickRepeater
14334  *   2007-02-03 jvs Modifications for FF Mac and Safari 
14335  *
14336  *  @constructor
14337  * @param {String/HTMLElement/Element} el The element to listen on
14338  * @param {Object} config
14339  **/
14340 Roo.util.ClickRepeater = function(el, config)
14341 {
14342     this.el = Roo.get(el);
14343     this.el.unselectable();
14344
14345     Roo.apply(this, config);
14346
14347     this.addEvents({
14348     /**
14349      * @event mousedown
14350      * Fires when the mouse button is depressed.
14351      * @param {Roo.util.ClickRepeater} this
14352      */
14353         "mousedown" : true,
14354     /**
14355      * @event click
14356      * Fires on a specified interval during the time the element is pressed.
14357      * @param {Roo.util.ClickRepeater} this
14358      */
14359         "click" : true,
14360     /**
14361      * @event mouseup
14362      * Fires when the mouse key is released.
14363      * @param {Roo.util.ClickRepeater} this
14364      */
14365         "mouseup" : true
14366     });
14367
14368     this.el.on("mousedown", this.handleMouseDown, this);
14369     if(this.preventDefault || this.stopDefault){
14370         this.el.on("click", function(e){
14371             if(this.preventDefault){
14372                 e.preventDefault();
14373             }
14374             if(this.stopDefault){
14375                 e.stopEvent();
14376             }
14377         }, this);
14378     }
14379
14380     // allow inline handler
14381     if(this.handler){
14382         this.on("click", this.handler,  this.scope || this);
14383     }
14384
14385     Roo.util.ClickRepeater.superclass.constructor.call(this);
14386 };
14387
14388 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
14389     interval : 20,
14390     delay: 250,
14391     preventDefault : true,
14392     stopDefault : false,
14393     timer : 0,
14394
14395     // private
14396     handleMouseDown : function(){
14397         clearTimeout(this.timer);
14398         this.el.blur();
14399         if(this.pressClass){
14400             this.el.addClass(this.pressClass);
14401         }
14402         this.mousedownTime = new Date();
14403
14404         Roo.get(document).on("mouseup", this.handleMouseUp, this);
14405         this.el.on("mouseout", this.handleMouseOut, this);
14406
14407         this.fireEvent("mousedown", this);
14408         this.fireEvent("click", this);
14409         
14410         this.timer = this.click.defer(this.delay || this.interval, this);
14411     },
14412
14413     // private
14414     click : function(){
14415         this.fireEvent("click", this);
14416         this.timer = this.click.defer(this.getInterval(), this);
14417     },
14418
14419     // private
14420     getInterval: function(){
14421         if(!this.accelerate){
14422             return this.interval;
14423         }
14424         var pressTime = this.mousedownTime.getElapsed();
14425         if(pressTime < 500){
14426             return 400;
14427         }else if(pressTime < 1700){
14428             return 320;
14429         }else if(pressTime < 2600){
14430             return 250;
14431         }else if(pressTime < 3500){
14432             return 180;
14433         }else if(pressTime < 4400){
14434             return 140;
14435         }else if(pressTime < 5300){
14436             return 80;
14437         }else if(pressTime < 6200){
14438             return 50;
14439         }else{
14440             return 10;
14441         }
14442     },
14443
14444     // private
14445     handleMouseOut : function(){
14446         clearTimeout(this.timer);
14447         if(this.pressClass){
14448             this.el.removeClass(this.pressClass);
14449         }
14450         this.el.on("mouseover", this.handleMouseReturn, this);
14451     },
14452
14453     // private
14454     handleMouseReturn : function(){
14455         this.el.un("mouseover", this.handleMouseReturn);
14456         if(this.pressClass){
14457             this.el.addClass(this.pressClass);
14458         }
14459         this.click();
14460     },
14461
14462     // private
14463     handleMouseUp : function(){
14464         clearTimeout(this.timer);
14465         this.el.un("mouseover", this.handleMouseReturn);
14466         this.el.un("mouseout", this.handleMouseOut);
14467         Roo.get(document).un("mouseup", this.handleMouseUp);
14468         this.el.removeClass(this.pressClass);
14469         this.fireEvent("mouseup", this);
14470     }
14471 });/*
14472  * Based on:
14473  * Ext JS Library 1.1.1
14474  * Copyright(c) 2006-2007, Ext JS, LLC.
14475  *
14476  * Originally Released Under LGPL - original licence link has changed is not relivant.
14477  *
14478  * Fork - LGPL
14479  * <script type="text/javascript">
14480  */
14481
14482  
14483 /**
14484  * @class Roo.KeyNav
14485  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
14486  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
14487  * way to implement custom navigation schemes for any UI component.</p>
14488  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
14489  * pageUp, pageDown, del, home, end.  Usage:</p>
14490  <pre><code>
14491 var nav = new Roo.KeyNav("my-element", {
14492     "left" : function(e){
14493         this.moveLeft(e.ctrlKey);
14494     },
14495     "right" : function(e){
14496         this.moveRight(e.ctrlKey);
14497     },
14498     "enter" : function(e){
14499         this.save();
14500     },
14501     scope : this
14502 });
14503 </code></pre>
14504  * @constructor
14505  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14506  * @param {Object} config The config
14507  */
14508 Roo.KeyNav = function(el, config){
14509     this.el = Roo.get(el);
14510     Roo.apply(this, config);
14511     if(!this.disabled){
14512         this.disabled = true;
14513         this.enable();
14514     }
14515 };
14516
14517 Roo.KeyNav.prototype = {
14518     /**
14519      * @cfg {Boolean} disabled
14520      * True to disable this KeyNav instance (defaults to false)
14521      */
14522     disabled : false,
14523     /**
14524      * @cfg {String} defaultEventAction
14525      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
14526      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
14527      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
14528      */
14529     defaultEventAction: "stopEvent",
14530     /**
14531      * @cfg {Boolean} forceKeyDown
14532      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
14533      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
14534      * handle keydown instead of keypress.
14535      */
14536     forceKeyDown : false,
14537
14538     // private
14539     prepareEvent : function(e){
14540         var k = e.getKey();
14541         var h = this.keyToHandler[k];
14542         //if(h && this[h]){
14543         //    e.stopPropagation();
14544         //}
14545         if(Roo.isSafari && h && k >= 37 && k <= 40){
14546             e.stopEvent();
14547         }
14548     },
14549
14550     // private
14551     relay : function(e){
14552         var k = e.getKey();
14553         var h = this.keyToHandler[k];
14554         if(h && this[h]){
14555             if(this.doRelay(e, this[h], h) !== true){
14556                 e[this.defaultEventAction]();
14557             }
14558         }
14559     },
14560
14561     // private
14562     doRelay : function(e, h, hname){
14563         return h.call(this.scope || this, e);
14564     },
14565
14566     // possible handlers
14567     enter : false,
14568     left : false,
14569     right : false,
14570     up : false,
14571     down : false,
14572     tab : false,
14573     esc : false,
14574     pageUp : false,
14575     pageDown : false,
14576     del : false,
14577     home : false,
14578     end : false,
14579
14580     // quick lookup hash
14581     keyToHandler : {
14582         37 : "left",
14583         39 : "right",
14584         38 : "up",
14585         40 : "down",
14586         33 : "pageUp",
14587         34 : "pageDown",
14588         46 : "del",
14589         36 : "home",
14590         35 : "end",
14591         13 : "enter",
14592         27 : "esc",
14593         9  : "tab"
14594     },
14595
14596         /**
14597          * Enable this KeyNav
14598          */
14599         enable: function(){
14600                 if(this.disabled){
14601             // ie won't do special keys on keypress, no one else will repeat keys with keydown
14602             // the EventObject will normalize Safari automatically
14603             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14604                 this.el.on("keydown", this.relay,  this);
14605             }else{
14606                 this.el.on("keydown", this.prepareEvent,  this);
14607                 this.el.on("keypress", this.relay,  this);
14608             }
14609                     this.disabled = false;
14610                 }
14611         },
14612
14613         /**
14614          * Disable this KeyNav
14615          */
14616         disable: function(){
14617                 if(!this.disabled){
14618                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14619                 this.el.un("keydown", this.relay);
14620             }else{
14621                 this.el.un("keydown", this.prepareEvent);
14622                 this.el.un("keypress", this.relay);
14623             }
14624                     this.disabled = true;
14625                 }
14626         }
14627 };/*
14628  * Based on:
14629  * Ext JS Library 1.1.1
14630  * Copyright(c) 2006-2007, Ext JS, LLC.
14631  *
14632  * Originally Released Under LGPL - original licence link has changed is not relivant.
14633  *
14634  * Fork - LGPL
14635  * <script type="text/javascript">
14636  */
14637
14638  
14639 /**
14640  * @class Roo.KeyMap
14641  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
14642  * The constructor accepts the same config object as defined by {@link #addBinding}.
14643  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
14644  * combination it will call the function with this signature (if the match is a multi-key
14645  * combination the callback will still be called only once): (String key, Roo.EventObject e)
14646  * A KeyMap can also handle a string representation of keys.<br />
14647  * Usage:
14648  <pre><code>
14649 // map one key by key code
14650 var map = new Roo.KeyMap("my-element", {
14651     key: 13, // or Roo.EventObject.ENTER
14652     fn: myHandler,
14653     scope: myObject
14654 });
14655
14656 // map multiple keys to one action by string
14657 var map = new Roo.KeyMap("my-element", {
14658     key: "a\r\n\t",
14659     fn: myHandler,
14660     scope: myObject
14661 });
14662
14663 // map multiple keys to multiple actions by strings and array of codes
14664 var map = new Roo.KeyMap("my-element", [
14665     {
14666         key: [10,13],
14667         fn: function(){ alert("Return was pressed"); }
14668     }, {
14669         key: "abc",
14670         fn: function(){ alert('a, b or c was pressed'); }
14671     }, {
14672         key: "\t",
14673         ctrl:true,
14674         shift:true,
14675         fn: function(){ alert('Control + shift + tab was pressed.'); }
14676     }
14677 ]);
14678 </code></pre>
14679  * <b>Note: A KeyMap starts enabled</b>
14680  * @constructor
14681  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14682  * @param {Object} config The config (see {@link #addBinding})
14683  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
14684  */
14685 Roo.KeyMap = function(el, config, eventName){
14686     this.el  = Roo.get(el);
14687     this.eventName = eventName || "keydown";
14688     this.bindings = [];
14689     if(config){
14690         this.addBinding(config);
14691     }
14692     this.enable();
14693 };
14694
14695 Roo.KeyMap.prototype = {
14696     /**
14697      * True to stop the event from bubbling and prevent the default browser action if the
14698      * key was handled by the KeyMap (defaults to false)
14699      * @type Boolean
14700      */
14701     stopEvent : false,
14702
14703     /**
14704      * Add a new binding to this KeyMap. The following config object properties are supported:
14705      * <pre>
14706 Property    Type             Description
14707 ----------  ---------------  ----------------------------------------------------------------------
14708 key         String/Array     A single keycode or an array of keycodes to handle
14709 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
14710 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
14711 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
14712 fn          Function         The function to call when KeyMap finds the expected key combination
14713 scope       Object           The scope of the callback function
14714 </pre>
14715      *
14716      * Usage:
14717      * <pre><code>
14718 // Create a KeyMap
14719 var map = new Roo.KeyMap(document, {
14720     key: Roo.EventObject.ENTER,
14721     fn: handleKey,
14722     scope: this
14723 });
14724
14725 //Add a new binding to the existing KeyMap later
14726 map.addBinding({
14727     key: 'abc',
14728     shift: true,
14729     fn: handleKey,
14730     scope: this
14731 });
14732 </code></pre>
14733      * @param {Object/Array} config A single KeyMap config or an array of configs
14734      */
14735         addBinding : function(config){
14736         if(config instanceof Array){
14737             for(var i = 0, len = config.length; i < len; i++){
14738                 this.addBinding(config[i]);
14739             }
14740             return;
14741         }
14742         var keyCode = config.key,
14743             shift = config.shift, 
14744             ctrl = config.ctrl, 
14745             alt = config.alt,
14746             fn = config.fn,
14747             scope = config.scope;
14748         if(typeof keyCode == "string"){
14749             var ks = [];
14750             var keyString = keyCode.toUpperCase();
14751             for(var j = 0, len = keyString.length; j < len; j++){
14752                 ks.push(keyString.charCodeAt(j));
14753             }
14754             keyCode = ks;
14755         }
14756         var keyArray = keyCode instanceof Array;
14757         var handler = function(e){
14758             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14759                 var k = e.getKey();
14760                 if(keyArray){
14761                     for(var i = 0, len = keyCode.length; i < len; i++){
14762                         if(keyCode[i] == k){
14763                           if(this.stopEvent){
14764                               e.stopEvent();
14765                           }
14766                           fn.call(scope || window, k, e);
14767                           return;
14768                         }
14769                     }
14770                 }else{
14771                     if(k == keyCode){
14772                         if(this.stopEvent){
14773                            e.stopEvent();
14774                         }
14775                         fn.call(scope || window, k, e);
14776                     }
14777                 }
14778             }
14779         };
14780         this.bindings.push(handler);  
14781         },
14782
14783     /**
14784      * Shorthand for adding a single key listener
14785      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
14786      * following options:
14787      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14788      * @param {Function} fn The function to call
14789      * @param {Object} scope (optional) The scope of the function
14790      */
14791     on : function(key, fn, scope){
14792         var keyCode, shift, ctrl, alt;
14793         if(typeof key == "object" && !(key instanceof Array)){
14794             keyCode = key.key;
14795             shift = key.shift;
14796             ctrl = key.ctrl;
14797             alt = key.alt;
14798         }else{
14799             keyCode = key;
14800         }
14801         this.addBinding({
14802             key: keyCode,
14803             shift: shift,
14804             ctrl: ctrl,
14805             alt: alt,
14806             fn: fn,
14807             scope: scope
14808         })
14809     },
14810
14811     // private
14812     handleKeyDown : function(e){
14813             if(this.enabled){ //just in case
14814             var b = this.bindings;
14815             for(var i = 0, len = b.length; i < len; i++){
14816                 b[i].call(this, e);
14817             }
14818             }
14819         },
14820         
14821         /**
14822          * Returns true if this KeyMap is enabled
14823          * @return {Boolean} 
14824          */
14825         isEnabled : function(){
14826             return this.enabled;  
14827         },
14828         
14829         /**
14830          * Enables this KeyMap
14831          */
14832         enable: function(){
14833                 if(!this.enabled){
14834                     this.el.on(this.eventName, this.handleKeyDown, this);
14835                     this.enabled = true;
14836                 }
14837         },
14838
14839         /**
14840          * Disable this KeyMap
14841          */
14842         disable: function(){
14843                 if(this.enabled){
14844                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
14845                     this.enabled = false;
14846                 }
14847         }
14848 };/*
14849  * Based on:
14850  * Ext JS Library 1.1.1
14851  * Copyright(c) 2006-2007, Ext JS, LLC.
14852  *
14853  * Originally Released Under LGPL - original licence link has changed is not relivant.
14854  *
14855  * Fork - LGPL
14856  * <script type="text/javascript">
14857  */
14858
14859  
14860 /**
14861  * @class Roo.util.TextMetrics
14862  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
14863  * wide, in pixels, a given block of text will be.
14864  * @singleton
14865  */
14866 Roo.util.TextMetrics = function(){
14867     var shared;
14868     return {
14869         /**
14870          * Measures the size of the specified text
14871          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
14872          * that can affect the size of the rendered text
14873          * @param {String} text The text to measure
14874          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14875          * in order to accurately measure the text height
14876          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14877          */
14878         measure : function(el, text, fixedWidth){
14879             if(!shared){
14880                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
14881             }
14882             shared.bind(el);
14883             shared.setFixedWidth(fixedWidth || 'auto');
14884             return shared.getSize(text);
14885         },
14886
14887         /**
14888          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
14889          * the overhead of multiple calls to initialize the style properties on each measurement.
14890          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
14891          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14892          * in order to accurately measure the text height
14893          * @return {Roo.util.TextMetrics.Instance} instance The new instance
14894          */
14895         createInstance : function(el, fixedWidth){
14896             return Roo.util.TextMetrics.Instance(el, fixedWidth);
14897         }
14898     };
14899 }();
14900
14901  
14902
14903 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
14904     var ml = new Roo.Element(document.createElement('div'));
14905     document.body.appendChild(ml.dom);
14906     ml.position('absolute');
14907     ml.setLeftTop(-1000, -1000);
14908     ml.hide();
14909
14910     if(fixedWidth){
14911         ml.setWidth(fixedWidth);
14912     }
14913      
14914     var instance = {
14915         /**
14916          * Returns the size of the specified text based on the internal element's style and width properties
14917          * @memberOf Roo.util.TextMetrics.Instance#
14918          * @param {String} text The text to measure
14919          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14920          */
14921         getSize : function(text){
14922             ml.update(text);
14923             var s = ml.getSize();
14924             ml.update('');
14925             return s;
14926         },
14927
14928         /**
14929          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
14930          * that can affect the size of the rendered text
14931          * @memberOf Roo.util.TextMetrics.Instance#
14932          * @param {String/HTMLElement} el The element, dom node or id
14933          */
14934         bind : function(el){
14935             ml.setStyle(
14936                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
14937             );
14938         },
14939
14940         /**
14941          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
14942          * to set a fixed width in order to accurately measure the text height.
14943          * @memberOf Roo.util.TextMetrics.Instance#
14944          * @param {Number} width The width to set on the element
14945          */
14946         setFixedWidth : function(width){
14947             ml.setWidth(width);
14948         },
14949
14950         /**
14951          * Returns the measured width of the specified text
14952          * @memberOf Roo.util.TextMetrics.Instance#
14953          * @param {String} text The text to measure
14954          * @return {Number} width The width in pixels
14955          */
14956         getWidth : function(text){
14957             ml.dom.style.width = 'auto';
14958             return this.getSize(text).width;
14959         },
14960
14961         /**
14962          * Returns the measured height of the specified text.  For multiline text, be sure to call
14963          * {@link #setFixedWidth} if necessary.
14964          * @memberOf Roo.util.TextMetrics.Instance#
14965          * @param {String} text The text to measure
14966          * @return {Number} height The height in pixels
14967          */
14968         getHeight : function(text){
14969             return this.getSize(text).height;
14970         }
14971     };
14972
14973     instance.bind(bindTo);
14974
14975     return instance;
14976 };
14977
14978 // backwards compat
14979 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
14980  * Based on:
14981  * Ext JS Library 1.1.1
14982  * Copyright(c) 2006-2007, Ext JS, LLC.
14983  *
14984  * Originally Released Under LGPL - original licence link has changed is not relivant.
14985  *
14986  * Fork - LGPL
14987  * <script type="text/javascript">
14988  */
14989
14990 /**
14991  * @class Roo.state.Provider
14992  * Abstract base class for state provider implementations. This class provides methods
14993  * for encoding and decoding <b>typed</b> variables including dates and defines the 
14994  * Provider interface.
14995  */
14996 Roo.state.Provider = function(){
14997     /**
14998      * @event statechange
14999      * Fires when a state change occurs.
15000      * @param {Provider} this This state provider
15001      * @param {String} key The state key which was changed
15002      * @param {String} value The encoded value for the state
15003      */
15004     this.addEvents({
15005         "statechange": true
15006     });
15007     this.state = {};
15008     Roo.state.Provider.superclass.constructor.call(this);
15009 };
15010 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
15011     /**
15012      * Returns the current value for a key
15013      * @param {String} name The key name
15014      * @param {Mixed} defaultValue A default value to return if the key's value is not found
15015      * @return {Mixed} The state data
15016      */
15017     get : function(name, defaultValue){
15018         return typeof this.state[name] == "undefined" ?
15019             defaultValue : this.state[name];
15020     },
15021     
15022     /**
15023      * Clears a value from the state
15024      * @param {String} name The key name
15025      */
15026     clear : function(name){
15027         delete this.state[name];
15028         this.fireEvent("statechange", this, name, null);
15029     },
15030     
15031     /**
15032      * Sets the value for a key
15033      * @param {String} name The key name
15034      * @param {Mixed} value The value to set
15035      */
15036     set : function(name, value){
15037         this.state[name] = value;
15038         this.fireEvent("statechange", this, name, value);
15039     },
15040     
15041     /**
15042      * Decodes a string previously encoded with {@link #encodeValue}.
15043      * @param {String} value The value to decode
15044      * @return {Mixed} The decoded value
15045      */
15046     decodeValue : function(cookie){
15047         var re = /^(a|n|d|b|s|o)\:(.*)$/;
15048         var matches = re.exec(unescape(cookie));
15049         if(!matches || !matches[1]) {
15050             return; // non state cookie
15051         }
15052         var type = matches[1];
15053         var v = matches[2];
15054         switch(type){
15055             case "n":
15056                 return parseFloat(v);
15057             case "d":
15058                 return new Date(Date.parse(v));
15059             case "b":
15060                 return (v == "1");
15061             case "a":
15062                 var all = [];
15063                 var values = v.split("^");
15064                 for(var i = 0, len = values.length; i < len; i++){
15065                     all.push(this.decodeValue(values[i]));
15066                 }
15067                 return all;
15068            case "o":
15069                 var all = {};
15070                 var values = v.split("^");
15071                 for(var i = 0, len = values.length; i < len; i++){
15072                     var kv = values[i].split("=");
15073                     all[kv[0]] = this.decodeValue(kv[1]);
15074                 }
15075                 return all;
15076            default:
15077                 return v;
15078         }
15079     },
15080     
15081     /**
15082      * Encodes a value including type information.  Decode with {@link #decodeValue}.
15083      * @param {Mixed} value The value to encode
15084      * @return {String} The encoded value
15085      */
15086     encodeValue : function(v){
15087         var enc;
15088         if(typeof v == "number"){
15089             enc = "n:" + v;
15090         }else if(typeof v == "boolean"){
15091             enc = "b:" + (v ? "1" : "0");
15092         }else if(v instanceof Date){
15093             enc = "d:" + v.toGMTString();
15094         }else if(v instanceof Array){
15095             var flat = "";
15096             for(var i = 0, len = v.length; i < len; i++){
15097                 flat += this.encodeValue(v[i]);
15098                 if(i != len-1) {
15099                     flat += "^";
15100                 }
15101             }
15102             enc = "a:" + flat;
15103         }else if(typeof v == "object"){
15104             var flat = "";
15105             for(var key in v){
15106                 if(typeof v[key] != "function"){
15107                     flat += key + "=" + this.encodeValue(v[key]) + "^";
15108                 }
15109             }
15110             enc = "o:" + flat.substring(0, flat.length-1);
15111         }else{
15112             enc = "s:" + v;
15113         }
15114         return escape(enc);        
15115     }
15116 });
15117
15118 /*
15119  * Based on:
15120  * Ext JS Library 1.1.1
15121  * Copyright(c) 2006-2007, Ext JS, LLC.
15122  *
15123  * Originally Released Under LGPL - original licence link has changed is not relivant.
15124  *
15125  * Fork - LGPL
15126  * <script type="text/javascript">
15127  */
15128 /**
15129  * @class Roo.state.Manager
15130  * This is the global state manager. By default all components that are "state aware" check this class
15131  * for state information if you don't pass them a custom state provider. In order for this class
15132  * to be useful, it must be initialized with a provider when your application initializes.
15133  <pre><code>
15134 // in your initialization function
15135 init : function(){
15136    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
15137    ...
15138    // supposed you have a {@link Roo.BorderLayout}
15139    var layout = new Roo.BorderLayout(...);
15140    layout.restoreState();
15141    // or a {Roo.BasicDialog}
15142    var dialog = new Roo.BasicDialog(...);
15143    dialog.restoreState();
15144  </code></pre>
15145  * @singleton
15146  */
15147 Roo.state.Manager = function(){
15148     var provider = new Roo.state.Provider();
15149     
15150     return {
15151         /**
15152          * Configures the default state provider for your application
15153          * @param {Provider} stateProvider The state provider to set
15154          */
15155         setProvider : function(stateProvider){
15156             provider = stateProvider;
15157         },
15158         
15159         /**
15160          * Returns the current value for a key
15161          * @param {String} name The key name
15162          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
15163          * @return {Mixed} The state data
15164          */
15165         get : function(key, defaultValue){
15166             return provider.get(key, defaultValue);
15167         },
15168         
15169         /**
15170          * Sets the value for a key
15171          * @param {String} name The key name
15172          * @param {Mixed} value The state data
15173          */
15174          set : function(key, value){
15175             provider.set(key, value);
15176         },
15177         
15178         /**
15179          * Clears a value from the state
15180          * @param {String} name The key name
15181          */
15182         clear : function(key){
15183             provider.clear(key);
15184         },
15185         
15186         /**
15187          * Gets the currently configured state provider
15188          * @return {Provider} The state provider
15189          */
15190         getProvider : function(){
15191             return provider;
15192         }
15193     };
15194 }();
15195 /*
15196  * Based on:
15197  * Ext JS Library 1.1.1
15198  * Copyright(c) 2006-2007, Ext JS, LLC.
15199  *
15200  * Originally Released Under LGPL - original licence link has changed is not relivant.
15201  *
15202  * Fork - LGPL
15203  * <script type="text/javascript">
15204  */
15205 /**
15206  * @class Roo.state.CookieProvider
15207  * @extends Roo.state.Provider
15208  * The default Provider implementation which saves state via cookies.
15209  * <br />Usage:
15210  <pre><code>
15211    var cp = new Roo.state.CookieProvider({
15212        path: "/cgi-bin/",
15213        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
15214        domain: "roojs.com"
15215    })
15216    Roo.state.Manager.setProvider(cp);
15217  </code></pre>
15218  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
15219  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
15220  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
15221  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
15222  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
15223  * domain the page is running on including the 'www' like 'www.roojs.com')
15224  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
15225  * @constructor
15226  * Create a new CookieProvider
15227  * @param {Object} config The configuration object
15228  */
15229 Roo.state.CookieProvider = function(config){
15230     Roo.state.CookieProvider.superclass.constructor.call(this);
15231     this.path = "/";
15232     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
15233     this.domain = null;
15234     this.secure = false;
15235     Roo.apply(this, config);
15236     this.state = this.readCookies();
15237 };
15238
15239 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
15240     // private
15241     set : function(name, value){
15242         if(typeof value == "undefined" || value === null){
15243             this.clear(name);
15244             return;
15245         }
15246         this.setCookie(name, value);
15247         Roo.state.CookieProvider.superclass.set.call(this, name, value);
15248     },
15249
15250     // private
15251     clear : function(name){
15252         this.clearCookie(name);
15253         Roo.state.CookieProvider.superclass.clear.call(this, name);
15254     },
15255
15256     // private
15257     readCookies : function(){
15258         var cookies = {};
15259         var c = document.cookie + ";";
15260         var re = /\s?(.*?)=(.*?);/g;
15261         var matches;
15262         while((matches = re.exec(c)) != null){
15263             var name = matches[1];
15264             var value = matches[2];
15265             if(name && name.substring(0,3) == "ys-"){
15266                 cookies[name.substr(3)] = this.decodeValue(value);
15267             }
15268         }
15269         return cookies;
15270     },
15271
15272     // private
15273     setCookie : function(name, value){
15274         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
15275            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
15276            ((this.path == null) ? "" : ("; path=" + this.path)) +
15277            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
15278            ((this.secure == true) ? "; secure" : "");
15279     },
15280
15281     // private
15282     clearCookie : function(name){
15283         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
15284            ((this.path == null) ? "" : ("; path=" + this.path)) +
15285            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
15286            ((this.secure == true) ? "; secure" : "");
15287     }
15288 });/*
15289  * Based on:
15290  * Ext JS Library 1.1.1
15291  * Copyright(c) 2006-2007, Ext JS, LLC.
15292  *
15293  * Originally Released Under LGPL - original licence link has changed is not relivant.
15294  *
15295  * Fork - LGPL
15296  * <script type="text/javascript">
15297  */
15298  
15299
15300 /**
15301  * @class Roo.ComponentMgr
15302  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
15303  * @singleton
15304  */
15305 Roo.ComponentMgr = function(){
15306     var all = new Roo.util.MixedCollection();
15307
15308     return {
15309         /**
15310          * Registers a component.
15311          * @param {Roo.Component} c The component
15312          */
15313         register : function(c){
15314             all.add(c);
15315         },
15316
15317         /**
15318          * Unregisters a component.
15319          * @param {Roo.Component} c The component
15320          */
15321         unregister : function(c){
15322             all.remove(c);
15323         },
15324
15325         /**
15326          * Returns a component by id
15327          * @param {String} id The component id
15328          */
15329         get : function(id){
15330             return all.get(id);
15331         },
15332
15333         /**
15334          * Registers a function that will be called when a specified component is added to ComponentMgr
15335          * @param {String} id The component id
15336          * @param {Funtction} fn The callback function
15337          * @param {Object} scope The scope of the callback
15338          */
15339         onAvailable : function(id, fn, scope){
15340             all.on("add", function(index, o){
15341                 if(o.id == id){
15342                     fn.call(scope || o, o);
15343                     all.un("add", fn, scope);
15344                 }
15345             });
15346         }
15347     };
15348 }();/*
15349  * Based on:
15350  * Ext JS Library 1.1.1
15351  * Copyright(c) 2006-2007, Ext JS, LLC.
15352  *
15353  * Originally Released Under LGPL - original licence link has changed is not relivant.
15354  *
15355  * Fork - LGPL
15356  * <script type="text/javascript">
15357  */
15358  
15359 /**
15360  * @class Roo.Component
15361  * @extends Roo.util.Observable
15362  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
15363  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
15364  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
15365  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
15366  * All visual components (widgets) that require rendering into a layout should subclass Component.
15367  * @constructor
15368  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
15369  * 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
15370  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
15371  */
15372 Roo.Component = function(config){
15373     config = config || {};
15374     if(config.tagName || config.dom || typeof config == "string"){ // element object
15375         config = {el: config, id: config.id || config};
15376     }
15377     this.initialConfig = config;
15378
15379     Roo.apply(this, config);
15380     this.addEvents({
15381         /**
15382          * @event disable
15383          * Fires after the component is disabled.
15384              * @param {Roo.Component} this
15385              */
15386         disable : true,
15387         /**
15388          * @event enable
15389          * Fires after the component is enabled.
15390              * @param {Roo.Component} this
15391              */
15392         enable : true,
15393         /**
15394          * @event beforeshow
15395          * Fires before the component is shown.  Return false to stop the show.
15396              * @param {Roo.Component} this
15397              */
15398         beforeshow : true,
15399         /**
15400          * @event show
15401          * Fires after the component is shown.
15402              * @param {Roo.Component} this
15403              */
15404         show : true,
15405         /**
15406          * @event beforehide
15407          * Fires before the component is hidden. Return false to stop the hide.
15408              * @param {Roo.Component} this
15409              */
15410         beforehide : true,
15411         /**
15412          * @event hide
15413          * Fires after the component is hidden.
15414              * @param {Roo.Component} this
15415              */
15416         hide : true,
15417         /**
15418          * @event beforerender
15419          * Fires before the component is rendered. Return false to stop the render.
15420              * @param {Roo.Component} this
15421              */
15422         beforerender : true,
15423         /**
15424          * @event render
15425          * Fires after the component is rendered.
15426              * @param {Roo.Component} this
15427              */
15428         render : true,
15429         /**
15430          * @event beforedestroy
15431          * Fires before the component is destroyed. Return false to stop the destroy.
15432              * @param {Roo.Component} this
15433              */
15434         beforedestroy : true,
15435         /**
15436          * @event destroy
15437          * Fires after the component is destroyed.
15438              * @param {Roo.Component} this
15439              */
15440         destroy : true
15441     });
15442     if(!this.id){
15443         this.id = "roo-comp-" + (++Roo.Component.AUTO_ID);
15444     }
15445     Roo.ComponentMgr.register(this);
15446     Roo.Component.superclass.constructor.call(this);
15447     this.initComponent();
15448     if(this.renderTo){ // not supported by all components yet. use at your own risk!
15449         this.render(this.renderTo);
15450         delete this.renderTo;
15451     }
15452 };
15453
15454 /** @private */
15455 Roo.Component.AUTO_ID = 1000;
15456
15457 Roo.extend(Roo.Component, Roo.util.Observable, {
15458     /**
15459      * @scope Roo.Component.prototype
15460      * @type {Boolean}
15461      * true if this component is hidden. Read-only.
15462      */
15463     hidden : false,
15464     /**
15465      * @type {Boolean}
15466      * true if this component is disabled. Read-only.
15467      */
15468     disabled : false,
15469     /**
15470      * @type {Boolean}
15471      * true if this component has been rendered. Read-only.
15472      */
15473     rendered : false,
15474     
15475     /** @cfg {String} disableClass
15476      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
15477      */
15478     disabledClass : "x-item-disabled",
15479         /** @cfg {Boolean} allowDomMove
15480          * Whether the component can move the Dom node when rendering (defaults to true).
15481          */
15482     allowDomMove : true,
15483     /** @cfg {String} hideMode (display|visibility)
15484      * How this component should hidden. Supported values are
15485      * "visibility" (css visibility), "offsets" (negative offset position) and
15486      * "display" (css display) - defaults to "display".
15487      */
15488     hideMode: 'display',
15489
15490     /** @private */
15491     ctype : "Roo.Component",
15492
15493     /**
15494      * @cfg {String} actionMode 
15495      * which property holds the element that used for  hide() / show() / disable() / enable()
15496      * default is 'el' for forms you probably want to set this to fieldEl 
15497      */
15498     actionMode : "el",
15499
15500     /** @private */
15501     getActionEl : function(){
15502         return this[this.actionMode];
15503     },
15504
15505     initComponent : Roo.emptyFn,
15506     /**
15507      * If this is a lazy rendering component, render it to its container element.
15508      * @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.
15509      */
15510     render : function(container, position){
15511         
15512         if(this.rendered){
15513             return this;
15514         }
15515         
15516         if(this.fireEvent("beforerender", this) === false){
15517             return false;
15518         }
15519         
15520         if(!container && this.el){
15521             this.el = Roo.get(this.el);
15522             container = this.el.dom.parentNode;
15523             this.allowDomMove = false;
15524         }
15525         this.container = Roo.get(container);
15526         this.rendered = true;
15527         if(position !== undefined){
15528             if(typeof position == 'number'){
15529                 position = this.container.dom.childNodes[position];
15530             }else{
15531                 position = Roo.getDom(position);
15532             }
15533         }
15534         this.onRender(this.container, position || null);
15535         if(this.cls){
15536             this.el.addClass(this.cls);
15537             delete this.cls;
15538         }
15539         if(this.style){
15540             this.el.applyStyles(this.style);
15541             delete this.style;
15542         }
15543         this.fireEvent("render", this);
15544         this.afterRender(this.container);
15545         if(this.hidden){
15546             this.hide();
15547         }
15548         if(this.disabled){
15549             this.disable();
15550         }
15551
15552         return this;
15553         
15554     },
15555
15556     /** @private */
15557     // default function is not really useful
15558     onRender : function(ct, position){
15559         if(this.el){
15560             this.el = Roo.get(this.el);
15561             if(this.allowDomMove !== false){
15562                 ct.dom.insertBefore(this.el.dom, position);
15563             }
15564         }
15565     },
15566
15567     /** @private */
15568     getAutoCreate : function(){
15569         var cfg = typeof this.autoCreate == "object" ?
15570                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
15571         if(this.id && !cfg.id){
15572             cfg.id = this.id;
15573         }
15574         return cfg;
15575     },
15576
15577     /** @private */
15578     afterRender : Roo.emptyFn,
15579
15580     /**
15581      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
15582      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
15583      */
15584     destroy : function(){
15585         if(this.fireEvent("beforedestroy", this) !== false){
15586             this.purgeListeners();
15587             this.beforeDestroy();
15588             if(this.rendered){
15589                 this.el.removeAllListeners();
15590                 this.el.remove();
15591                 if(this.actionMode == "container"){
15592                     this.container.remove();
15593                 }
15594             }
15595             this.onDestroy();
15596             Roo.ComponentMgr.unregister(this);
15597             this.fireEvent("destroy", this);
15598         }
15599     },
15600
15601         /** @private */
15602     beforeDestroy : function(){
15603
15604     },
15605
15606         /** @private */
15607         onDestroy : function(){
15608
15609     },
15610
15611     /**
15612      * Returns the underlying {@link Roo.Element}.
15613      * @return {Roo.Element} The element
15614      */
15615     getEl : function(){
15616         return this.el;
15617     },
15618
15619     /**
15620      * Returns the id of this component.
15621      * @return {String}
15622      */
15623     getId : function(){
15624         return this.id;
15625     },
15626
15627     /**
15628      * Try to focus this component.
15629      * @param {Boolean} selectText True to also select the text in this component (if applicable)
15630      * @return {Roo.Component} this
15631      */
15632     focus : function(selectText){
15633         if(this.rendered){
15634             this.el.focus();
15635             if(selectText === true){
15636                 this.el.dom.select();
15637             }
15638         }
15639         return this;
15640     },
15641
15642     /** @private */
15643     blur : function(){
15644         if(this.rendered){
15645             this.el.blur();
15646         }
15647         return this;
15648     },
15649
15650     /**
15651      * Disable this component.
15652      * @return {Roo.Component} this
15653      */
15654     disable : function(){
15655         if(this.rendered){
15656             this.onDisable();
15657         }
15658         this.disabled = true;
15659         this.fireEvent("disable", this);
15660         return this;
15661     },
15662
15663         // private
15664     onDisable : function(){
15665         this.getActionEl().addClass(this.disabledClass);
15666         this.el.dom.disabled = true;
15667     },
15668
15669     /**
15670      * Enable this component.
15671      * @return {Roo.Component} this
15672      */
15673     enable : function(){
15674         if(this.rendered){
15675             this.onEnable();
15676         }
15677         this.disabled = false;
15678         this.fireEvent("enable", this);
15679         return this;
15680     },
15681
15682         // private
15683     onEnable : function(){
15684         this.getActionEl().removeClass(this.disabledClass);
15685         this.el.dom.disabled = false;
15686     },
15687
15688     /**
15689      * Convenience function for setting disabled/enabled by boolean.
15690      * @param {Boolean} disabled
15691      */
15692     setDisabled : function(disabled){
15693         this[disabled ? "disable" : "enable"]();
15694     },
15695
15696     /**
15697      * Show this component.
15698      * @return {Roo.Component} this
15699      */
15700     show: function(){
15701         if(this.fireEvent("beforeshow", this) !== false){
15702             this.hidden = false;
15703             if(this.rendered){
15704                 this.onShow();
15705             }
15706             this.fireEvent("show", this);
15707         }
15708         return this;
15709     },
15710
15711     // private
15712     onShow : function(){
15713         var ae = this.getActionEl();
15714         if(this.hideMode == 'visibility'){
15715             ae.dom.style.visibility = "visible";
15716         }else if(this.hideMode == 'offsets'){
15717             ae.removeClass('x-hidden');
15718         }else{
15719             ae.dom.style.display = "";
15720         }
15721     },
15722
15723     /**
15724      * Hide this component.
15725      * @return {Roo.Component} this
15726      */
15727     hide: function(){
15728         if(this.fireEvent("beforehide", this) !== false){
15729             this.hidden = true;
15730             if(this.rendered){
15731                 this.onHide();
15732             }
15733             this.fireEvent("hide", this);
15734         }
15735         return this;
15736     },
15737
15738     // private
15739     onHide : function(){
15740         var ae = this.getActionEl();
15741         if(this.hideMode == 'visibility'){
15742             ae.dom.style.visibility = "hidden";
15743         }else if(this.hideMode == 'offsets'){
15744             ae.addClass('x-hidden');
15745         }else{
15746             ae.dom.style.display = "none";
15747         }
15748     },
15749
15750     /**
15751      * Convenience function to hide or show this component by boolean.
15752      * @param {Boolean} visible True to show, false to hide
15753      * @return {Roo.Component} this
15754      */
15755     setVisible: function(visible){
15756         if(visible) {
15757             this.show();
15758         }else{
15759             this.hide();
15760         }
15761         return this;
15762     },
15763
15764     /**
15765      * Returns true if this component is visible.
15766      */
15767     isVisible : function(){
15768         return this.getActionEl().isVisible();
15769     },
15770
15771     cloneConfig : function(overrides){
15772         overrides = overrides || {};
15773         var id = overrides.id || Roo.id();
15774         var cfg = Roo.applyIf(overrides, this.initialConfig);
15775         cfg.id = id; // prevent dup id
15776         return new this.constructor(cfg);
15777     }
15778 });/*
15779  * Based on:
15780  * Ext JS Library 1.1.1
15781  * Copyright(c) 2006-2007, Ext JS, LLC.
15782  *
15783  * Originally Released Under LGPL - original licence link has changed is not relivant.
15784  *
15785  * Fork - LGPL
15786  * <script type="text/javascript">
15787  */
15788
15789 /**
15790  * @class Roo.BoxComponent
15791  * @extends Roo.Component
15792  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
15793  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
15794  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
15795  * layout containers.
15796  * @constructor
15797  * @param {Roo.Element/String/Object} config The configuration options.
15798  */
15799 Roo.BoxComponent = function(config){
15800     Roo.Component.call(this, config);
15801     this.addEvents({
15802         /**
15803          * @event resize
15804          * Fires after the component is resized.
15805              * @param {Roo.Component} this
15806              * @param {Number} adjWidth The box-adjusted width that was set
15807              * @param {Number} adjHeight The box-adjusted height that was set
15808              * @param {Number} rawWidth The width that was originally specified
15809              * @param {Number} rawHeight The height that was originally specified
15810              */
15811         resize : true,
15812         /**
15813          * @event move
15814          * Fires after the component is moved.
15815              * @param {Roo.Component} this
15816              * @param {Number} x The new x position
15817              * @param {Number} y The new y position
15818              */
15819         move : true
15820     });
15821 };
15822
15823 Roo.extend(Roo.BoxComponent, Roo.Component, {
15824     // private, set in afterRender to signify that the component has been rendered
15825     boxReady : false,
15826     // private, used to defer height settings to subclasses
15827     deferHeight: false,
15828     /** @cfg {Number} width
15829      * width (optional) size of component
15830      */
15831      /** @cfg {Number} height
15832      * height (optional) size of component
15833      */
15834      
15835     /**
15836      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
15837      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
15838      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
15839      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
15840      * @return {Roo.BoxComponent} this
15841      */
15842     setSize : function(w, h){
15843         // support for standard size objects
15844         if(typeof w == 'object'){
15845             h = w.height;
15846             w = w.width;
15847         }
15848         // not rendered
15849         if(!this.boxReady){
15850             this.width = w;
15851             this.height = h;
15852             return this;
15853         }
15854
15855         // prevent recalcs when not needed
15856         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
15857             return this;
15858         }
15859         this.lastSize = {width: w, height: h};
15860
15861         var adj = this.adjustSize(w, h);
15862         var aw = adj.width, ah = adj.height;
15863         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
15864             var rz = this.getResizeEl();
15865             if(!this.deferHeight && aw !== undefined && ah !== undefined){
15866                 rz.setSize(aw, ah);
15867             }else if(!this.deferHeight && ah !== undefined){
15868                 rz.setHeight(ah);
15869             }else if(aw !== undefined){
15870                 rz.setWidth(aw);
15871             }
15872             this.onResize(aw, ah, w, h);
15873             this.fireEvent('resize', this, aw, ah, w, h);
15874         }
15875         return this;
15876     },
15877
15878     /**
15879      * Gets the current size of the component's underlying element.
15880      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
15881      */
15882     getSize : function(){
15883         return this.el.getSize();
15884     },
15885
15886     /**
15887      * Gets the current XY position of the component's underlying element.
15888      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15889      * @return {Array} The XY position of the element (e.g., [100, 200])
15890      */
15891     getPosition : function(local){
15892         if(local === true){
15893             return [this.el.getLeft(true), this.el.getTop(true)];
15894         }
15895         return this.xy || this.el.getXY();
15896     },
15897
15898     /**
15899      * Gets the current box measurements of the component's underlying element.
15900      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15901      * @returns {Object} box An object in the format {x, y, width, height}
15902      */
15903     getBox : function(local){
15904         var s = this.el.getSize();
15905         if(local){
15906             s.x = this.el.getLeft(true);
15907             s.y = this.el.getTop(true);
15908         }else{
15909             var xy = this.xy || this.el.getXY();
15910             s.x = xy[0];
15911             s.y = xy[1];
15912         }
15913         return s;
15914     },
15915
15916     /**
15917      * Sets the current box measurements of the component's underlying element.
15918      * @param {Object} box An object in the format {x, y, width, height}
15919      * @returns {Roo.BoxComponent} this
15920      */
15921     updateBox : function(box){
15922         this.setSize(box.width, box.height);
15923         this.setPagePosition(box.x, box.y);
15924         return this;
15925     },
15926
15927     // protected
15928     getResizeEl : function(){
15929         return this.resizeEl || this.el;
15930     },
15931
15932     // protected
15933     getPositionEl : function(){
15934         return this.positionEl || this.el;
15935     },
15936
15937     /**
15938      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
15939      * This method fires the move event.
15940      * @param {Number} left The new left
15941      * @param {Number} top The new top
15942      * @returns {Roo.BoxComponent} this
15943      */
15944     setPosition : function(x, y){
15945         this.x = x;
15946         this.y = y;
15947         if(!this.boxReady){
15948             return this;
15949         }
15950         var adj = this.adjustPosition(x, y);
15951         var ax = adj.x, ay = adj.y;
15952
15953         var el = this.getPositionEl();
15954         if(ax !== undefined || ay !== undefined){
15955             if(ax !== undefined && ay !== undefined){
15956                 el.setLeftTop(ax, ay);
15957             }else if(ax !== undefined){
15958                 el.setLeft(ax);
15959             }else if(ay !== undefined){
15960                 el.setTop(ay);
15961             }
15962             this.onPosition(ax, ay);
15963             this.fireEvent('move', this, ax, ay);
15964         }
15965         return this;
15966     },
15967
15968     /**
15969      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
15970      * This method fires the move event.
15971      * @param {Number} x The new x position
15972      * @param {Number} y The new y position
15973      * @returns {Roo.BoxComponent} this
15974      */
15975     setPagePosition : function(x, y){
15976         this.pageX = x;
15977         this.pageY = y;
15978         if(!this.boxReady){
15979             return;
15980         }
15981         if(x === undefined || y === undefined){ // cannot translate undefined points
15982             return;
15983         }
15984         var p = this.el.translatePoints(x, y);
15985         this.setPosition(p.left, p.top);
15986         return this;
15987     },
15988
15989     // private
15990     onRender : function(ct, position){
15991         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
15992         if(this.resizeEl){
15993             this.resizeEl = Roo.get(this.resizeEl);
15994         }
15995         if(this.positionEl){
15996             this.positionEl = Roo.get(this.positionEl);
15997         }
15998     },
15999
16000     // private
16001     afterRender : function(){
16002         Roo.BoxComponent.superclass.afterRender.call(this);
16003         this.boxReady = true;
16004         this.setSize(this.width, this.height);
16005         if(this.x || this.y){
16006             this.setPosition(this.x, this.y);
16007         }
16008         if(this.pageX || this.pageY){
16009             this.setPagePosition(this.pageX, this.pageY);
16010         }
16011     },
16012
16013     /**
16014      * Force the component's size to recalculate based on the underlying element's current height and width.
16015      * @returns {Roo.BoxComponent} this
16016      */
16017     syncSize : function(){
16018         delete this.lastSize;
16019         this.setSize(this.el.getWidth(), this.el.getHeight());
16020         return this;
16021     },
16022
16023     /**
16024      * Called after the component is resized, this method is empty by default but can be implemented by any
16025      * subclass that needs to perform custom logic after a resize occurs.
16026      * @param {Number} adjWidth The box-adjusted width that was set
16027      * @param {Number} adjHeight The box-adjusted height that was set
16028      * @param {Number} rawWidth The width that was originally specified
16029      * @param {Number} rawHeight The height that was originally specified
16030      */
16031     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
16032
16033     },
16034
16035     /**
16036      * Called after the component is moved, this method is empty by default but can be implemented by any
16037      * subclass that needs to perform custom logic after a move occurs.
16038      * @param {Number} x The new x position
16039      * @param {Number} y The new y position
16040      */
16041     onPosition : function(x, y){
16042
16043     },
16044
16045     // private
16046     adjustSize : function(w, h){
16047         if(this.autoWidth){
16048             w = 'auto';
16049         }
16050         if(this.autoHeight){
16051             h = 'auto';
16052         }
16053         return {width : w, height: h};
16054     },
16055
16056     // private
16057     adjustPosition : function(x, y){
16058         return {x : x, y: y};
16059     }
16060 });/*
16061  * Based on:
16062  * Ext JS Library 1.1.1
16063  * Copyright(c) 2006-2007, Ext JS, LLC.
16064  *
16065  * Originally Released Under LGPL - original licence link has changed is not relivant.
16066  *
16067  * Fork - LGPL
16068  * <script type="text/javascript">
16069  */
16070  (function(){ 
16071 /**
16072  * @class Roo.Layer
16073  * @extends Roo.Element
16074  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
16075  * automatic maintaining of shadow/shim positions.
16076  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
16077  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
16078  * you can pass a string with a CSS class name. False turns off the shadow.
16079  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
16080  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
16081  * @cfg {String} cls CSS class to add to the element
16082  * @cfg {Number} zindex Starting z-index (defaults to 11000)
16083  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
16084  * @constructor
16085  * @param {Object} config An object with config options.
16086  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
16087  */
16088
16089 Roo.Layer = function(config, existingEl){
16090     config = config || {};
16091     var dh = Roo.DomHelper;
16092     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
16093     if(existingEl){
16094         this.dom = Roo.getDom(existingEl);
16095     }
16096     if(!this.dom){
16097         var o = config.dh || {tag: "div", cls: "x-layer"};
16098         this.dom = dh.append(pel, o);
16099     }
16100     if(config.cls){
16101         this.addClass(config.cls);
16102     }
16103     this.constrain = config.constrain !== false;
16104     this.visibilityMode = Roo.Element.VISIBILITY;
16105     if(config.id){
16106         this.id = this.dom.id = config.id;
16107     }else{
16108         this.id = Roo.id(this.dom);
16109     }
16110     this.zindex = config.zindex || this.getZIndex();
16111     this.position("absolute", this.zindex);
16112     if(config.shadow){
16113         this.shadowOffset = config.shadowOffset || 4;
16114         this.shadow = new Roo.Shadow({
16115             offset : this.shadowOffset,
16116             mode : config.shadow
16117         });
16118     }else{
16119         this.shadowOffset = 0;
16120     }
16121     this.useShim = config.shim !== false && Roo.useShims;
16122     this.useDisplay = config.useDisplay;
16123     this.hide();
16124 };
16125
16126 var supr = Roo.Element.prototype;
16127
16128 // shims are shared among layer to keep from having 100 iframes
16129 var shims = [];
16130
16131 Roo.extend(Roo.Layer, Roo.Element, {
16132
16133     getZIndex : function(){
16134         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
16135     },
16136
16137     getShim : function(){
16138         if(!this.useShim){
16139             return null;
16140         }
16141         if(this.shim){
16142             return this.shim;
16143         }
16144         var shim = shims.shift();
16145         if(!shim){
16146             shim = this.createShim();
16147             shim.enableDisplayMode('block');
16148             shim.dom.style.display = 'none';
16149             shim.dom.style.visibility = 'visible';
16150         }
16151         var pn = this.dom.parentNode;
16152         if(shim.dom.parentNode != pn){
16153             pn.insertBefore(shim.dom, this.dom);
16154         }
16155         shim.setStyle('z-index', this.getZIndex()-2);
16156         this.shim = shim;
16157         return shim;
16158     },
16159
16160     hideShim : function(){
16161         if(this.shim){
16162             this.shim.setDisplayed(false);
16163             shims.push(this.shim);
16164             delete this.shim;
16165         }
16166     },
16167
16168     disableShadow : function(){
16169         if(this.shadow){
16170             this.shadowDisabled = true;
16171             this.shadow.hide();
16172             this.lastShadowOffset = this.shadowOffset;
16173             this.shadowOffset = 0;
16174         }
16175     },
16176
16177     enableShadow : function(show){
16178         if(this.shadow){
16179             this.shadowDisabled = false;
16180             this.shadowOffset = this.lastShadowOffset;
16181             delete this.lastShadowOffset;
16182             if(show){
16183                 this.sync(true);
16184             }
16185         }
16186     },
16187
16188     // private
16189     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
16190     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
16191     sync : function(doShow){
16192         var sw = this.shadow;
16193         if(!this.updating && this.isVisible() && (sw || this.useShim)){
16194             var sh = this.getShim();
16195
16196             var w = this.getWidth(),
16197                 h = this.getHeight();
16198
16199             var l = this.getLeft(true),
16200                 t = this.getTop(true);
16201
16202             if(sw && !this.shadowDisabled){
16203                 if(doShow && !sw.isVisible()){
16204                     sw.show(this);
16205                 }else{
16206                     sw.realign(l, t, w, h);
16207                 }
16208                 if(sh){
16209                     if(doShow){
16210                        sh.show();
16211                     }
16212                     // fit the shim behind the shadow, so it is shimmed too
16213                     var a = sw.adjusts, s = sh.dom.style;
16214                     s.left = (Math.min(l, l+a.l))+"px";
16215                     s.top = (Math.min(t, t+a.t))+"px";
16216                     s.width = (w+a.w)+"px";
16217                     s.height = (h+a.h)+"px";
16218                 }
16219             }else if(sh){
16220                 if(doShow){
16221                    sh.show();
16222                 }
16223                 sh.setSize(w, h);
16224                 sh.setLeftTop(l, t);
16225             }
16226             
16227         }
16228     },
16229
16230     // private
16231     destroy : function(){
16232         this.hideShim();
16233         if(this.shadow){
16234             this.shadow.hide();
16235         }
16236         this.removeAllListeners();
16237         var pn = this.dom.parentNode;
16238         if(pn){
16239             pn.removeChild(this.dom);
16240         }
16241         Roo.Element.uncache(this.id);
16242     },
16243
16244     remove : function(){
16245         this.destroy();
16246     },
16247
16248     // private
16249     beginUpdate : function(){
16250         this.updating = true;
16251     },
16252
16253     // private
16254     endUpdate : function(){
16255         this.updating = false;
16256         this.sync(true);
16257     },
16258
16259     // private
16260     hideUnders : function(negOffset){
16261         if(this.shadow){
16262             this.shadow.hide();
16263         }
16264         this.hideShim();
16265     },
16266
16267     // private
16268     constrainXY : function(){
16269         if(this.constrain){
16270             var vw = Roo.lib.Dom.getViewWidth(),
16271                 vh = Roo.lib.Dom.getViewHeight();
16272             var s = Roo.get(document).getScroll();
16273
16274             var xy = this.getXY();
16275             var x = xy[0], y = xy[1];   
16276             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
16277             // only move it if it needs it
16278             var moved = false;
16279             // first validate right/bottom
16280             if((x + w) > vw+s.left){
16281                 x = vw - w - this.shadowOffset;
16282                 moved = true;
16283             }
16284             if((y + h) > vh+s.top){
16285                 y = vh - h - this.shadowOffset;
16286                 moved = true;
16287             }
16288             // then make sure top/left isn't negative
16289             if(x < s.left){
16290                 x = s.left;
16291                 moved = true;
16292             }
16293             if(y < s.top){
16294                 y = s.top;
16295                 moved = true;
16296             }
16297             if(moved){
16298                 if(this.avoidY){
16299                     var ay = this.avoidY;
16300                     if(y <= ay && (y+h) >= ay){
16301                         y = ay-h-5;   
16302                     }
16303                 }
16304                 xy = [x, y];
16305                 this.storeXY(xy);
16306                 supr.setXY.call(this, xy);
16307                 this.sync();
16308             }
16309         }
16310     },
16311
16312     isVisible : function(){
16313         return this.visible;    
16314     },
16315
16316     // private
16317     showAction : function(){
16318         this.visible = true; // track visibility to prevent getStyle calls
16319         if(this.useDisplay === true){
16320             this.setDisplayed("");
16321         }else if(this.lastXY){
16322             supr.setXY.call(this, this.lastXY);
16323         }else if(this.lastLT){
16324             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
16325         }
16326     },
16327
16328     // private
16329     hideAction : function(){
16330         this.visible = false;
16331         if(this.useDisplay === true){
16332             this.setDisplayed(false);
16333         }else{
16334             this.setLeftTop(-10000,-10000);
16335         }
16336     },
16337
16338     // overridden Element method
16339     setVisible : function(v, a, d, c, e){
16340         if(v){
16341             this.showAction();
16342         }
16343         if(a && v){
16344             var cb = function(){
16345                 this.sync(true);
16346                 if(c){
16347                     c();
16348                 }
16349             }.createDelegate(this);
16350             supr.setVisible.call(this, true, true, d, cb, e);
16351         }else{
16352             if(!v){
16353                 this.hideUnders(true);
16354             }
16355             var cb = c;
16356             if(a){
16357                 cb = function(){
16358                     this.hideAction();
16359                     if(c){
16360                         c();
16361                     }
16362                 }.createDelegate(this);
16363             }
16364             supr.setVisible.call(this, v, a, d, cb, e);
16365             if(v){
16366                 this.sync(true);
16367             }else if(!a){
16368                 this.hideAction();
16369             }
16370         }
16371     },
16372
16373     storeXY : function(xy){
16374         delete this.lastLT;
16375         this.lastXY = xy;
16376     },
16377
16378     storeLeftTop : function(left, top){
16379         delete this.lastXY;
16380         this.lastLT = [left, top];
16381     },
16382
16383     // private
16384     beforeFx : function(){
16385         this.beforeAction();
16386         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
16387     },
16388
16389     // private
16390     afterFx : function(){
16391         Roo.Layer.superclass.afterFx.apply(this, arguments);
16392         this.sync(this.isVisible());
16393     },
16394
16395     // private
16396     beforeAction : function(){
16397         if(!this.updating && this.shadow){
16398             this.shadow.hide();
16399         }
16400     },
16401
16402     // overridden Element method
16403     setLeft : function(left){
16404         this.storeLeftTop(left, this.getTop(true));
16405         supr.setLeft.apply(this, arguments);
16406         this.sync();
16407     },
16408
16409     setTop : function(top){
16410         this.storeLeftTop(this.getLeft(true), top);
16411         supr.setTop.apply(this, arguments);
16412         this.sync();
16413     },
16414
16415     setLeftTop : function(left, top){
16416         this.storeLeftTop(left, top);
16417         supr.setLeftTop.apply(this, arguments);
16418         this.sync();
16419     },
16420
16421     setXY : function(xy, a, d, c, e){
16422         this.fixDisplay();
16423         this.beforeAction();
16424         this.storeXY(xy);
16425         var cb = this.createCB(c);
16426         supr.setXY.call(this, xy, a, d, cb, e);
16427         if(!a){
16428             cb();
16429         }
16430     },
16431
16432     // private
16433     createCB : function(c){
16434         var el = this;
16435         return function(){
16436             el.constrainXY();
16437             el.sync(true);
16438             if(c){
16439                 c();
16440             }
16441         };
16442     },
16443
16444     // overridden Element method
16445     setX : function(x, a, d, c, e){
16446         this.setXY([x, this.getY()], a, d, c, e);
16447     },
16448
16449     // overridden Element method
16450     setY : function(y, a, d, c, e){
16451         this.setXY([this.getX(), y], a, d, c, e);
16452     },
16453
16454     // overridden Element method
16455     setSize : function(w, h, a, d, c, e){
16456         this.beforeAction();
16457         var cb = this.createCB(c);
16458         supr.setSize.call(this, w, h, a, d, cb, e);
16459         if(!a){
16460             cb();
16461         }
16462     },
16463
16464     // overridden Element method
16465     setWidth : function(w, a, d, c, e){
16466         this.beforeAction();
16467         var cb = this.createCB(c);
16468         supr.setWidth.call(this, w, a, d, cb, e);
16469         if(!a){
16470             cb();
16471         }
16472     },
16473
16474     // overridden Element method
16475     setHeight : function(h, a, d, c, e){
16476         this.beforeAction();
16477         var cb = this.createCB(c);
16478         supr.setHeight.call(this, h, a, d, cb, e);
16479         if(!a){
16480             cb();
16481         }
16482     },
16483
16484     // overridden Element method
16485     setBounds : function(x, y, w, h, a, d, c, e){
16486         this.beforeAction();
16487         var cb = this.createCB(c);
16488         if(!a){
16489             this.storeXY([x, y]);
16490             supr.setXY.call(this, [x, y]);
16491             supr.setSize.call(this, w, h, a, d, cb, e);
16492             cb();
16493         }else{
16494             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
16495         }
16496         return this;
16497     },
16498     
16499     /**
16500      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
16501      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
16502      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
16503      * @param {Number} zindex The new z-index to set
16504      * @return {this} The Layer
16505      */
16506     setZIndex : function(zindex){
16507         this.zindex = zindex;
16508         this.setStyle("z-index", zindex + 2);
16509         if(this.shadow){
16510             this.shadow.setZIndex(zindex + 1);
16511         }
16512         if(this.shim){
16513             this.shim.setStyle("z-index", zindex);
16514         }
16515     }
16516 });
16517 })();/*
16518  * Original code for Roojs - LGPL
16519  * <script type="text/javascript">
16520  */
16521  
16522 /**
16523  * @class Roo.XComponent
16524  * A delayed Element creator...
16525  * Or a way to group chunks of interface together.
16526  * technically this is a wrapper around a tree of Roo elements (which defines a 'module'),
16527  *  used in conjunction with XComponent.build() it will create an instance of each element,
16528  *  then call addxtype() to build the User interface.
16529  * 
16530  * Mypart.xyx = new Roo.XComponent({
16531
16532     parent : 'Mypart.xyz', // empty == document.element.!!
16533     order : '001',
16534     name : 'xxxx'
16535     region : 'xxxx'
16536     disabled : function() {} 
16537      
16538     tree : function() { // return an tree of xtype declared components
16539         var MODULE = this;
16540         return 
16541         {
16542             xtype : 'NestedLayoutPanel',
16543             // technicall
16544         }
16545      ]
16546  *})
16547  *
16548  *
16549  * It can be used to build a big heiracy, with parent etc.
16550  * or you can just use this to render a single compoent to a dom element
16551  * MYPART.render(Roo.Element | String(id) | dom_element )
16552  *
16553  *
16554  * Usage patterns.
16555  *
16556  * Classic Roo
16557  *
16558  * Roo is designed primarily as a single page application, so the UI build for a standard interface will
16559  * expect a single 'TOP' level module normally indicated by the 'parent' of the XComponent definition being defined as false.
16560  *
16561  * Each sub module is expected to have a parent pointing to the class name of it's parent module.
16562  *
16563  * When the top level is false, a 'Roo.BorderLayout' is created and the element is flagged as 'topModule'
16564  * - if mulitple topModules exist, the last one is defined as the top module.
16565  *
16566  * Embeded Roo
16567  * 
16568  * When the top level or multiple modules are to embedded into a existing HTML page,
16569  * the parent element can container '#id' of the element where the module will be drawn.
16570  *
16571  * Bootstrap Roo
16572  *
16573  * Unlike classic Roo, the bootstrap tends not to be used as a single page.
16574  * it relies more on a include mechanism, where sub modules are included into an outer page.
16575  * This is normally managed by the builder tools using Roo.apply( options, Included.Sub.Module )
16576  * 
16577  * Bootstrap Roo Included elements
16578  *
16579  * Our builder application needs the ability to preview these sub compoennts. They will normally have parent=false set,
16580  * hence confusing the component builder as it thinks there are multiple top level elements. 
16581  *
16582  * String Over-ride & Translations
16583  *
16584  * Our builder application writes all the strings as _strings and _named_strings. This is to enable the translation of elements,
16585  * and also the 'overlaying of string values - needed when different versions of the same application with different text content
16586  * are needed. @see Roo.XComponent.overlayString  
16587  * 
16588  * 
16589  * 
16590  * @extends Roo.util.Observable
16591  * @constructor
16592  * @param cfg {Object} configuration of component
16593  * 
16594  */
16595 Roo.XComponent = function(cfg) {
16596     Roo.apply(this, cfg);
16597     this.addEvents({ 
16598         /**
16599              * @event built
16600              * Fires when this the componnt is built
16601              * @param {Roo.XComponent} c the component
16602              */
16603         'built' : true
16604         
16605     });
16606     this.region = this.region || 'center'; // default..
16607     Roo.XComponent.register(this);
16608     this.modules = false;
16609     this.el = false; // where the layout goes..
16610     
16611     
16612 }
16613 Roo.extend(Roo.XComponent, Roo.util.Observable, {
16614     /**
16615      * @property el
16616      * The created element (with Roo.factory())
16617      * @type {Roo.Layout}
16618      */
16619     el  : false,
16620     
16621     /**
16622      * @property el
16623      * for BC  - use el in new code
16624      * @type {Roo.Layout}
16625      */
16626     panel : false,
16627     
16628     /**
16629      * @property layout
16630      * for BC  - use el in new code
16631      * @type {Roo.Layout}
16632      */
16633     layout : false,
16634     
16635      /**
16636      * @cfg {Function|boolean} disabled
16637      * If this module is disabled by some rule, return true from the funtion
16638      */
16639     disabled : false,
16640     
16641     /**
16642      * @cfg {String} parent 
16643      * Name of parent element which it get xtype added to..
16644      */
16645     parent: false,
16646     
16647     /**
16648      * @cfg {String} order
16649      * Used to set the order in which elements are created (usefull for multiple tabs)
16650      */
16651     
16652     order : false,
16653     /**
16654      * @cfg {String} name
16655      * String to display while loading.
16656      */
16657     name : false,
16658     /**
16659      * @cfg {String} region
16660      * Region to render component to (defaults to center)
16661      */
16662     region : 'center',
16663     
16664     /**
16665      * @cfg {Array} items
16666      * A single item array - the first element is the root of the tree..
16667      * It's done this way to stay compatible with the Xtype system...
16668      */
16669     items : false,
16670     
16671     /**
16672      * @property _tree
16673      * The method that retuns the tree of parts that make up this compoennt 
16674      * @type {function}
16675      */
16676     _tree  : false,
16677     
16678      /**
16679      * render
16680      * render element to dom or tree
16681      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
16682      */
16683     
16684     render : function(el)
16685     {
16686         
16687         el = el || false;
16688         var hp = this.parent ? 1 : 0;
16689         Roo.debug &&  Roo.log(this);
16690         
16691         var tree = this._tree ? this._tree() : this.tree();
16692
16693         
16694         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
16695             // if parent is a '#.....' string, then let's use that..
16696             var ename = this.parent.substr(1);
16697             this.parent = false;
16698             Roo.debug && Roo.log(ename);
16699             switch (ename) {
16700                 case 'bootstrap-body':
16701                     if (typeof(tree.el) != 'undefined' && tree.el == document.body)  {
16702                         // this is the BorderLayout standard?
16703                        this.parent = { el : true };
16704                        break;
16705                     }
16706                     if (["Nest", "Content", "Grid", "Tree"].indexOf(tree.xtype)  > -1)  {
16707                         // need to insert stuff...
16708                         this.parent =  {
16709                              el : new Roo.bootstrap.layout.Border({
16710                                  el : document.body, 
16711                      
16712                                  center: {
16713                                     titlebar: false,
16714                                     autoScroll:false,
16715                                     closeOnTab: true,
16716                                     tabPosition: 'top',
16717                                       //resizeTabs: true,
16718                                     alwaysShowTabs: true,
16719                                     hideTabs: false
16720                                      //minTabWidth: 140
16721                                  }
16722                              })
16723                         
16724                          };
16725                          break;
16726                     }
16727                          
16728                     if (typeof(Roo.bootstrap.Body) != 'undefined' ) {
16729                         this.parent = { el :  new  Roo.bootstrap.Body() };
16730                         Roo.debug && Roo.log("setting el to doc body");
16731                          
16732                     } else {
16733                         throw "Container is bootstrap body, but Roo.bootstrap.Body is not defined";
16734                     }
16735                     break;
16736                 case 'bootstrap':
16737                     this.parent = { el : true};
16738                     // fall through
16739                 default:
16740                     el = Roo.get(ename);
16741                     if (typeof(Roo.bootstrap) != 'undefined' && tree['|xns'] == 'Roo.bootstrap') {
16742                         this.parent = { el : true};
16743                     }
16744                     
16745                     break;
16746             }
16747                 
16748             
16749             if (!el && !this.parent) {
16750                 Roo.debug && Roo.log("Warning - element can not be found :#" + ename );
16751                 return;
16752             }
16753         }
16754         
16755         Roo.debug && Roo.log("EL:");
16756         Roo.debug && Roo.log(el);
16757         Roo.debug && Roo.log("this.parent.el:");
16758         Roo.debug && Roo.log(this.parent.el);
16759         
16760
16761         // altertive root elements ??? - we need a better way to indicate these.
16762         var is_alt = Roo.XComponent.is_alt ||
16763                     (typeof(tree.el) != 'undefined' && tree.el == document.body) ||
16764                     (typeof(Roo.bootstrap) != 'undefined' && tree.xns == Roo.bootstrap) ||
16765                     (typeof(Roo.mailer) != 'undefined' && tree.xns == Roo.mailer) ;
16766         
16767         
16768         
16769         if (!this.parent && is_alt) {
16770             //el = Roo.get(document.body);
16771             this.parent = { el : true };
16772         }
16773             
16774             
16775         
16776         if (!this.parent) {
16777             
16778             Roo.debug && Roo.log("no parent - creating one");
16779             
16780             el = el ? Roo.get(el) : false;      
16781             
16782             if (typeof(Roo.BorderLayout) == 'undefined' ) {
16783                 
16784                 this.parent =  {
16785                     el : new Roo.bootstrap.layout.Border({
16786                         el: el || document.body,
16787                     
16788                         center: {
16789                             titlebar: false,
16790                             autoScroll:false,
16791                             closeOnTab: true,
16792                             tabPosition: 'top',
16793                              //resizeTabs: true,
16794                             alwaysShowTabs: false,
16795                             hideTabs: true,
16796                             minTabWidth: 140,
16797                             overflow: 'visible'
16798                          }
16799                      })
16800                 };
16801             } else {
16802             
16803                 // it's a top level one..
16804                 this.parent =  {
16805                     el : new Roo.BorderLayout(el || document.body, {
16806                         center: {
16807                             titlebar: false,
16808                             autoScroll:false,
16809                             closeOnTab: true,
16810                             tabPosition: 'top',
16811                              //resizeTabs: true,
16812                             alwaysShowTabs: el && hp? false :  true,
16813                             hideTabs: el || !hp ? true :  false,
16814                             minTabWidth: 140
16815                          }
16816                     })
16817                 };
16818             }
16819         }
16820         
16821         if (!this.parent.el) {
16822                 // probably an old style ctor, which has been disabled.
16823                 return;
16824
16825         }
16826                 // The 'tree' method is  '_tree now' 
16827             
16828         tree.region = tree.region || this.region;
16829         var is_body = false;
16830         if (this.parent.el === true) {
16831             // bootstrap... - body..
16832             if (el) {
16833                 tree.el = el;
16834             }
16835             this.parent.el = Roo.factory(tree);
16836             is_body = true;
16837         }
16838         
16839         this.el = this.parent.el.addxtype(tree, undefined, is_body);
16840         this.fireEvent('built', this);
16841         
16842         this.panel = this.el;
16843         this.layout = this.panel.layout;
16844         this.parentLayout = this.parent.layout  || false;  
16845          
16846     }
16847     
16848 });
16849
16850 Roo.apply(Roo.XComponent, {
16851     /**
16852      * @property  hideProgress
16853      * true to disable the building progress bar.. usefull on single page renders.
16854      * @type Boolean
16855      */
16856     hideProgress : false,
16857     /**
16858      * @property  buildCompleted
16859      * True when the builder has completed building the interface.
16860      * @type Boolean
16861      */
16862     buildCompleted : false,
16863      
16864     /**
16865      * @property  topModule
16866      * the upper most module - uses document.element as it's constructor.
16867      * @type Object
16868      */
16869      
16870     topModule  : false,
16871       
16872     /**
16873      * @property  modules
16874      * array of modules to be created by registration system.
16875      * @type {Array} of Roo.XComponent
16876      */
16877     
16878     modules : [],
16879     /**
16880      * @property  elmodules
16881      * array of modules to be created by which use #ID 
16882      * @type {Array} of Roo.XComponent
16883      */
16884      
16885     elmodules : [],
16886
16887      /**
16888      * @property  is_alt
16889      * Is an alternative Root - normally used by bootstrap or other systems,
16890      *    where the top element in the tree can wrap 'body' 
16891      * @type {boolean}  (default false)
16892      */
16893      
16894     is_alt : false,
16895     /**
16896      * @property  build_from_html
16897      * Build elements from html - used by bootstrap HTML stuff 
16898      *    - this is cleared after build is completed
16899      * @type {boolean}    (default false)
16900      */
16901      
16902     build_from_html : false,
16903     /**
16904      * Register components to be built later.
16905      *
16906      * This solves the following issues
16907      * - Building is not done on page load, but after an authentication process has occured.
16908      * - Interface elements are registered on page load
16909      * - Parent Interface elements may not be loaded before child, so this handles that..
16910      * 
16911      *
16912      * example:
16913      * 
16914      * MyApp.register({
16915           order : '000001',
16916           module : 'Pman.Tab.projectMgr',
16917           region : 'center',
16918           parent : 'Pman.layout',
16919           disabled : false,  // or use a function..
16920         })
16921      
16922      * * @param {Object} details about module
16923      */
16924     register : function(obj) {
16925                 
16926         Roo.XComponent.event.fireEvent('register', obj);
16927         switch(typeof(obj.disabled) ) {
16928                 
16929             case 'undefined':
16930                 break;
16931             
16932             case 'function':
16933                 if ( obj.disabled() ) {
16934                         return;
16935                 }
16936                 break;
16937             
16938             default:
16939                 if (obj.disabled || obj.region == '#disabled') {
16940                         return;
16941                 }
16942                 break;
16943         }
16944                 
16945         this.modules.push(obj);
16946          
16947     },
16948     /**
16949      * convert a string to an object..
16950      * eg. 'AAA.BBB' -> finds AAA.BBB
16951
16952      */
16953     
16954     toObject : function(str)
16955     {
16956         if (!str || typeof(str) == 'object') {
16957             return str;
16958         }
16959         if (str.substring(0,1) == '#') {
16960             return str;
16961         }
16962
16963         var ar = str.split('.');
16964         var rt, o;
16965         rt = ar.shift();
16966             /** eval:var:o */
16967         try {
16968             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
16969         } catch (e) {
16970             throw "Module not found : " + str;
16971         }
16972         
16973         if (o === false) {
16974             throw "Module not found : " + str;
16975         }
16976         Roo.each(ar, function(e) {
16977             if (typeof(o[e]) == 'undefined') {
16978                 throw "Module not found : " + str;
16979             }
16980             o = o[e];
16981         });
16982         
16983         return o;
16984         
16985     },
16986     
16987     
16988     /**
16989      * move modules into their correct place in the tree..
16990      * 
16991      */
16992     preBuild : function ()
16993     {
16994         var _t = this;
16995         Roo.each(this.modules , function (obj)
16996         {
16997             Roo.XComponent.event.fireEvent('beforebuild', obj);
16998             
16999             var opar = obj.parent;
17000             try { 
17001                 obj.parent = this.toObject(opar);
17002             } catch(e) {
17003                 Roo.debug && Roo.log("parent:toObject failed: " + e.toString());
17004                 return;
17005             }
17006             
17007             if (!obj.parent) {
17008                 Roo.debug && Roo.log("GOT top level module");
17009                 Roo.debug && Roo.log(obj);
17010                 obj.modules = new Roo.util.MixedCollection(false, 
17011                     function(o) { return o.order + '' }
17012                 );
17013                 this.topModule = obj;
17014                 return;
17015             }
17016                         // parent is a string (usually a dom element name..)
17017             if (typeof(obj.parent) == 'string') {
17018                 this.elmodules.push(obj);
17019                 return;
17020             }
17021             if (obj.parent.constructor != Roo.XComponent) {
17022                 Roo.debug && Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
17023             }
17024             if (!obj.parent.modules) {
17025                 obj.parent.modules = new Roo.util.MixedCollection(false, 
17026                     function(o) { return o.order + '' }
17027                 );
17028             }
17029             if (obj.parent.disabled) {
17030                 obj.disabled = true;
17031             }
17032             obj.parent.modules.add(obj);
17033         }, this);
17034     },
17035     
17036      /**
17037      * make a list of modules to build.
17038      * @return {Array} list of modules. 
17039      */ 
17040     
17041     buildOrder : function()
17042     {
17043         var _this = this;
17044         var cmp = function(a,b) {   
17045             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
17046         };
17047         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
17048             throw "No top level modules to build";
17049         }
17050         
17051         // make a flat list in order of modules to build.
17052         var mods = this.topModule ? [ this.topModule ] : [];
17053                 
17054         
17055         // elmodules (is a list of DOM based modules )
17056         Roo.each(this.elmodules, function(e) {
17057             mods.push(e);
17058             if (!this.topModule &&
17059                 typeof(e.parent) == 'string' &&
17060                 e.parent.substring(0,1) == '#' &&
17061                 Roo.get(e.parent.substr(1))
17062                ) {
17063                 
17064                 _this.topModule = e;
17065             }
17066             
17067         });
17068
17069         
17070         // add modules to their parents..
17071         var addMod = function(m) {
17072             Roo.debug && Roo.log("build Order: add: " + m.name);
17073                 
17074             mods.push(m);
17075             if (m.modules && !m.disabled) {
17076                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
17077                 m.modules.keySort('ASC',  cmp );
17078                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
17079     
17080                 m.modules.each(addMod);
17081             } else {
17082                 Roo.debug && Roo.log("build Order: no child modules");
17083             }
17084             // not sure if this is used any more..
17085             if (m.finalize) {
17086                 m.finalize.name = m.name + " (clean up) ";
17087                 mods.push(m.finalize);
17088             }
17089             
17090         }
17091         if (this.topModule && this.topModule.modules) { 
17092             this.topModule.modules.keySort('ASC',  cmp );
17093             this.topModule.modules.each(addMod);
17094         } 
17095         return mods;
17096     },
17097     
17098      /**
17099      * Build the registered modules.
17100      * @param {Object} parent element.
17101      * @param {Function} optional method to call after module has been added.
17102      * 
17103      */ 
17104    
17105     build : function(opts) 
17106     {
17107         
17108         if (typeof(opts) != 'undefined') {
17109             Roo.apply(this,opts);
17110         }
17111         
17112         this.preBuild();
17113         var mods = this.buildOrder();
17114       
17115         //this.allmods = mods;
17116         //Roo.debug && Roo.log(mods);
17117         //return;
17118         if (!mods.length) { // should not happen
17119             throw "NO modules!!!";
17120         }
17121         
17122         
17123         var msg = "Building Interface...";
17124         // flash it up as modal - so we store the mask!?
17125         if (!this.hideProgress && Roo.MessageBox) {
17126             Roo.MessageBox.show({ title: 'loading' });
17127             Roo.MessageBox.show({
17128                title: "Please wait...",
17129                msg: msg,
17130                width:450,
17131                progress:true,
17132                buttons : false,
17133                closable:false,
17134                modal: false
17135               
17136             });
17137         }
17138         var total = mods.length;
17139         
17140         var _this = this;
17141         var progressRun = function() {
17142             if (!mods.length) {
17143                 Roo.debug && Roo.log('hide?');
17144                 if (!this.hideProgress && Roo.MessageBox) {
17145                     Roo.MessageBox.hide();
17146                 }
17147                 Roo.XComponent.build_from_html = false; // reset, so dialogs will be build from javascript
17148                 
17149                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
17150                 
17151                 // THE END...
17152                 return false;   
17153             }
17154             
17155             var m = mods.shift();
17156             
17157             
17158             Roo.debug && Roo.log(m);
17159             // not sure if this is supported any more.. - modules that are are just function
17160             if (typeof(m) == 'function') { 
17161                 m.call(this);
17162                 return progressRun.defer(10, _this);
17163             } 
17164             
17165             
17166             msg = "Building Interface " + (total  - mods.length) + 
17167                     " of " + total + 
17168                     (m.name ? (' - ' + m.name) : '');
17169                         Roo.debug && Roo.log(msg);
17170             if (!_this.hideProgress &&  Roo.MessageBox) { 
17171                 Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
17172             }
17173             
17174          
17175             // is the module disabled?
17176             var disabled = (typeof(m.disabled) == 'function') ?
17177                 m.disabled.call(m.module.disabled) : m.disabled;    
17178             
17179             
17180             if (disabled) {
17181                 return progressRun(); // we do not update the display!
17182             }
17183             
17184             // now build 
17185             
17186                         
17187                         
17188             m.render();
17189             // it's 10 on top level, and 1 on others??? why...
17190             return progressRun.defer(10, _this);
17191              
17192         }
17193         progressRun.defer(1, _this);
17194      
17195         
17196         
17197     },
17198     /**
17199      * Overlay a set of modified strings onto a component
17200      * This is dependant on our builder exporting the strings and 'named strings' elements.
17201      * 
17202      * @param {Object} element to overlay on - eg. Pman.Dialog.Login
17203      * @param {Object} associative array of 'named' string and it's new value.
17204      * 
17205      */
17206         overlayStrings : function( component, strings )
17207     {
17208         if (typeof(component['_named_strings']) == 'undefined') {
17209             throw "ERROR: component does not have _named_strings";
17210         }
17211         for ( var k in strings ) {
17212             var md = typeof(component['_named_strings'][k]) == 'undefined' ? false : component['_named_strings'][k];
17213             if (md !== false) {
17214                 component['_strings'][md] = strings[k];
17215             } else {
17216                 Roo.log('could not find named string: ' + k + ' in');
17217                 Roo.log(component);
17218             }
17219             
17220         }
17221         
17222     },
17223     
17224         
17225         /**
17226          * Event Object.
17227          *
17228          *
17229          */
17230         event: false, 
17231     /**
17232          * wrapper for event.on - aliased later..  
17233          * Typically use to register a event handler for register:
17234          *
17235          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
17236          *
17237          */
17238     on : false
17239    
17240     
17241     
17242 });
17243
17244 Roo.XComponent.event = new Roo.util.Observable({
17245                 events : { 
17246                         /**
17247                          * @event register
17248                          * Fires when an Component is registered,
17249                          * set the disable property on the Component to stop registration.
17250                          * @param {Roo.XComponent} c the component being registerd.
17251                          * 
17252                          */
17253                         'register' : true,
17254             /**
17255                          * @event beforebuild
17256                          * Fires before each Component is built
17257                          * can be used to apply permissions.
17258                          * @param {Roo.XComponent} c the component being registerd.
17259                          * 
17260                          */
17261                         'beforebuild' : true,
17262                         /**
17263                          * @event buildcomplete
17264                          * Fires on the top level element when all elements have been built
17265                          * @param {Roo.XComponent} the top level component.
17266                          */
17267                         'buildcomplete' : true
17268                         
17269                 }
17270 });
17271
17272 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
17273  //
17274  /**
17275  * marked - a markdown parser
17276  * Copyright (c) 2011-2014, Christopher Jeffrey. (MIT Licensed)
17277  * https://github.com/chjj/marked
17278  */
17279
17280
17281 /**
17282  *
17283  * Roo.Markdown - is a very crude wrapper around marked..
17284  *
17285  * usage:
17286  * 
17287  * alert( Roo.Markdown.toHtml("Markdown *rocks*.") );
17288  * 
17289  * Note: move the sample code to the bottom of this
17290  * file before uncommenting it.
17291  *
17292  */
17293
17294 Roo.Markdown = {};
17295 Roo.Markdown.toHtml = function(text) {
17296     
17297     var c = new Roo.Markdown.marked.setOptions({
17298             renderer: new Roo.Markdown.marked.Renderer(),
17299             gfm: true,
17300             tables: true,
17301             breaks: false,
17302             pedantic: false,
17303             sanitize: false,
17304             smartLists: true,
17305             smartypants: false
17306           });
17307     // A FEW HACKS!!?
17308     
17309     text = text.replace(/\\\n/g,' ');
17310     return Roo.Markdown.marked(text);
17311 };
17312 //
17313 // converter
17314 //
17315 // Wraps all "globals" so that the only thing
17316 // exposed is makeHtml().
17317 //
17318 (function() {
17319     
17320      /**
17321          * eval:var:escape
17322          * eval:var:unescape
17323          * eval:var:replace
17324          */
17325       
17326     /**
17327      * Helpers
17328      */
17329     
17330     var escape = function (html, encode) {
17331       return html
17332         .replace(!encode ? /&(?!#?\w+;)/g : /&/g, '&amp;')
17333         .replace(/</g, '&lt;')
17334         .replace(/>/g, '&gt;')
17335         .replace(/"/g, '&quot;')
17336         .replace(/'/g, '&#39;');
17337     }
17338     
17339     var unescape = function (html) {
17340         // explicitly match decimal, hex, and named HTML entities 
17341       return html.replace(/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/g, function(_, n) {
17342         n = n.toLowerCase();
17343         if (n === 'colon') { return ':'; }
17344         if (n.charAt(0) === '#') {
17345           return n.charAt(1) === 'x'
17346             ? String.fromCharCode(parseInt(n.substring(2), 16))
17347             : String.fromCharCode(+n.substring(1));
17348         }
17349         return '';
17350       });
17351     }
17352     
17353     var replace = function (regex, opt) {
17354       regex = regex.source;
17355       opt = opt || '';
17356       return function self(name, val) {
17357         if (!name) { return new RegExp(regex, opt); }
17358         val = val.source || val;
17359         val = val.replace(/(^|[^\[])\^/g, '$1');
17360         regex = regex.replace(name, val);
17361         return self;
17362       };
17363     }
17364
17365
17366          /**
17367          * eval:var:noop
17368     */
17369     var noop = function () {}
17370     noop.exec = noop;
17371     
17372          /**
17373          * eval:var:merge
17374     */
17375     var merge = function (obj) {
17376       var i = 1
17377         , target
17378         , key;
17379     
17380       for (; i < arguments.length; i++) {
17381         target = arguments[i];
17382         for (key in target) {
17383           if (Object.prototype.hasOwnProperty.call(target, key)) {
17384             obj[key] = target[key];
17385           }
17386         }
17387       }
17388     
17389       return obj;
17390     }
17391     
17392     
17393     /**
17394      * Block-Level Grammar
17395      */
17396     
17397     
17398     
17399     
17400     var block = {
17401       newline: /^\n+/,
17402       code: /^( {4}[^\n]+\n*)+/,
17403       fences: noop,
17404       hr: /^( *[-*_]){3,} *(?:\n+|$)/,
17405       heading: /^ *(#{1,6}) *([^\n]+?) *#* *(?:\n+|$)/,
17406       nptable: noop,
17407       lheading: /^([^\n]+)\n *(=|-){2,} *(?:\n+|$)/,
17408       blockquote: /^( *>[^\n]+(\n(?!def)[^\n]+)*\n*)+/,
17409       list: /^( *)(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,
17410       html: /^ *(?:comment *(?:\n|\s*$)|closed *(?:\n{2,}|\s*$)|closing *(?:\n{2,}|\s*$))/,
17411       def: /^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +["(]([^\n]+)[")])? *(?:\n+|$)/,
17412       table: noop,
17413       paragraph: /^((?:[^\n]+\n?(?!hr|heading|lheading|blockquote|tag|def))+)\n*/,
17414       text: /^[^\n]+/
17415     };
17416     
17417     block.bullet = /(?:[*+-]|\d+\.)/;
17418     block.item = /^( *)(bull) [^\n]*(?:\n(?!\1bull )[^\n]*)*/;
17419     block.item = replace(block.item, 'gm')
17420       (/bull/g, block.bullet)
17421       ();
17422     
17423     block.list = replace(block.list)
17424       (/bull/g, block.bullet)
17425       ('hr', '\\n+(?=\\1?(?:[-*_] *){3,}(?:\\n+|$))')
17426       ('def', '\\n+(?=' + block.def.source + ')')
17427       ();
17428     
17429     block.blockquote = replace(block.blockquote)
17430       ('def', block.def)
17431       ();
17432     
17433     block._tag = '(?!(?:'
17434       + 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code'
17435       + '|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo'
17436       + '|span|br|wbr|ins|del|img)\\b)\\w+(?!:/|[^\\w\\s@]*@)\\b';
17437     
17438     block.html = replace(block.html)
17439       ('comment', /<!--[\s\S]*?-->/)
17440       ('closed', /<(tag)[\s\S]+?<\/\1>/)
17441       ('closing', /<tag(?:"[^"]*"|'[^']*'|[^'">])*?>/)
17442       (/tag/g, block._tag)
17443       ();
17444     
17445     block.paragraph = replace(block.paragraph)
17446       ('hr', block.hr)
17447       ('heading', block.heading)
17448       ('lheading', block.lheading)
17449       ('blockquote', block.blockquote)
17450       ('tag', '<' + block._tag)
17451       ('def', block.def)
17452       ();
17453     
17454     /**
17455      * Normal Block Grammar
17456      */
17457     
17458     block.normal = merge({}, block);
17459     
17460     /**
17461      * GFM Block Grammar
17462      */
17463     
17464     block.gfm = merge({}, block.normal, {
17465       fences: /^ *(`{3,}|~{3,})[ \.]*(\S+)? *\n([\s\S]*?)\s*\1 *(?:\n+|$)/,
17466       paragraph: /^/,
17467       heading: /^ *(#{1,6}) +([^\n]+?) *#* *(?:\n+|$)/
17468     });
17469     
17470     block.gfm.paragraph = replace(block.paragraph)
17471       ('(?!', '(?!'
17472         + block.gfm.fences.source.replace('\\1', '\\2') + '|'
17473         + block.list.source.replace('\\1', '\\3') + '|')
17474       ();
17475     
17476     /**
17477      * GFM + Tables Block Grammar
17478      */
17479     
17480     block.tables = merge({}, block.gfm, {
17481       nptable: /^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*/,
17482       table: /^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)\n*/
17483     });
17484     
17485     /**
17486      * Block Lexer
17487      */
17488     
17489     var Lexer = function (options) {
17490       this.tokens = [];
17491       this.tokens.links = {};
17492       this.options = options || marked.defaults;
17493       this.rules = block.normal;
17494     
17495       if (this.options.gfm) {
17496         if (this.options.tables) {
17497           this.rules = block.tables;
17498         } else {
17499           this.rules = block.gfm;
17500         }
17501       }
17502     }
17503     
17504     /**
17505      * Expose Block Rules
17506      */
17507     
17508     Lexer.rules = block;
17509     
17510     /**
17511      * Static Lex Method
17512      */
17513     
17514     Lexer.lex = function(src, options) {
17515       var lexer = new Lexer(options);
17516       return lexer.lex(src);
17517     };
17518     
17519     /**
17520      * Preprocessing
17521      */
17522     
17523     Lexer.prototype.lex = function(src) {
17524       src = src
17525         .replace(/\r\n|\r/g, '\n')
17526         .replace(/\t/g, '    ')
17527         .replace(/\u00a0/g, ' ')
17528         .replace(/\u2424/g, '\n');
17529     
17530       return this.token(src, true);
17531     };
17532     
17533     /**
17534      * Lexing
17535      */
17536     
17537     Lexer.prototype.token = function(src, top, bq) {
17538       var src = src.replace(/^ +$/gm, '')
17539         , next
17540         , loose
17541         , cap
17542         , bull
17543         , b
17544         , item
17545         , space
17546         , i
17547         , l;
17548     
17549       while (src) {
17550         // newline
17551         if (cap = this.rules.newline.exec(src)) {
17552           src = src.substring(cap[0].length);
17553           if (cap[0].length > 1) {
17554             this.tokens.push({
17555               type: 'space'
17556             });
17557           }
17558         }
17559     
17560         // code
17561         if (cap = this.rules.code.exec(src)) {
17562           src = src.substring(cap[0].length);
17563           cap = cap[0].replace(/^ {4}/gm, '');
17564           this.tokens.push({
17565             type: 'code',
17566             text: !this.options.pedantic
17567               ? cap.replace(/\n+$/, '')
17568               : cap
17569           });
17570           continue;
17571         }
17572     
17573         // fences (gfm)
17574         if (cap = this.rules.fences.exec(src)) {
17575           src = src.substring(cap[0].length);
17576           this.tokens.push({
17577             type: 'code',
17578             lang: cap[2],
17579             text: cap[3] || ''
17580           });
17581           continue;
17582         }
17583     
17584         // heading
17585         if (cap = this.rules.heading.exec(src)) {
17586           src = src.substring(cap[0].length);
17587           this.tokens.push({
17588             type: 'heading',
17589             depth: cap[1].length,
17590             text: cap[2]
17591           });
17592           continue;
17593         }
17594     
17595         // table no leading pipe (gfm)
17596         if (top && (cap = this.rules.nptable.exec(src))) {
17597           src = src.substring(cap[0].length);
17598     
17599           item = {
17600             type: 'table',
17601             header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
17602             align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
17603             cells: cap[3].replace(/\n$/, '').split('\n')
17604           };
17605     
17606           for (i = 0; i < item.align.length; i++) {
17607             if (/^ *-+: *$/.test(item.align[i])) {
17608               item.align[i] = 'right';
17609             } else if (/^ *:-+: *$/.test(item.align[i])) {
17610               item.align[i] = 'center';
17611             } else if (/^ *:-+ *$/.test(item.align[i])) {
17612               item.align[i] = 'left';
17613             } else {
17614               item.align[i] = null;
17615             }
17616           }
17617     
17618           for (i = 0; i < item.cells.length; i++) {
17619             item.cells[i] = item.cells[i].split(/ *\| */);
17620           }
17621     
17622           this.tokens.push(item);
17623     
17624           continue;
17625         }
17626     
17627         // lheading
17628         if (cap = this.rules.lheading.exec(src)) {
17629           src = src.substring(cap[0].length);
17630           this.tokens.push({
17631             type: 'heading',
17632             depth: cap[2] === '=' ? 1 : 2,
17633             text: cap[1]
17634           });
17635           continue;
17636         }
17637     
17638         // hr
17639         if (cap = this.rules.hr.exec(src)) {
17640           src = src.substring(cap[0].length);
17641           this.tokens.push({
17642             type: 'hr'
17643           });
17644           continue;
17645         }
17646     
17647         // blockquote
17648         if (cap = this.rules.blockquote.exec(src)) {
17649           src = src.substring(cap[0].length);
17650     
17651           this.tokens.push({
17652             type: 'blockquote_start'
17653           });
17654     
17655           cap = cap[0].replace(/^ *> ?/gm, '');
17656     
17657           // Pass `top` to keep the current
17658           // "toplevel" state. This is exactly
17659           // how markdown.pl works.
17660           this.token(cap, top, true);
17661     
17662           this.tokens.push({
17663             type: 'blockquote_end'
17664           });
17665     
17666           continue;
17667         }
17668     
17669         // list
17670         if (cap = this.rules.list.exec(src)) {
17671           src = src.substring(cap[0].length);
17672           bull = cap[2];
17673     
17674           this.tokens.push({
17675             type: 'list_start',
17676             ordered: bull.length > 1
17677           });
17678     
17679           // Get each top-level item.
17680           cap = cap[0].match(this.rules.item);
17681     
17682           next = false;
17683           l = cap.length;
17684           i = 0;
17685     
17686           for (; i < l; i++) {
17687             item = cap[i];
17688     
17689             // Remove the list item's bullet
17690             // so it is seen as the next token.
17691             space = item.length;
17692             item = item.replace(/^ *([*+-]|\d+\.) +/, '');
17693     
17694             // Outdent whatever the
17695             // list item contains. Hacky.
17696             if (~item.indexOf('\n ')) {
17697               space -= item.length;
17698               item = !this.options.pedantic
17699                 ? item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '')
17700                 : item.replace(/^ {1,4}/gm, '');
17701             }
17702     
17703             // Determine whether the next list item belongs here.
17704             // Backpedal if it does not belong in this list.
17705             if (this.options.smartLists && i !== l - 1) {
17706               b = block.bullet.exec(cap[i + 1])[0];
17707               if (bull !== b && !(bull.length > 1 && b.length > 1)) {
17708                 src = cap.slice(i + 1).join('\n') + src;
17709                 i = l - 1;
17710               }
17711             }
17712     
17713             // Determine whether item is loose or not.
17714             // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/
17715             // for discount behavior.
17716             loose = next || /\n\n(?!\s*$)/.test(item);
17717             if (i !== l - 1) {
17718               next = item.charAt(item.length - 1) === '\n';
17719               if (!loose) { loose = next; }
17720             }
17721     
17722             this.tokens.push({
17723               type: loose
17724                 ? 'loose_item_start'
17725                 : 'list_item_start'
17726             });
17727     
17728             // Recurse.
17729             this.token(item, false, bq);
17730     
17731             this.tokens.push({
17732               type: 'list_item_end'
17733             });
17734           }
17735     
17736           this.tokens.push({
17737             type: 'list_end'
17738           });
17739     
17740           continue;
17741         }
17742     
17743         // html
17744         if (cap = this.rules.html.exec(src)) {
17745           src = src.substring(cap[0].length);
17746           this.tokens.push({
17747             type: this.options.sanitize
17748               ? 'paragraph'
17749               : 'html',
17750             pre: !this.options.sanitizer
17751               && (cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style'),
17752             text: cap[0]
17753           });
17754           continue;
17755         }
17756     
17757         // def
17758         if ((!bq && top) && (cap = this.rules.def.exec(src))) {
17759           src = src.substring(cap[0].length);
17760           this.tokens.links[cap[1].toLowerCase()] = {
17761             href: cap[2],
17762             title: cap[3]
17763           };
17764           continue;
17765         }
17766     
17767         // table (gfm)
17768         if (top && (cap = this.rules.table.exec(src))) {
17769           src = src.substring(cap[0].length);
17770     
17771           item = {
17772             type: 'table',
17773             header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
17774             align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
17775             cells: cap[3].replace(/(?: *\| *)?\n$/, '').split('\n')
17776           };
17777     
17778           for (i = 0; i < item.align.length; i++) {
17779             if (/^ *-+: *$/.test(item.align[i])) {
17780               item.align[i] = 'right';
17781             } else if (/^ *:-+: *$/.test(item.align[i])) {
17782               item.align[i] = 'center';
17783             } else if (/^ *:-+ *$/.test(item.align[i])) {
17784               item.align[i] = 'left';
17785             } else {
17786               item.align[i] = null;
17787             }
17788           }
17789     
17790           for (i = 0; i < item.cells.length; i++) {
17791             item.cells[i] = item.cells[i]
17792               .replace(/^ *\| *| *\| *$/g, '')
17793               .split(/ *\| */);
17794           }
17795     
17796           this.tokens.push(item);
17797     
17798           continue;
17799         }
17800     
17801         // top-level paragraph
17802         if (top && (cap = this.rules.paragraph.exec(src))) {
17803           src = src.substring(cap[0].length);
17804           this.tokens.push({
17805             type: 'paragraph',
17806             text: cap[1].charAt(cap[1].length - 1) === '\n'
17807               ? cap[1].slice(0, -1)
17808               : cap[1]
17809           });
17810           continue;
17811         }
17812     
17813         // text
17814         if (cap = this.rules.text.exec(src)) {
17815           // Top-level should never reach here.
17816           src = src.substring(cap[0].length);
17817           this.tokens.push({
17818             type: 'text',
17819             text: cap[0]
17820           });
17821           continue;
17822         }
17823     
17824         if (src) {
17825           throw new
17826             Error('Infinite loop on byte: ' + src.charCodeAt(0));
17827         }
17828       }
17829     
17830       return this.tokens;
17831     };
17832     
17833     /**
17834      * Inline-Level Grammar
17835      */
17836     
17837     var inline = {
17838       escape: /^\\([\\`*{}\[\]()#+\-.!_>])/,
17839       autolink: /^<([^ >]+(@|:\/)[^ >]+)>/,
17840       url: noop,
17841       tag: /^<!--[\s\S]*?-->|^<\/?\w+(?:"[^"]*"|'[^']*'|[^'">])*?>/,
17842       link: /^!?\[(inside)\]\(href\)/,
17843       reflink: /^!?\[(inside)\]\s*\[([^\]]*)\]/,
17844       nolink: /^!?\[((?:\[[^\]]*\]|[^\[\]])*)\]/,
17845       strong: /^__([\s\S]+?)__(?!_)|^\*\*([\s\S]+?)\*\*(?!\*)/,
17846       em: /^\b_((?:[^_]|__)+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/,
17847       code: /^(`+)\s*([\s\S]*?[^`])\s*\1(?!`)/,
17848       br: /^ {2,}\n(?!\s*$)/,
17849       del: noop,
17850       text: /^[\s\S]+?(?=[\\<!\[_*`]| {2,}\n|$)/
17851     };
17852     
17853     inline._inside = /(?:\[[^\]]*\]|[^\[\]]|\](?=[^\[]*\]))*/;
17854     inline._href = /\s*<?([\s\S]*?)>?(?:\s+['"]([\s\S]*?)['"])?\s*/;
17855     
17856     inline.link = replace(inline.link)
17857       ('inside', inline._inside)
17858       ('href', inline._href)
17859       ();
17860     
17861     inline.reflink = replace(inline.reflink)
17862       ('inside', inline._inside)
17863       ();
17864     
17865     /**
17866      * Normal Inline Grammar
17867      */
17868     
17869     inline.normal = merge({}, inline);
17870     
17871     /**
17872      * Pedantic Inline Grammar
17873      */
17874     
17875     inline.pedantic = merge({}, inline.normal, {
17876       strong: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,
17877       em: /^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/
17878     });
17879     
17880     /**
17881      * GFM Inline Grammar
17882      */
17883     
17884     inline.gfm = merge({}, inline.normal, {
17885       escape: replace(inline.escape)('])', '~|])')(),
17886       url: /^(https?:\/\/[^\s<]+[^<.,:;"')\]\s])/,
17887       del: /^~~(?=\S)([\s\S]*?\S)~~/,
17888       text: replace(inline.text)
17889         (']|', '~]|')
17890         ('|', '|https?://|')
17891         ()
17892     });
17893     
17894     /**
17895      * GFM + Line Breaks Inline Grammar
17896      */
17897     
17898     inline.breaks = merge({}, inline.gfm, {
17899       br: replace(inline.br)('{2,}', '*')(),
17900       text: replace(inline.gfm.text)('{2,}', '*')()
17901     });
17902     
17903     /**
17904      * Inline Lexer & Compiler
17905      */
17906     
17907     var InlineLexer  = function (links, options) {
17908       this.options = options || marked.defaults;
17909       this.links = links;
17910       this.rules = inline.normal;
17911       this.renderer = this.options.renderer || new Renderer;
17912       this.renderer.options = this.options;
17913     
17914       if (!this.links) {
17915         throw new
17916           Error('Tokens array requires a `links` property.');
17917       }
17918     
17919       if (this.options.gfm) {
17920         if (this.options.breaks) {
17921           this.rules = inline.breaks;
17922         } else {
17923           this.rules = inline.gfm;
17924         }
17925       } else if (this.options.pedantic) {
17926         this.rules = inline.pedantic;
17927       }
17928     }
17929     
17930     /**
17931      * Expose Inline Rules
17932      */
17933     
17934     InlineLexer.rules = inline;
17935     
17936     /**
17937      * Static Lexing/Compiling Method
17938      */
17939     
17940     InlineLexer.output = function(src, links, options) {
17941       var inline = new InlineLexer(links, options);
17942       return inline.output(src);
17943     };
17944     
17945     /**
17946      * Lexing/Compiling
17947      */
17948     
17949     InlineLexer.prototype.output = function(src) {
17950       var out = ''
17951         , link
17952         , text
17953         , href
17954         , cap;
17955     
17956       while (src) {
17957         // escape
17958         if (cap = this.rules.escape.exec(src)) {
17959           src = src.substring(cap[0].length);
17960           out += cap[1];
17961           continue;
17962         }
17963     
17964         // autolink
17965         if (cap = this.rules.autolink.exec(src)) {
17966           src = src.substring(cap[0].length);
17967           if (cap[2] === '@') {
17968             text = cap[1].charAt(6) === ':'
17969               ? this.mangle(cap[1].substring(7))
17970               : this.mangle(cap[1]);
17971             href = this.mangle('mailto:') + text;
17972           } else {
17973             text = escape(cap[1]);
17974             href = text;
17975           }
17976           out += this.renderer.link(href, null, text);
17977           continue;
17978         }
17979     
17980         // url (gfm)
17981         if (!this.inLink && (cap = this.rules.url.exec(src))) {
17982           src = src.substring(cap[0].length);
17983           text = escape(cap[1]);
17984           href = text;
17985           out += this.renderer.link(href, null, text);
17986           continue;
17987         }
17988     
17989         // tag
17990         if (cap = this.rules.tag.exec(src)) {
17991           if (!this.inLink && /^<a /i.test(cap[0])) {
17992             this.inLink = true;
17993           } else if (this.inLink && /^<\/a>/i.test(cap[0])) {
17994             this.inLink = false;
17995           }
17996           src = src.substring(cap[0].length);
17997           out += this.options.sanitize
17998             ? this.options.sanitizer
17999               ? this.options.sanitizer(cap[0])
18000               : escape(cap[0])
18001             : cap[0];
18002           continue;
18003         }
18004     
18005         // link
18006         if (cap = this.rules.link.exec(src)) {
18007           src = src.substring(cap[0].length);
18008           this.inLink = true;
18009           out += this.outputLink(cap, {
18010             href: cap[2],
18011             title: cap[3]
18012           });
18013           this.inLink = false;
18014           continue;
18015         }
18016     
18017         // reflink, nolink
18018         if ((cap = this.rules.reflink.exec(src))
18019             || (cap = this.rules.nolink.exec(src))) {
18020           src = src.substring(cap[0].length);
18021           link = (cap[2] || cap[1]).replace(/\s+/g, ' ');
18022           link = this.links[link.toLowerCase()];
18023           if (!link || !link.href) {
18024             out += cap[0].charAt(0);
18025             src = cap[0].substring(1) + src;
18026             continue;
18027           }
18028           this.inLink = true;
18029           out += this.outputLink(cap, link);
18030           this.inLink = false;
18031           continue;
18032         }
18033     
18034         // strong
18035         if (cap = this.rules.strong.exec(src)) {
18036           src = src.substring(cap[0].length);
18037           out += this.renderer.strong(this.output(cap[2] || cap[1]));
18038           continue;
18039         }
18040     
18041         // em
18042         if (cap = this.rules.em.exec(src)) {
18043           src = src.substring(cap[0].length);
18044           out += this.renderer.em(this.output(cap[2] || cap[1]));
18045           continue;
18046         }
18047     
18048         // code
18049         if (cap = this.rules.code.exec(src)) {
18050           src = src.substring(cap[0].length);
18051           out += this.renderer.codespan(escape(cap[2], true));
18052           continue;
18053         }
18054     
18055         // br
18056         if (cap = this.rules.br.exec(src)) {
18057           src = src.substring(cap[0].length);
18058           out += this.renderer.br();
18059           continue;
18060         }
18061     
18062         // del (gfm)
18063         if (cap = this.rules.del.exec(src)) {
18064           src = src.substring(cap[0].length);
18065           out += this.renderer.del(this.output(cap[1]));
18066           continue;
18067         }
18068     
18069         // text
18070         if (cap = this.rules.text.exec(src)) {
18071           src = src.substring(cap[0].length);
18072           out += this.renderer.text(escape(this.smartypants(cap[0])));
18073           continue;
18074         }
18075     
18076         if (src) {
18077           throw new
18078             Error('Infinite loop on byte: ' + src.charCodeAt(0));
18079         }
18080       }
18081     
18082       return out;
18083     };
18084     
18085     /**
18086      * Compile Link
18087      */
18088     
18089     InlineLexer.prototype.outputLink = function(cap, link) {
18090       var href = escape(link.href)
18091         , title = link.title ? escape(link.title) : null;
18092     
18093       return cap[0].charAt(0) !== '!'
18094         ? this.renderer.link(href, title, this.output(cap[1]))
18095         : this.renderer.image(href, title, escape(cap[1]));
18096     };
18097     
18098     /**
18099      * Smartypants Transformations
18100      */
18101     
18102     InlineLexer.prototype.smartypants = function(text) {
18103       if (!this.options.smartypants)  { return text; }
18104       return text
18105         // em-dashes
18106         .replace(/---/g, '\u2014')
18107         // en-dashes
18108         .replace(/--/g, '\u2013')
18109         // opening singles
18110         .replace(/(^|[-\u2014/(\[{"\s])'/g, '$1\u2018')
18111         // closing singles & apostrophes
18112         .replace(/'/g, '\u2019')
18113         // opening doubles
18114         .replace(/(^|[-\u2014/(\[{\u2018\s])"/g, '$1\u201c')
18115         // closing doubles
18116         .replace(/"/g, '\u201d')
18117         // ellipses
18118         .replace(/\.{3}/g, '\u2026');
18119     };
18120     
18121     /**
18122      * Mangle Links
18123      */
18124     
18125     InlineLexer.prototype.mangle = function(text) {
18126       if (!this.options.mangle) { return text; }
18127       var out = ''
18128         , l = text.length
18129         , i = 0
18130         , ch;
18131     
18132       for (; i < l; i++) {
18133         ch = text.charCodeAt(i);
18134         if (Math.random() > 0.5) {
18135           ch = 'x' + ch.toString(16);
18136         }
18137         out += '&#' + ch + ';';
18138       }
18139     
18140       return out;
18141     };
18142     
18143     /**
18144      * Renderer
18145      */
18146     
18147      /**
18148          * eval:var:Renderer
18149     */
18150     
18151     var Renderer   = function (options) {
18152       this.options = options || {};
18153     }
18154     
18155     Renderer.prototype.code = function(code, lang, escaped) {
18156       if (this.options.highlight) {
18157         var out = this.options.highlight(code, lang);
18158         if (out != null && out !== code) {
18159           escaped = true;
18160           code = out;
18161         }
18162       } else {
18163             // hack!!! - it's already escapeD?
18164             escaped = true;
18165       }
18166     
18167       if (!lang) {
18168         return '<pre><code>'
18169           + (escaped ? code : escape(code, true))
18170           + '\n</code></pre>';
18171       }
18172     
18173       return '<pre><code class="'
18174         + this.options.langPrefix
18175         + escape(lang, true)
18176         + '">'
18177         + (escaped ? code : escape(code, true))
18178         + '\n</code></pre>\n';
18179     };
18180     
18181     Renderer.prototype.blockquote = function(quote) {
18182       return '<blockquote>\n' + quote + '</blockquote>\n';
18183     };
18184     
18185     Renderer.prototype.html = function(html) {
18186       return html;
18187     };
18188     
18189     Renderer.prototype.heading = function(text, level, raw) {
18190       return '<h'
18191         + level
18192         + ' id="'
18193         + this.options.headerPrefix
18194         + raw.toLowerCase().replace(/[^\w]+/g, '-')
18195         + '">'
18196         + text
18197         + '</h'
18198         + level
18199         + '>\n';
18200     };
18201     
18202     Renderer.prototype.hr = function() {
18203       return this.options.xhtml ? '<hr/>\n' : '<hr>\n';
18204     };
18205     
18206     Renderer.prototype.list = function(body, ordered) {
18207       var type = ordered ? 'ol' : 'ul';
18208       return '<' + type + '>\n' + body + '</' + type + '>\n';
18209     };
18210     
18211     Renderer.prototype.listitem = function(text) {
18212       return '<li>' + text + '</li>\n';
18213     };
18214     
18215     Renderer.prototype.paragraph = function(text) {
18216       return '<p>' + text + '</p>\n';
18217     };
18218     
18219     Renderer.prototype.table = function(header, body) {
18220       return '<table class="table table-striped">\n'
18221         + '<thead>\n'
18222         + header
18223         + '</thead>\n'
18224         + '<tbody>\n'
18225         + body
18226         + '</tbody>\n'
18227         + '</table>\n';
18228     };
18229     
18230     Renderer.prototype.tablerow = function(content) {
18231       return '<tr>\n' + content + '</tr>\n';
18232     };
18233     
18234     Renderer.prototype.tablecell = function(content, flags) {
18235       var type = flags.header ? 'th' : 'td';
18236       var tag = flags.align
18237         ? '<' + type + ' style="text-align:' + flags.align + '">'
18238         : '<' + type + '>';
18239       return tag + content + '</' + type + '>\n';
18240     };
18241     
18242     // span level renderer
18243     Renderer.prototype.strong = function(text) {
18244       return '<strong>' + text + '</strong>';
18245     };
18246     
18247     Renderer.prototype.em = function(text) {
18248       return '<em>' + text + '</em>';
18249     };
18250     
18251     Renderer.prototype.codespan = function(text) {
18252       return '<code>' + text + '</code>';
18253     };
18254     
18255     Renderer.prototype.br = function() {
18256       return this.options.xhtml ? '<br/>' : '<br>';
18257     };
18258     
18259     Renderer.prototype.del = function(text) {
18260       return '<del>' + text + '</del>';
18261     };
18262     
18263     Renderer.prototype.link = function(href, title, text) {
18264       if (this.options.sanitize) {
18265         try {
18266           var prot = decodeURIComponent(unescape(href))
18267             .replace(/[^\w:]/g, '')
18268             .toLowerCase();
18269         } catch (e) {
18270           return '';
18271         }
18272         if (prot.indexOf('javascript:') === 0 || prot.indexOf('vbscript:') === 0) {
18273           return '';
18274         }
18275       }
18276       var out = '<a href="' + href + '"';
18277       if (title) {
18278         out += ' title="' + title + '"';
18279       }
18280       out += '>' + text + '</a>';
18281       return out;
18282     };
18283     
18284     Renderer.prototype.image = function(href, title, text) {
18285       var out = '<img src="' + href + '" alt="' + text + '"';
18286       if (title) {
18287         out += ' title="' + title + '"';
18288       }
18289       out += this.options.xhtml ? '/>' : '>';
18290       return out;
18291     };
18292     
18293     Renderer.prototype.text = function(text) {
18294       return text;
18295     };
18296     
18297     /**
18298      * Parsing & Compiling
18299      */
18300          /**
18301          * eval:var:Parser
18302     */
18303     
18304     var Parser= function (options) {
18305       this.tokens = [];
18306       this.token = null;
18307       this.options = options || marked.defaults;
18308       this.options.renderer = this.options.renderer || new Renderer;
18309       this.renderer = this.options.renderer;
18310       this.renderer.options = this.options;
18311     }
18312     
18313     /**
18314      * Static Parse Method
18315      */
18316     
18317     Parser.parse = function(src, options, renderer) {
18318       var parser = new Parser(options, renderer);
18319       return parser.parse(src);
18320     };
18321     
18322     /**
18323      * Parse Loop
18324      */
18325     
18326     Parser.prototype.parse = function(src) {
18327       this.inline = new InlineLexer(src.links, this.options, this.renderer);
18328       this.tokens = src.reverse();
18329     
18330       var out = '';
18331       while (this.next()) {
18332         out += this.tok();
18333       }
18334     
18335       return out;
18336     };
18337     
18338     /**
18339      * Next Token
18340      */
18341     
18342     Parser.prototype.next = function() {
18343       return this.token = this.tokens.pop();
18344     };
18345     
18346     /**
18347      * Preview Next Token
18348      */
18349     
18350     Parser.prototype.peek = function() {
18351       return this.tokens[this.tokens.length - 1] || 0;
18352     };
18353     
18354     /**
18355      * Parse Text Tokens
18356      */
18357     
18358     Parser.prototype.parseText = function() {
18359       var body = this.token.text;
18360     
18361       while (this.peek().type === 'text') {
18362         body += '\n' + this.next().text;
18363       }
18364     
18365       return this.inline.output(body);
18366     };
18367     
18368     /**
18369      * Parse Current Token
18370      */
18371     
18372     Parser.prototype.tok = function() {
18373       switch (this.token.type) {
18374         case 'space': {
18375           return '';
18376         }
18377         case 'hr': {
18378           return this.renderer.hr();
18379         }
18380         case 'heading': {
18381           return this.renderer.heading(
18382             this.inline.output(this.token.text),
18383             this.token.depth,
18384             this.token.text);
18385         }
18386         case 'code': {
18387           return this.renderer.code(this.token.text,
18388             this.token.lang,
18389             this.token.escaped);
18390         }
18391         case 'table': {
18392           var header = ''
18393             , body = ''
18394             , i
18395             , row
18396             , cell
18397             , flags
18398             , j;
18399     
18400           // header
18401           cell = '';
18402           for (i = 0; i < this.token.header.length; i++) {
18403             flags = { header: true, align: this.token.align[i] };
18404             cell += this.renderer.tablecell(
18405               this.inline.output(this.token.header[i]),
18406               { header: true, align: this.token.align[i] }
18407             );
18408           }
18409           header += this.renderer.tablerow(cell);
18410     
18411           for (i = 0; i < this.token.cells.length; i++) {
18412             row = this.token.cells[i];
18413     
18414             cell = '';
18415             for (j = 0; j < row.length; j++) {
18416               cell += this.renderer.tablecell(
18417                 this.inline.output(row[j]),
18418                 { header: false, align: this.token.align[j] }
18419               );
18420             }
18421     
18422             body += this.renderer.tablerow(cell);
18423           }
18424           return this.renderer.table(header, body);
18425         }
18426         case 'blockquote_start': {
18427           var body = '';
18428     
18429           while (this.next().type !== 'blockquote_end') {
18430             body += this.tok();
18431           }
18432     
18433           return this.renderer.blockquote(body);
18434         }
18435         case 'list_start': {
18436           var body = ''
18437             , ordered = this.token.ordered;
18438     
18439           while (this.next().type !== 'list_end') {
18440             body += this.tok();
18441           }
18442     
18443           return this.renderer.list(body, ordered);
18444         }
18445         case 'list_item_start': {
18446           var body = '';
18447     
18448           while (this.next().type !== 'list_item_end') {
18449             body += this.token.type === 'text'
18450               ? this.parseText()
18451               : this.tok();
18452           }
18453     
18454           return this.renderer.listitem(body);
18455         }
18456         case 'loose_item_start': {
18457           var body = '';
18458     
18459           while (this.next().type !== 'list_item_end') {
18460             body += this.tok();
18461           }
18462     
18463           return this.renderer.listitem(body);
18464         }
18465         case 'html': {
18466           var html = !this.token.pre && !this.options.pedantic
18467             ? this.inline.output(this.token.text)
18468             : this.token.text;
18469           return this.renderer.html(html);
18470         }
18471         case 'paragraph': {
18472           return this.renderer.paragraph(this.inline.output(this.token.text));
18473         }
18474         case 'text': {
18475           return this.renderer.paragraph(this.parseText());
18476         }
18477       }
18478     };
18479   
18480     
18481     /**
18482      * Marked
18483      */
18484          /**
18485          * eval:var:marked
18486     */
18487     var marked = function (src, opt, callback) {
18488       if (callback || typeof opt === 'function') {
18489         if (!callback) {
18490           callback = opt;
18491           opt = null;
18492         }
18493     
18494         opt = merge({}, marked.defaults, opt || {});
18495     
18496         var highlight = opt.highlight
18497           , tokens
18498           , pending
18499           , i = 0;
18500     
18501         try {
18502           tokens = Lexer.lex(src, opt)
18503         } catch (e) {
18504           return callback(e);
18505         }
18506     
18507         pending = tokens.length;
18508          /**
18509          * eval:var:done
18510     */
18511         var done = function(err) {
18512           if (err) {
18513             opt.highlight = highlight;
18514             return callback(err);
18515           }
18516     
18517           var out;
18518     
18519           try {
18520             out = Parser.parse(tokens, opt);
18521           } catch (e) {
18522             err = e;
18523           }
18524     
18525           opt.highlight = highlight;
18526     
18527           return err
18528             ? callback(err)
18529             : callback(null, out);
18530         };
18531     
18532         if (!highlight || highlight.length < 3) {
18533           return done();
18534         }
18535     
18536         delete opt.highlight;
18537     
18538         if (!pending) { return done(); }
18539     
18540         for (; i < tokens.length; i++) {
18541           (function(token) {
18542             if (token.type !== 'code') {
18543               return --pending || done();
18544             }
18545             return highlight(token.text, token.lang, function(err, code) {
18546               if (err) { return done(err); }
18547               if (code == null || code === token.text) {
18548                 return --pending || done();
18549               }
18550               token.text = code;
18551               token.escaped = true;
18552               --pending || done();
18553             });
18554           })(tokens[i]);
18555         }
18556     
18557         return;
18558       }
18559       try {
18560         if (opt) { opt = merge({}, marked.defaults, opt); }
18561         return Parser.parse(Lexer.lex(src, opt), opt);
18562       } catch (e) {
18563         e.message += '\nPlease report this to https://github.com/chjj/marked.';
18564         if ((opt || marked.defaults).silent) {
18565           return '<p>An error occured:</p><pre>'
18566             + escape(e.message + '', true)
18567             + '</pre>';
18568         }
18569         throw e;
18570       }
18571     }
18572     
18573     /**
18574      * Options
18575      */
18576     
18577     marked.options =
18578     marked.setOptions = function(opt) {
18579       merge(marked.defaults, opt);
18580       return marked;
18581     };
18582     
18583     marked.defaults = {
18584       gfm: true,
18585       tables: true,
18586       breaks: false,
18587       pedantic: false,
18588       sanitize: false,
18589       sanitizer: null,
18590       mangle: true,
18591       smartLists: false,
18592       silent: false,
18593       highlight: null,
18594       langPrefix: 'lang-',
18595       smartypants: false,
18596       headerPrefix: '',
18597       renderer: new Renderer,
18598       xhtml: false
18599     };
18600     
18601     /**
18602      * Expose
18603      */
18604     
18605     marked.Parser = Parser;
18606     marked.parser = Parser.parse;
18607     
18608     marked.Renderer = Renderer;
18609     
18610     marked.Lexer = Lexer;
18611     marked.lexer = Lexer.lex;
18612     
18613     marked.InlineLexer = InlineLexer;
18614     marked.inlineLexer = InlineLexer.output;
18615     
18616     marked.parse = marked;
18617     
18618     Roo.Markdown.marked = marked;
18619
18620 })();/*
18621  * Based on:
18622  * Ext JS Library 1.1.1
18623  * Copyright(c) 2006-2007, Ext JS, LLC.
18624  *
18625  * Originally Released Under LGPL - original licence link has changed is not relivant.
18626  *
18627  * Fork - LGPL
18628  * <script type="text/javascript">
18629  */
18630
18631
18632
18633 /*
18634  * These classes are derivatives of the similarly named classes in the YUI Library.
18635  * The original license:
18636  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
18637  * Code licensed under the BSD License:
18638  * http://developer.yahoo.net/yui/license.txt
18639  */
18640
18641 (function() {
18642
18643 var Event=Roo.EventManager;
18644 var Dom=Roo.lib.Dom;
18645
18646 /**
18647  * @class Roo.dd.DragDrop
18648  * @extends Roo.util.Observable
18649  * Defines the interface and base operation of items that that can be
18650  * dragged or can be drop targets.  It was designed to be extended, overriding
18651  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
18652  * Up to three html elements can be associated with a DragDrop instance:
18653  * <ul>
18654  * <li>linked element: the element that is passed into the constructor.
18655  * This is the element which defines the boundaries for interaction with
18656  * other DragDrop objects.</li>
18657  * <li>handle element(s): The drag operation only occurs if the element that
18658  * was clicked matches a handle element.  By default this is the linked
18659  * element, but there are times that you will want only a portion of the
18660  * linked element to initiate the drag operation, and the setHandleElId()
18661  * method provides a way to define this.</li>
18662  * <li>drag element: this represents the element that would be moved along
18663  * with the cursor during a drag operation.  By default, this is the linked
18664  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
18665  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
18666  * </li>
18667  * </ul>
18668  * This class should not be instantiated until the onload event to ensure that
18669  * the associated elements are available.
18670  * The following would define a DragDrop obj that would interact with any
18671  * other DragDrop obj in the "group1" group:
18672  * <pre>
18673  *  dd = new Roo.dd.DragDrop("div1", "group1");
18674  * </pre>
18675  * Since none of the event handlers have been implemented, nothing would
18676  * actually happen if you were to run the code above.  Normally you would
18677  * override this class or one of the default implementations, but you can
18678  * also override the methods you want on an instance of the class...
18679  * <pre>
18680  *  dd.onDragDrop = function(e, id) {
18681  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
18682  *  }
18683  * </pre>
18684  * @constructor
18685  * @param {String} id of the element that is linked to this instance
18686  * @param {String} sGroup the group of related DragDrop objects
18687  * @param {object} config an object containing configurable attributes
18688  *                Valid properties for DragDrop:
18689  *                    padding, isTarget, maintainOffset, primaryButtonOnly
18690  */
18691 Roo.dd.DragDrop = function(id, sGroup, config) {
18692     if (id) {
18693         this.init(id, sGroup, config);
18694     }
18695     
18696 };
18697
18698 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
18699
18700     /**
18701      * The id of the element associated with this object.  This is what we
18702      * refer to as the "linked element" because the size and position of
18703      * this element is used to determine when the drag and drop objects have
18704      * interacted.
18705      * @property id
18706      * @type String
18707      */
18708     id: null,
18709
18710     /**
18711      * Configuration attributes passed into the constructor
18712      * @property config
18713      * @type object
18714      */
18715     config: null,
18716
18717     /**
18718      * The id of the element that will be dragged.  By default this is same
18719      * as the linked element , but could be changed to another element. Ex:
18720      * Roo.dd.DDProxy
18721      * @property dragElId
18722      * @type String
18723      * @private
18724      */
18725     dragElId: null,
18726
18727     /**
18728      * the id of the element that initiates the drag operation.  By default
18729      * this is the linked element, but could be changed to be a child of this
18730      * element.  This lets us do things like only starting the drag when the
18731      * header element within the linked html element is clicked.
18732      * @property handleElId
18733      * @type String
18734      * @private
18735      */
18736     handleElId: null,
18737
18738     /**
18739      * An associative array of HTML tags that will be ignored if clicked.
18740      * @property invalidHandleTypes
18741      * @type {string: string}
18742      */
18743     invalidHandleTypes: null,
18744
18745     /**
18746      * An associative array of ids for elements that will be ignored if clicked
18747      * @property invalidHandleIds
18748      * @type {string: string}
18749      */
18750     invalidHandleIds: null,
18751
18752     /**
18753      * An indexted array of css class names for elements that will be ignored
18754      * if clicked.
18755      * @property invalidHandleClasses
18756      * @type string[]
18757      */
18758     invalidHandleClasses: null,
18759
18760     /**
18761      * The linked element's absolute X position at the time the drag was
18762      * started
18763      * @property startPageX
18764      * @type int
18765      * @private
18766      */
18767     startPageX: 0,
18768
18769     /**
18770      * The linked element's absolute X position at the time the drag was
18771      * started
18772      * @property startPageY
18773      * @type int
18774      * @private
18775      */
18776     startPageY: 0,
18777
18778     /**
18779      * The group defines a logical collection of DragDrop objects that are
18780      * related.  Instances only get events when interacting with other
18781      * DragDrop object in the same group.  This lets us define multiple
18782      * groups using a single DragDrop subclass if we want.
18783      * @property groups
18784      * @type {string: string}
18785      */
18786     groups: null,
18787
18788     /**
18789      * Individual drag/drop instances can be locked.  This will prevent
18790      * onmousedown start drag.
18791      * @property locked
18792      * @type boolean
18793      * @private
18794      */
18795     locked: false,
18796
18797     /**
18798      * Lock this instance
18799      * @method lock
18800      */
18801     lock: function() { this.locked = true; },
18802
18803     /**
18804      * Unlock this instace
18805      * @method unlock
18806      */
18807     unlock: function() { this.locked = false; },
18808
18809     /**
18810      * By default, all insances can be a drop target.  This can be disabled by
18811      * setting isTarget to false.
18812      * @method isTarget
18813      * @type boolean
18814      */
18815     isTarget: true,
18816
18817     /**
18818      * The padding configured for this drag and drop object for calculating
18819      * the drop zone intersection with this object.
18820      * @method padding
18821      * @type int[]
18822      */
18823     padding: null,
18824
18825     /**
18826      * Cached reference to the linked element
18827      * @property _domRef
18828      * @private
18829      */
18830     _domRef: null,
18831
18832     /**
18833      * Internal typeof flag
18834      * @property __ygDragDrop
18835      * @private
18836      */
18837     __ygDragDrop: true,
18838
18839     /**
18840      * Set to true when horizontal contraints are applied
18841      * @property constrainX
18842      * @type boolean
18843      * @private
18844      */
18845     constrainX: false,
18846
18847     /**
18848      * Set to true when vertical contraints are applied
18849      * @property constrainY
18850      * @type boolean
18851      * @private
18852      */
18853     constrainY: false,
18854
18855     /**
18856      * The left constraint
18857      * @property minX
18858      * @type int
18859      * @private
18860      */
18861     minX: 0,
18862
18863     /**
18864      * The right constraint
18865      * @property maxX
18866      * @type int
18867      * @private
18868      */
18869     maxX: 0,
18870
18871     /**
18872      * The up constraint
18873      * @property minY
18874      * @type int
18875      * @type int
18876      * @private
18877      */
18878     minY: 0,
18879
18880     /**
18881      * The down constraint
18882      * @property maxY
18883      * @type int
18884      * @private
18885      */
18886     maxY: 0,
18887
18888     /**
18889      * Maintain offsets when we resetconstraints.  Set to true when you want
18890      * the position of the element relative to its parent to stay the same
18891      * when the page changes
18892      *
18893      * @property maintainOffset
18894      * @type boolean
18895      */
18896     maintainOffset: false,
18897
18898     /**
18899      * Array of pixel locations the element will snap to if we specified a
18900      * horizontal graduation/interval.  This array is generated automatically
18901      * when you define a tick interval.
18902      * @property xTicks
18903      * @type int[]
18904      */
18905     xTicks: null,
18906
18907     /**
18908      * Array of pixel locations the element will snap to if we specified a
18909      * vertical graduation/interval.  This array is generated automatically
18910      * when you define a tick interval.
18911      * @property yTicks
18912      * @type int[]
18913      */
18914     yTicks: null,
18915
18916     /**
18917      * By default the drag and drop instance will only respond to the primary
18918      * button click (left button for a right-handed mouse).  Set to true to
18919      * allow drag and drop to start with any mouse click that is propogated
18920      * by the browser
18921      * @property primaryButtonOnly
18922      * @type boolean
18923      */
18924     primaryButtonOnly: true,
18925
18926     /**
18927      * The availabe property is false until the linked dom element is accessible.
18928      * @property available
18929      * @type boolean
18930      */
18931     available: false,
18932
18933     /**
18934      * By default, drags can only be initiated if the mousedown occurs in the
18935      * region the linked element is.  This is done in part to work around a
18936      * bug in some browsers that mis-report the mousedown if the previous
18937      * mouseup happened outside of the window.  This property is set to true
18938      * if outer handles are defined.
18939      *
18940      * @property hasOuterHandles
18941      * @type boolean
18942      * @default false
18943      */
18944     hasOuterHandles: false,
18945
18946     /**
18947      * Code that executes immediately before the startDrag event
18948      * @method b4StartDrag
18949      * @private
18950      */
18951     b4StartDrag: function(x, y) { },
18952
18953     /**
18954      * Abstract method called after a drag/drop object is clicked
18955      * and the drag or mousedown time thresholds have beeen met.
18956      * @method startDrag
18957      * @param {int} X click location
18958      * @param {int} Y click location
18959      */
18960     startDrag: function(x, y) { /* override this */ },
18961
18962     /**
18963      * Code that executes immediately before the onDrag event
18964      * @method b4Drag
18965      * @private
18966      */
18967     b4Drag: function(e) { },
18968
18969     /**
18970      * Abstract method called during the onMouseMove event while dragging an
18971      * object.
18972      * @method onDrag
18973      * @param {Event} e the mousemove event
18974      */
18975     onDrag: function(e) { /* override this */ },
18976
18977     /**
18978      * Abstract method called when this element fist begins hovering over
18979      * another DragDrop obj
18980      * @method onDragEnter
18981      * @param {Event} e the mousemove event
18982      * @param {String|DragDrop[]} id In POINT mode, the element
18983      * id this is hovering over.  In INTERSECT mode, an array of one or more
18984      * dragdrop items being hovered over.
18985      */
18986     onDragEnter: function(e, id) { /* override this */ },
18987
18988     /**
18989      * Code that executes immediately before the onDragOver event
18990      * @method b4DragOver
18991      * @private
18992      */
18993     b4DragOver: function(e) { },
18994
18995     /**
18996      * Abstract method called when this element is hovering over another
18997      * DragDrop obj
18998      * @method onDragOver
18999      * @param {Event} e the mousemove event
19000      * @param {String|DragDrop[]} id In POINT mode, the element
19001      * id this is hovering over.  In INTERSECT mode, an array of dd items
19002      * being hovered over.
19003      */
19004     onDragOver: function(e, id) { /* override this */ },
19005
19006     /**
19007      * Code that executes immediately before the onDragOut event
19008      * @method b4DragOut
19009      * @private
19010      */
19011     b4DragOut: function(e) { },
19012
19013     /**
19014      * Abstract method called when we are no longer hovering over an element
19015      * @method onDragOut
19016      * @param {Event} e the mousemove event
19017      * @param {String|DragDrop[]} id In POINT mode, the element
19018      * id this was hovering over.  In INTERSECT mode, an array of dd items
19019      * that the mouse is no longer over.
19020      */
19021     onDragOut: function(e, id) { /* override this */ },
19022
19023     /**
19024      * Code that executes immediately before the onDragDrop event
19025      * @method b4DragDrop
19026      * @private
19027      */
19028     b4DragDrop: function(e) { },
19029
19030     /**
19031      * Abstract method called when this item is dropped on another DragDrop
19032      * obj
19033      * @method onDragDrop
19034      * @param {Event} e the mouseup event
19035      * @param {String|DragDrop[]} id In POINT mode, the element
19036      * id this was dropped on.  In INTERSECT mode, an array of dd items this
19037      * was dropped on.
19038      */
19039     onDragDrop: function(e, id) { /* override this */ },
19040
19041     /**
19042      * Abstract method called when this item is dropped on an area with no
19043      * drop target
19044      * @method onInvalidDrop
19045      * @param {Event} e the mouseup event
19046      */
19047     onInvalidDrop: function(e) { /* override this */ },
19048
19049     /**
19050      * Code that executes immediately before the endDrag event
19051      * @method b4EndDrag
19052      * @private
19053      */
19054     b4EndDrag: function(e) { },
19055
19056     /**
19057      * Fired when we are done dragging the object
19058      * @method endDrag
19059      * @param {Event} e the mouseup event
19060      */
19061     endDrag: function(e) { /* override this */ },
19062
19063     /**
19064      * Code executed immediately before the onMouseDown event
19065      * @method b4MouseDown
19066      * @param {Event} e the mousedown event
19067      * @private
19068      */
19069     b4MouseDown: function(e) {  },
19070
19071     /**
19072      * Event handler that fires when a drag/drop obj gets a mousedown
19073      * @method onMouseDown
19074      * @param {Event} e the mousedown event
19075      */
19076     onMouseDown: function(e) { /* override this */ },
19077
19078     /**
19079      * Event handler that fires when a drag/drop obj gets a mouseup
19080      * @method onMouseUp
19081      * @param {Event} e the mouseup event
19082      */
19083     onMouseUp: function(e) { /* override this */ },
19084
19085     /**
19086      * Override the onAvailable method to do what is needed after the initial
19087      * position was determined.
19088      * @method onAvailable
19089      */
19090     onAvailable: function () {
19091     },
19092
19093     /*
19094      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
19095      * @type Object
19096      */
19097     defaultPadding : {left:0, right:0, top:0, bottom:0},
19098
19099     /*
19100      * Initializes the drag drop object's constraints to restrict movement to a certain element.
19101  *
19102  * Usage:
19103  <pre><code>
19104  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
19105                 { dragElId: "existingProxyDiv" });
19106  dd.startDrag = function(){
19107      this.constrainTo("parent-id");
19108  };
19109  </code></pre>
19110  * Or you can initalize it using the {@link Roo.Element} object:
19111  <pre><code>
19112  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
19113      startDrag : function(){
19114          this.constrainTo("parent-id");
19115      }
19116  });
19117  </code></pre>
19118      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
19119      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
19120      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
19121      * an object containing the sides to pad. For example: {right:10, bottom:10}
19122      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
19123      */
19124     constrainTo : function(constrainTo, pad, inContent){
19125         if(typeof pad == "number"){
19126             pad = {left: pad, right:pad, top:pad, bottom:pad};
19127         }
19128         pad = pad || this.defaultPadding;
19129         var b = Roo.get(this.getEl()).getBox();
19130         var ce = Roo.get(constrainTo);
19131         var s = ce.getScroll();
19132         var c, cd = ce.dom;
19133         if(cd == document.body){
19134             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
19135         }else{
19136             xy = ce.getXY();
19137             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
19138         }
19139
19140
19141         var topSpace = b.y - c.y;
19142         var leftSpace = b.x - c.x;
19143
19144         this.resetConstraints();
19145         this.setXConstraint(leftSpace - (pad.left||0), // left
19146                 c.width - leftSpace - b.width - (pad.right||0) //right
19147         );
19148         this.setYConstraint(topSpace - (pad.top||0), //top
19149                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
19150         );
19151     },
19152
19153     /**
19154      * Returns a reference to the linked element
19155      * @method getEl
19156      * @return {HTMLElement} the html element
19157      */
19158     getEl: function() {
19159         if (!this._domRef) {
19160             this._domRef = Roo.getDom(this.id);
19161         }
19162
19163         return this._domRef;
19164     },
19165
19166     /**
19167      * Returns a reference to the actual element to drag.  By default this is
19168      * the same as the html element, but it can be assigned to another
19169      * element. An example of this can be found in Roo.dd.DDProxy
19170      * @method getDragEl
19171      * @return {HTMLElement} the html element
19172      */
19173     getDragEl: function() {
19174         return Roo.getDom(this.dragElId);
19175     },
19176
19177     /**
19178      * Sets up the DragDrop object.  Must be called in the constructor of any
19179      * Roo.dd.DragDrop subclass
19180      * @method init
19181      * @param id the id of the linked element
19182      * @param {String} sGroup the group of related items
19183      * @param {object} config configuration attributes
19184      */
19185     init: function(id, sGroup, config) {
19186         this.initTarget(id, sGroup, config);
19187         if (!Roo.isTouch) {
19188             Event.on(this.id, "mousedown", this.handleMouseDown, this);
19189         }
19190         Event.on(this.id, "touchstart", this.handleMouseDown, this);
19191         // Event.on(this.id, "selectstart", Event.preventDefault);
19192     },
19193
19194     /**
19195      * Initializes Targeting functionality only... the object does not
19196      * get a mousedown handler.
19197      * @method initTarget
19198      * @param id the id of the linked element
19199      * @param {String} sGroup the group of related items
19200      * @param {object} config configuration attributes
19201      */
19202     initTarget: function(id, sGroup, config) {
19203
19204         // configuration attributes
19205         this.config = config || {};
19206
19207         // create a local reference to the drag and drop manager
19208         this.DDM = Roo.dd.DDM;
19209         // initialize the groups array
19210         this.groups = {};
19211
19212         // assume that we have an element reference instead of an id if the
19213         // parameter is not a string
19214         if (typeof id !== "string") {
19215             id = Roo.id(id);
19216         }
19217
19218         // set the id
19219         this.id = id;
19220
19221         // add to an interaction group
19222         this.addToGroup((sGroup) ? sGroup : "default");
19223
19224         // We don't want to register this as the handle with the manager
19225         // so we just set the id rather than calling the setter.
19226         this.handleElId = id;
19227
19228         // the linked element is the element that gets dragged by default
19229         this.setDragElId(id);
19230
19231         // by default, clicked anchors will not start drag operations.
19232         this.invalidHandleTypes = { A: "A" };
19233         this.invalidHandleIds = {};
19234         this.invalidHandleClasses = [];
19235
19236         this.applyConfig();
19237
19238         this.handleOnAvailable();
19239     },
19240
19241     /**
19242      * Applies the configuration parameters that were passed into the constructor.
19243      * This is supposed to happen at each level through the inheritance chain.  So
19244      * a DDProxy implentation will execute apply config on DDProxy, DD, and
19245      * DragDrop in order to get all of the parameters that are available in
19246      * each object.
19247      * @method applyConfig
19248      */
19249     applyConfig: function() {
19250
19251         // configurable properties:
19252         //    padding, isTarget, maintainOffset, primaryButtonOnly
19253         this.padding           = this.config.padding || [0, 0, 0, 0];
19254         this.isTarget          = (this.config.isTarget !== false);
19255         this.maintainOffset    = (this.config.maintainOffset);
19256         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
19257
19258     },
19259
19260     /**
19261      * Executed when the linked element is available
19262      * @method handleOnAvailable
19263      * @private
19264      */
19265     handleOnAvailable: function() {
19266         this.available = true;
19267         this.resetConstraints();
19268         this.onAvailable();
19269     },
19270
19271      /**
19272      * Configures the padding for the target zone in px.  Effectively expands
19273      * (or reduces) the virtual object size for targeting calculations.
19274      * Supports css-style shorthand; if only one parameter is passed, all sides
19275      * will have that padding, and if only two are passed, the top and bottom
19276      * will have the first param, the left and right the second.
19277      * @method setPadding
19278      * @param {int} iTop    Top pad
19279      * @param {int} iRight  Right pad
19280      * @param {int} iBot    Bot pad
19281      * @param {int} iLeft   Left pad
19282      */
19283     setPadding: function(iTop, iRight, iBot, iLeft) {
19284         // this.padding = [iLeft, iRight, iTop, iBot];
19285         if (!iRight && 0 !== iRight) {
19286             this.padding = [iTop, iTop, iTop, iTop];
19287         } else if (!iBot && 0 !== iBot) {
19288             this.padding = [iTop, iRight, iTop, iRight];
19289         } else {
19290             this.padding = [iTop, iRight, iBot, iLeft];
19291         }
19292     },
19293
19294     /**
19295      * Stores the initial placement of the linked element.
19296      * @method setInitialPosition
19297      * @param {int} diffX   the X offset, default 0
19298      * @param {int} diffY   the Y offset, default 0
19299      */
19300     setInitPosition: function(diffX, diffY) {
19301         var el = this.getEl();
19302
19303         if (!this.DDM.verifyEl(el)) {
19304             return;
19305         }
19306
19307         var dx = diffX || 0;
19308         var dy = diffY || 0;
19309
19310         var p = Dom.getXY( el );
19311
19312         this.initPageX = p[0] - dx;
19313         this.initPageY = p[1] - dy;
19314
19315         this.lastPageX = p[0];
19316         this.lastPageY = p[1];
19317
19318
19319         this.setStartPosition(p);
19320     },
19321
19322     /**
19323      * Sets the start position of the element.  This is set when the obj
19324      * is initialized, the reset when a drag is started.
19325      * @method setStartPosition
19326      * @param pos current position (from previous lookup)
19327      * @private
19328      */
19329     setStartPosition: function(pos) {
19330         var p = pos || Dom.getXY( this.getEl() );
19331         this.deltaSetXY = null;
19332
19333         this.startPageX = p[0];
19334         this.startPageY = p[1];
19335     },
19336
19337     /**
19338      * Add this instance to a group of related drag/drop objects.  All
19339      * instances belong to at least one group, and can belong to as many
19340      * groups as needed.
19341      * @method addToGroup
19342      * @param sGroup {string} the name of the group
19343      */
19344     addToGroup: function(sGroup) {
19345         this.groups[sGroup] = true;
19346         this.DDM.regDragDrop(this, sGroup);
19347     },
19348
19349     /**
19350      * Remove's this instance from the supplied interaction group
19351      * @method removeFromGroup
19352      * @param {string}  sGroup  The group to drop
19353      */
19354     removeFromGroup: function(sGroup) {
19355         if (this.groups[sGroup]) {
19356             delete this.groups[sGroup];
19357         }
19358
19359         this.DDM.removeDDFromGroup(this, sGroup);
19360     },
19361
19362     /**
19363      * Allows you to specify that an element other than the linked element
19364      * will be moved with the cursor during a drag
19365      * @method setDragElId
19366      * @param id {string} the id of the element that will be used to initiate the drag
19367      */
19368     setDragElId: function(id) {
19369         this.dragElId = id;
19370     },
19371
19372     /**
19373      * Allows you to specify a child of the linked element that should be
19374      * used to initiate the drag operation.  An example of this would be if
19375      * you have a content div with text and links.  Clicking anywhere in the
19376      * content area would normally start the drag operation.  Use this method
19377      * to specify that an element inside of the content div is the element
19378      * that starts the drag operation.
19379      * @method setHandleElId
19380      * @param id {string} the id of the element that will be used to
19381      * initiate the drag.
19382      */
19383     setHandleElId: function(id) {
19384         if (typeof id !== "string") {
19385             id = Roo.id(id);
19386         }
19387         this.handleElId = id;
19388         this.DDM.regHandle(this.id, id);
19389     },
19390
19391     /**
19392      * Allows you to set an element outside of the linked element as a drag
19393      * handle
19394      * @method setOuterHandleElId
19395      * @param id the id of the element that will be used to initiate the drag
19396      */
19397     setOuterHandleElId: function(id) {
19398         if (typeof id !== "string") {
19399             id = Roo.id(id);
19400         }
19401         Event.on(id, "mousedown",
19402                 this.handleMouseDown, this);
19403         this.setHandleElId(id);
19404
19405         this.hasOuterHandles = true;
19406     },
19407
19408     /**
19409      * Remove all drag and drop hooks for this element
19410      * @method unreg
19411      */
19412     unreg: function() {
19413         Event.un(this.id, "mousedown",
19414                 this.handleMouseDown);
19415         Event.un(this.id, "touchstart",
19416                 this.handleMouseDown);
19417         this._domRef = null;
19418         this.DDM._remove(this);
19419     },
19420
19421     destroy : function(){
19422         this.unreg();
19423     },
19424
19425     /**
19426      * Returns true if this instance is locked, or the drag drop mgr is locked
19427      * (meaning that all drag/drop is disabled on the page.)
19428      * @method isLocked
19429      * @return {boolean} true if this obj or all drag/drop is locked, else
19430      * false
19431      */
19432     isLocked: function() {
19433         return (this.DDM.isLocked() || this.locked);
19434     },
19435
19436     /**
19437      * Fired when this object is clicked
19438      * @method handleMouseDown
19439      * @param {Event} e
19440      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
19441      * @private
19442      */
19443     handleMouseDown: function(e, oDD){
19444      
19445         if (!Roo.isTouch && this.primaryButtonOnly && e.button != 0) {
19446             //Roo.log('not touch/ button !=0');
19447             return;
19448         }
19449         if (e.browserEvent.touches && e.browserEvent.touches.length != 1) {
19450             return; // double touch..
19451         }
19452         
19453
19454         if (this.isLocked()) {
19455             //Roo.log('locked');
19456             return;
19457         }
19458
19459         this.DDM.refreshCache(this.groups);
19460 //        Roo.log([Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e)]);
19461         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
19462         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
19463             //Roo.log('no outer handes or not over target');
19464                 // do nothing.
19465         } else {
19466 //            Roo.log('check validator');
19467             if (this.clickValidator(e)) {
19468 //                Roo.log('validate success');
19469                 // set the initial element position
19470                 this.setStartPosition();
19471
19472
19473                 this.b4MouseDown(e);
19474                 this.onMouseDown(e);
19475
19476                 this.DDM.handleMouseDown(e, this);
19477
19478                 this.DDM.stopEvent(e);
19479             } else {
19480
19481
19482             }
19483         }
19484     },
19485
19486     clickValidator: function(e) {
19487         var target = e.getTarget();
19488         return ( this.isValidHandleChild(target) &&
19489                     (this.id == this.handleElId ||
19490                         this.DDM.handleWasClicked(target, this.id)) );
19491     },
19492
19493     /**
19494      * Allows you to specify a tag name that should not start a drag operation
19495      * when clicked.  This is designed to facilitate embedding links within a
19496      * drag handle that do something other than start the drag.
19497      * @method addInvalidHandleType
19498      * @param {string} tagName the type of element to exclude
19499      */
19500     addInvalidHandleType: function(tagName) {
19501         var type = tagName.toUpperCase();
19502         this.invalidHandleTypes[type] = type;
19503     },
19504
19505     /**
19506      * Lets you to specify an element id for a child of a drag handle
19507      * that should not initiate a drag
19508      * @method addInvalidHandleId
19509      * @param {string} id the element id of the element you wish to ignore
19510      */
19511     addInvalidHandleId: function(id) {
19512         if (typeof id !== "string") {
19513             id = Roo.id(id);
19514         }
19515         this.invalidHandleIds[id] = id;
19516     },
19517
19518     /**
19519      * Lets you specify a css class of elements that will not initiate a drag
19520      * @method addInvalidHandleClass
19521      * @param {string} cssClass the class of the elements you wish to ignore
19522      */
19523     addInvalidHandleClass: function(cssClass) {
19524         this.invalidHandleClasses.push(cssClass);
19525     },
19526
19527     /**
19528      * Unsets an excluded tag name set by addInvalidHandleType
19529      * @method removeInvalidHandleType
19530      * @param {string} tagName the type of element to unexclude
19531      */
19532     removeInvalidHandleType: function(tagName) {
19533         var type = tagName.toUpperCase();
19534         // this.invalidHandleTypes[type] = null;
19535         delete this.invalidHandleTypes[type];
19536     },
19537
19538     /**
19539      * Unsets an invalid handle id
19540      * @method removeInvalidHandleId
19541      * @param {string} id the id of the element to re-enable
19542      */
19543     removeInvalidHandleId: function(id) {
19544         if (typeof id !== "string") {
19545             id = Roo.id(id);
19546         }
19547         delete this.invalidHandleIds[id];
19548     },
19549
19550     /**
19551      * Unsets an invalid css class
19552      * @method removeInvalidHandleClass
19553      * @param {string} cssClass the class of the element(s) you wish to
19554      * re-enable
19555      */
19556     removeInvalidHandleClass: function(cssClass) {
19557         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
19558             if (this.invalidHandleClasses[i] == cssClass) {
19559                 delete this.invalidHandleClasses[i];
19560             }
19561         }
19562     },
19563
19564     /**
19565      * Checks the tag exclusion list to see if this click should be ignored
19566      * @method isValidHandleChild
19567      * @param {HTMLElement} node the HTMLElement to evaluate
19568      * @return {boolean} true if this is a valid tag type, false if not
19569      */
19570     isValidHandleChild: function(node) {
19571
19572         var valid = true;
19573         // var n = (node.nodeName == "#text") ? node.parentNode : node;
19574         var nodeName;
19575         try {
19576             nodeName = node.nodeName.toUpperCase();
19577         } catch(e) {
19578             nodeName = node.nodeName;
19579         }
19580         valid = valid && !this.invalidHandleTypes[nodeName];
19581         valid = valid && !this.invalidHandleIds[node.id];
19582
19583         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
19584             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
19585         }
19586
19587
19588         return valid;
19589
19590     },
19591
19592     /**
19593      * Create the array of horizontal tick marks if an interval was specified
19594      * in setXConstraint().
19595      * @method setXTicks
19596      * @private
19597      */
19598     setXTicks: function(iStartX, iTickSize) {
19599         this.xTicks = [];
19600         this.xTickSize = iTickSize;
19601
19602         var tickMap = {};
19603
19604         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
19605             if (!tickMap[i]) {
19606                 this.xTicks[this.xTicks.length] = i;
19607                 tickMap[i] = true;
19608             }
19609         }
19610
19611         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
19612             if (!tickMap[i]) {
19613                 this.xTicks[this.xTicks.length] = i;
19614                 tickMap[i] = true;
19615             }
19616         }
19617
19618         this.xTicks.sort(this.DDM.numericSort) ;
19619     },
19620
19621     /**
19622      * Create the array of vertical tick marks if an interval was specified in
19623      * setYConstraint().
19624      * @method setYTicks
19625      * @private
19626      */
19627     setYTicks: function(iStartY, iTickSize) {
19628         this.yTicks = [];
19629         this.yTickSize = iTickSize;
19630
19631         var tickMap = {};
19632
19633         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
19634             if (!tickMap[i]) {
19635                 this.yTicks[this.yTicks.length] = i;
19636                 tickMap[i] = true;
19637             }
19638         }
19639
19640         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
19641             if (!tickMap[i]) {
19642                 this.yTicks[this.yTicks.length] = i;
19643                 tickMap[i] = true;
19644             }
19645         }
19646
19647         this.yTicks.sort(this.DDM.numericSort) ;
19648     },
19649
19650     /**
19651      * By default, the element can be dragged any place on the screen.  Use
19652      * this method to limit the horizontal travel of the element.  Pass in
19653      * 0,0 for the parameters if you want to lock the drag to the y axis.
19654      * @method setXConstraint
19655      * @param {int} iLeft the number of pixels the element can move to the left
19656      * @param {int} iRight the number of pixels the element can move to the
19657      * right
19658      * @param {int} iTickSize optional parameter for specifying that the
19659      * element
19660      * should move iTickSize pixels at a time.
19661      */
19662     setXConstraint: function(iLeft, iRight, iTickSize) {
19663         this.leftConstraint = iLeft;
19664         this.rightConstraint = iRight;
19665
19666         this.minX = this.initPageX - iLeft;
19667         this.maxX = this.initPageX + iRight;
19668         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
19669
19670         this.constrainX = true;
19671     },
19672
19673     /**
19674      * Clears any constraints applied to this instance.  Also clears ticks
19675      * since they can't exist independent of a constraint at this time.
19676      * @method clearConstraints
19677      */
19678     clearConstraints: function() {
19679         this.constrainX = false;
19680         this.constrainY = false;
19681         this.clearTicks();
19682     },
19683
19684     /**
19685      * Clears any tick interval defined for this instance
19686      * @method clearTicks
19687      */
19688     clearTicks: function() {
19689         this.xTicks = null;
19690         this.yTicks = null;
19691         this.xTickSize = 0;
19692         this.yTickSize = 0;
19693     },
19694
19695     /**
19696      * By default, the element can be dragged any place on the screen.  Set
19697      * this to limit the vertical travel of the element.  Pass in 0,0 for the
19698      * parameters if you want to lock the drag to the x axis.
19699      * @method setYConstraint
19700      * @param {int} iUp the number of pixels the element can move up
19701      * @param {int} iDown the number of pixels the element can move down
19702      * @param {int} iTickSize optional parameter for specifying that the
19703      * element should move iTickSize pixels at a time.
19704      */
19705     setYConstraint: function(iUp, iDown, iTickSize) {
19706         this.topConstraint = iUp;
19707         this.bottomConstraint = iDown;
19708
19709         this.minY = this.initPageY - iUp;
19710         this.maxY = this.initPageY + iDown;
19711         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
19712
19713         this.constrainY = true;
19714
19715     },
19716
19717     /**
19718      * resetConstraints must be called if you manually reposition a dd element.
19719      * @method resetConstraints
19720      * @param {boolean} maintainOffset
19721      */
19722     resetConstraints: function() {
19723
19724
19725         // Maintain offsets if necessary
19726         if (this.initPageX || this.initPageX === 0) {
19727             // figure out how much this thing has moved
19728             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
19729             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
19730
19731             this.setInitPosition(dx, dy);
19732
19733         // This is the first time we have detected the element's position
19734         } else {
19735             this.setInitPosition();
19736         }
19737
19738         if (this.constrainX) {
19739             this.setXConstraint( this.leftConstraint,
19740                                  this.rightConstraint,
19741                                  this.xTickSize        );
19742         }
19743
19744         if (this.constrainY) {
19745             this.setYConstraint( this.topConstraint,
19746                                  this.bottomConstraint,
19747                                  this.yTickSize         );
19748         }
19749     },
19750
19751     /**
19752      * Normally the drag element is moved pixel by pixel, but we can specify
19753      * that it move a number of pixels at a time.  This method resolves the
19754      * location when we have it set up like this.
19755      * @method getTick
19756      * @param {int} val where we want to place the object
19757      * @param {int[]} tickArray sorted array of valid points
19758      * @return {int} the closest tick
19759      * @private
19760      */
19761     getTick: function(val, tickArray) {
19762
19763         if (!tickArray) {
19764             // If tick interval is not defined, it is effectively 1 pixel,
19765             // so we return the value passed to us.
19766             return val;
19767         } else if (tickArray[0] >= val) {
19768             // The value is lower than the first tick, so we return the first
19769             // tick.
19770             return tickArray[0];
19771         } else {
19772             for (var i=0, len=tickArray.length; i<len; ++i) {
19773                 var next = i + 1;
19774                 if (tickArray[next] && tickArray[next] >= val) {
19775                     var diff1 = val - tickArray[i];
19776                     var diff2 = tickArray[next] - val;
19777                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
19778                 }
19779             }
19780
19781             // The value is larger than the last tick, so we return the last
19782             // tick.
19783             return tickArray[tickArray.length - 1];
19784         }
19785     },
19786
19787     /**
19788      * toString method
19789      * @method toString
19790      * @return {string} string representation of the dd obj
19791      */
19792     toString: function() {
19793         return ("DragDrop " + this.id);
19794     }
19795
19796 });
19797
19798 })();
19799 /*
19800  * Based on:
19801  * Ext JS Library 1.1.1
19802  * Copyright(c) 2006-2007, Ext JS, LLC.
19803  *
19804  * Originally Released Under LGPL - original licence link has changed is not relivant.
19805  *
19806  * Fork - LGPL
19807  * <script type="text/javascript">
19808  */
19809
19810
19811 /**
19812  * The drag and drop utility provides a framework for building drag and drop
19813  * applications.  In addition to enabling drag and drop for specific elements,
19814  * the drag and drop elements are tracked by the manager class, and the
19815  * interactions between the various elements are tracked during the drag and
19816  * the implementing code is notified about these important moments.
19817  */
19818
19819 // Only load the library once.  Rewriting the manager class would orphan
19820 // existing drag and drop instances.
19821 if (!Roo.dd.DragDropMgr) {
19822
19823 /**
19824  * @class Roo.dd.DragDropMgr
19825  * DragDropMgr is a singleton that tracks the element interaction for
19826  * all DragDrop items in the window.  Generally, you will not call
19827  * this class directly, but it does have helper methods that could
19828  * be useful in your DragDrop implementations.
19829  * @singleton
19830  */
19831 Roo.dd.DragDropMgr = function() {
19832
19833     var Event = Roo.EventManager;
19834
19835     return {
19836
19837         /**
19838          * Two dimensional Array of registered DragDrop objects.  The first
19839          * dimension is the DragDrop item group, the second the DragDrop
19840          * object.
19841          * @property ids
19842          * @type {string: string}
19843          * @private
19844          * @static
19845          */
19846         ids: {},
19847
19848         /**
19849          * Array of element ids defined as drag handles.  Used to determine
19850          * if the element that generated the mousedown event is actually the
19851          * handle and not the html element itself.
19852          * @property handleIds
19853          * @type {string: string}
19854          * @private
19855          * @static
19856          */
19857         handleIds: {},
19858
19859         /**
19860          * the DragDrop object that is currently being dragged
19861          * @property dragCurrent
19862          * @type DragDrop
19863          * @private
19864          * @static
19865          **/
19866         dragCurrent: null,
19867
19868         /**
19869          * the DragDrop object(s) that are being hovered over
19870          * @property dragOvers
19871          * @type Array
19872          * @private
19873          * @static
19874          */
19875         dragOvers: {},
19876
19877         /**
19878          * the X distance between the cursor and the object being dragged
19879          * @property deltaX
19880          * @type int
19881          * @private
19882          * @static
19883          */
19884         deltaX: 0,
19885
19886         /**
19887          * the Y distance between the cursor and the object being dragged
19888          * @property deltaY
19889          * @type int
19890          * @private
19891          * @static
19892          */
19893         deltaY: 0,
19894
19895         /**
19896          * Flag to determine if we should prevent the default behavior of the
19897          * events we define. By default this is true, but this can be set to
19898          * false if you need the default behavior (not recommended)
19899          * @property preventDefault
19900          * @type boolean
19901          * @static
19902          */
19903         preventDefault: true,
19904
19905         /**
19906          * Flag to determine if we should stop the propagation of the events
19907          * we generate. This is true by default but you may want to set it to
19908          * false if the html element contains other features that require the
19909          * mouse click.
19910          * @property stopPropagation
19911          * @type boolean
19912          * @static
19913          */
19914         stopPropagation: true,
19915
19916         /**
19917          * Internal flag that is set to true when drag and drop has been
19918          * intialized
19919          * @property initialized
19920          * @private
19921          * @static
19922          */
19923         initalized: false,
19924
19925         /**
19926          * All drag and drop can be disabled.
19927          * @property locked
19928          * @private
19929          * @static
19930          */
19931         locked: false,
19932
19933         /**
19934          * Called the first time an element is registered.
19935          * @method init
19936          * @private
19937          * @static
19938          */
19939         init: function() {
19940             this.initialized = true;
19941         },
19942
19943         /**
19944          * In point mode, drag and drop interaction is defined by the
19945          * location of the cursor during the drag/drop
19946          * @property POINT
19947          * @type int
19948          * @static
19949          */
19950         POINT: 0,
19951
19952         /**
19953          * In intersect mode, drag and drop interactio nis defined by the
19954          * overlap of two or more drag and drop objects.
19955          * @property INTERSECT
19956          * @type int
19957          * @static
19958          */
19959         INTERSECT: 1,
19960
19961         /**
19962          * The current drag and drop mode.  Default: POINT
19963          * @property mode
19964          * @type int
19965          * @static
19966          */
19967         mode: 0,
19968
19969         /**
19970          * Runs method on all drag and drop objects
19971          * @method _execOnAll
19972          * @private
19973          * @static
19974          */
19975         _execOnAll: function(sMethod, args) {
19976             for (var i in this.ids) {
19977                 for (var j in this.ids[i]) {
19978                     var oDD = this.ids[i][j];
19979                     if (! this.isTypeOfDD(oDD)) {
19980                         continue;
19981                     }
19982                     oDD[sMethod].apply(oDD, args);
19983                 }
19984             }
19985         },
19986
19987         /**
19988          * Drag and drop initialization.  Sets up the global event handlers
19989          * @method _onLoad
19990          * @private
19991          * @static
19992          */
19993         _onLoad: function() {
19994
19995             this.init();
19996
19997             if (!Roo.isTouch) {
19998                 Event.on(document, "mouseup",   this.handleMouseUp, this, true);
19999                 Event.on(document, "mousemove", this.handleMouseMove, this, true);
20000             }
20001             Event.on(document, "touchend",   this.handleMouseUp, this, true);
20002             Event.on(document, "touchmove", this.handleMouseMove, this, true);
20003             
20004             Event.on(window,   "unload",    this._onUnload, this, true);
20005             Event.on(window,   "resize",    this._onResize, this, true);
20006             // Event.on(window,   "mouseout",    this._test);
20007
20008         },
20009
20010         /**
20011          * Reset constraints on all drag and drop objs
20012          * @method _onResize
20013          * @private
20014          * @static
20015          */
20016         _onResize: function(e) {
20017             this._execOnAll("resetConstraints", []);
20018         },
20019
20020         /**
20021          * Lock all drag and drop functionality
20022          * @method lock
20023          * @static
20024          */
20025         lock: function() { this.locked = true; },
20026
20027         /**
20028          * Unlock all drag and drop functionality
20029          * @method unlock
20030          * @static
20031          */
20032         unlock: function() { this.locked = false; },
20033
20034         /**
20035          * Is drag and drop locked?
20036          * @method isLocked
20037          * @return {boolean} True if drag and drop is locked, false otherwise.
20038          * @static
20039          */
20040         isLocked: function() { return this.locked; },
20041
20042         /**
20043          * Location cache that is set for all drag drop objects when a drag is
20044          * initiated, cleared when the drag is finished.
20045          * @property locationCache
20046          * @private
20047          * @static
20048          */
20049         locationCache: {},
20050
20051         /**
20052          * Set useCache to false if you want to force object the lookup of each
20053          * drag and drop linked element constantly during a drag.
20054          * @property useCache
20055          * @type boolean
20056          * @static
20057          */
20058         useCache: true,
20059
20060         /**
20061          * The number of pixels that the mouse needs to move after the
20062          * mousedown before the drag is initiated.  Default=3;
20063          * @property clickPixelThresh
20064          * @type int
20065          * @static
20066          */
20067         clickPixelThresh: 3,
20068
20069         /**
20070          * The number of milliseconds after the mousedown event to initiate the
20071          * drag if we don't get a mouseup event. Default=1000
20072          * @property clickTimeThresh
20073          * @type int
20074          * @static
20075          */
20076         clickTimeThresh: 350,
20077
20078         /**
20079          * Flag that indicates that either the drag pixel threshold or the
20080          * mousdown time threshold has been met
20081          * @property dragThreshMet
20082          * @type boolean
20083          * @private
20084          * @static
20085          */
20086         dragThreshMet: false,
20087
20088         /**
20089          * Timeout used for the click time threshold
20090          * @property clickTimeout
20091          * @type Object
20092          * @private
20093          * @static
20094          */
20095         clickTimeout: null,
20096
20097         /**
20098          * The X position of the mousedown event stored for later use when a
20099          * drag threshold is met.
20100          * @property startX
20101          * @type int
20102          * @private
20103          * @static
20104          */
20105         startX: 0,
20106
20107         /**
20108          * The Y position of the mousedown event stored for later use when a
20109          * drag threshold is met.
20110          * @property startY
20111          * @type int
20112          * @private
20113          * @static
20114          */
20115         startY: 0,
20116
20117         /**
20118          * Each DragDrop instance must be registered with the DragDropMgr.
20119          * This is executed in DragDrop.init()
20120          * @method regDragDrop
20121          * @param {DragDrop} oDD the DragDrop object to register
20122          * @param {String} sGroup the name of the group this element belongs to
20123          * @static
20124          */
20125         regDragDrop: function(oDD, sGroup) {
20126             if (!this.initialized) { this.init(); }
20127
20128             if (!this.ids[sGroup]) {
20129                 this.ids[sGroup] = {};
20130             }
20131             this.ids[sGroup][oDD.id] = oDD;
20132         },
20133
20134         /**
20135          * Removes the supplied dd instance from the supplied group. Executed
20136          * by DragDrop.removeFromGroup, so don't call this function directly.
20137          * @method removeDDFromGroup
20138          * @private
20139          * @static
20140          */
20141         removeDDFromGroup: function(oDD, sGroup) {
20142             if (!this.ids[sGroup]) {
20143                 this.ids[sGroup] = {};
20144             }
20145
20146             var obj = this.ids[sGroup];
20147             if (obj && obj[oDD.id]) {
20148                 delete obj[oDD.id];
20149             }
20150         },
20151
20152         /**
20153          * Unregisters a drag and drop item.  This is executed in
20154          * DragDrop.unreg, use that method instead of calling this directly.
20155          * @method _remove
20156          * @private
20157          * @static
20158          */
20159         _remove: function(oDD) {
20160             for (var g in oDD.groups) {
20161                 if (g && this.ids[g][oDD.id]) {
20162                     delete this.ids[g][oDD.id];
20163                 }
20164             }
20165             delete this.handleIds[oDD.id];
20166         },
20167
20168         /**
20169          * Each DragDrop handle element must be registered.  This is done
20170          * automatically when executing DragDrop.setHandleElId()
20171          * @method regHandle
20172          * @param {String} sDDId the DragDrop id this element is a handle for
20173          * @param {String} sHandleId the id of the element that is the drag
20174          * handle
20175          * @static
20176          */
20177         regHandle: function(sDDId, sHandleId) {
20178             if (!this.handleIds[sDDId]) {
20179                 this.handleIds[sDDId] = {};
20180             }
20181             this.handleIds[sDDId][sHandleId] = sHandleId;
20182         },
20183
20184         /**
20185          * Utility function to determine if a given element has been
20186          * registered as a drag drop item.
20187          * @method isDragDrop
20188          * @param {String} id the element id to check
20189          * @return {boolean} true if this element is a DragDrop item,
20190          * false otherwise
20191          * @static
20192          */
20193         isDragDrop: function(id) {
20194             return ( this.getDDById(id) ) ? true : false;
20195         },
20196
20197         /**
20198          * Returns the drag and drop instances that are in all groups the
20199          * passed in instance belongs to.
20200          * @method getRelated
20201          * @param {DragDrop} p_oDD the obj to get related data for
20202          * @param {boolean} bTargetsOnly if true, only return targetable objs
20203          * @return {DragDrop[]} the related instances
20204          * @static
20205          */
20206         getRelated: function(p_oDD, bTargetsOnly) {
20207             var oDDs = [];
20208             for (var i in p_oDD.groups) {
20209                 for (j in this.ids[i]) {
20210                     var dd = this.ids[i][j];
20211                     if (! this.isTypeOfDD(dd)) {
20212                         continue;
20213                     }
20214                     if (!bTargetsOnly || dd.isTarget) {
20215                         oDDs[oDDs.length] = dd;
20216                     }
20217                 }
20218             }
20219
20220             return oDDs;
20221         },
20222
20223         /**
20224          * Returns true if the specified dd target is a legal target for
20225          * the specifice drag obj
20226          * @method isLegalTarget
20227          * @param {DragDrop} the drag obj
20228          * @param {DragDrop} the target
20229          * @return {boolean} true if the target is a legal target for the
20230          * dd obj
20231          * @static
20232          */
20233         isLegalTarget: function (oDD, oTargetDD) {
20234             var targets = this.getRelated(oDD, true);
20235             for (var i=0, len=targets.length;i<len;++i) {
20236                 if (targets[i].id == oTargetDD.id) {
20237                     return true;
20238                 }
20239             }
20240
20241             return false;
20242         },
20243
20244         /**
20245          * My goal is to be able to transparently determine if an object is
20246          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
20247          * returns "object", oDD.constructor.toString() always returns
20248          * "DragDrop" and not the name of the subclass.  So for now it just
20249          * evaluates a well-known variable in DragDrop.
20250          * @method isTypeOfDD
20251          * @param {Object} the object to evaluate
20252          * @return {boolean} true if typeof oDD = DragDrop
20253          * @static
20254          */
20255         isTypeOfDD: function (oDD) {
20256             return (oDD && oDD.__ygDragDrop);
20257         },
20258
20259         /**
20260          * Utility function to determine if a given element has been
20261          * registered as a drag drop handle for the given Drag Drop object.
20262          * @method isHandle
20263          * @param {String} id the element id to check
20264          * @return {boolean} true if this element is a DragDrop handle, false
20265          * otherwise
20266          * @static
20267          */
20268         isHandle: function(sDDId, sHandleId) {
20269             return ( this.handleIds[sDDId] &&
20270                             this.handleIds[sDDId][sHandleId] );
20271         },
20272
20273         /**
20274          * Returns the DragDrop instance for a given id
20275          * @method getDDById
20276          * @param {String} id the id of the DragDrop object
20277          * @return {DragDrop} the drag drop object, null if it is not found
20278          * @static
20279          */
20280         getDDById: function(id) {
20281             for (var i in this.ids) {
20282                 if (this.ids[i][id]) {
20283                     return this.ids[i][id];
20284                 }
20285             }
20286             return null;
20287         },
20288
20289         /**
20290          * Fired after a registered DragDrop object gets the mousedown event.
20291          * Sets up the events required to track the object being dragged
20292          * @method handleMouseDown
20293          * @param {Event} e the event
20294          * @param oDD the DragDrop object being dragged
20295          * @private
20296          * @static
20297          */
20298         handleMouseDown: function(e, oDD) {
20299             if(Roo.QuickTips){
20300                 Roo.QuickTips.disable();
20301             }
20302             this.currentTarget = e.getTarget();
20303
20304             this.dragCurrent = oDD;
20305
20306             var el = oDD.getEl();
20307
20308             // track start position
20309             this.startX = e.getPageX();
20310             this.startY = e.getPageY();
20311
20312             this.deltaX = this.startX - el.offsetLeft;
20313             this.deltaY = this.startY - el.offsetTop;
20314
20315             this.dragThreshMet = false;
20316
20317             this.clickTimeout = setTimeout(
20318                     function() {
20319                         var DDM = Roo.dd.DDM;
20320                         DDM.startDrag(DDM.startX, DDM.startY);
20321                     },
20322                     this.clickTimeThresh );
20323         },
20324
20325         /**
20326          * Fired when either the drag pixel threshol or the mousedown hold
20327          * time threshold has been met.
20328          * @method startDrag
20329          * @param x {int} the X position of the original mousedown
20330          * @param y {int} the Y position of the original mousedown
20331          * @static
20332          */
20333         startDrag: function(x, y) {
20334             clearTimeout(this.clickTimeout);
20335             if (this.dragCurrent) {
20336                 this.dragCurrent.b4StartDrag(x, y);
20337                 this.dragCurrent.startDrag(x, y);
20338             }
20339             this.dragThreshMet = true;
20340         },
20341
20342         /**
20343          * Internal function to handle the mouseup event.  Will be invoked
20344          * from the context of the document.
20345          * @method handleMouseUp
20346          * @param {Event} e the event
20347          * @private
20348          * @static
20349          */
20350         handleMouseUp: function(e) {
20351
20352             if(Roo.QuickTips){
20353                 Roo.QuickTips.enable();
20354             }
20355             if (! this.dragCurrent) {
20356                 return;
20357             }
20358
20359             clearTimeout(this.clickTimeout);
20360
20361             if (this.dragThreshMet) {
20362                 this.fireEvents(e, true);
20363             } else {
20364             }
20365
20366             this.stopDrag(e);
20367
20368             this.stopEvent(e);
20369         },
20370
20371         /**
20372          * Utility to stop event propagation and event default, if these
20373          * features are turned on.
20374          * @method stopEvent
20375          * @param {Event} e the event as returned by this.getEvent()
20376          * @static
20377          */
20378         stopEvent: function(e){
20379             if(this.stopPropagation) {
20380                 e.stopPropagation();
20381             }
20382
20383             if (this.preventDefault) {
20384                 e.preventDefault();
20385             }
20386         },
20387
20388         /**
20389          * Internal function to clean up event handlers after the drag
20390          * operation is complete
20391          * @method stopDrag
20392          * @param {Event} e the event
20393          * @private
20394          * @static
20395          */
20396         stopDrag: function(e) {
20397             // Fire the drag end event for the item that was dragged
20398             if (this.dragCurrent) {
20399                 if (this.dragThreshMet) {
20400                     this.dragCurrent.b4EndDrag(e);
20401                     this.dragCurrent.endDrag(e);
20402                 }
20403
20404                 this.dragCurrent.onMouseUp(e);
20405             }
20406
20407             this.dragCurrent = null;
20408             this.dragOvers = {};
20409         },
20410
20411         /**
20412          * Internal function to handle the mousemove event.  Will be invoked
20413          * from the context of the html element.
20414          *
20415          * @TODO figure out what we can do about mouse events lost when the
20416          * user drags objects beyond the window boundary.  Currently we can
20417          * detect this in internet explorer by verifying that the mouse is
20418          * down during the mousemove event.  Firefox doesn't give us the
20419          * button state on the mousemove event.
20420          * @method handleMouseMove
20421          * @param {Event} e the event
20422          * @private
20423          * @static
20424          */
20425         handleMouseMove: function(e) {
20426             if (! this.dragCurrent) {
20427                 return true;
20428             }
20429
20430             // var button = e.which || e.button;
20431
20432             // check for IE mouseup outside of page boundary
20433             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
20434                 this.stopEvent(e);
20435                 return this.handleMouseUp(e);
20436             }
20437
20438             if (!this.dragThreshMet) {
20439                 var diffX = Math.abs(this.startX - e.getPageX());
20440                 var diffY = Math.abs(this.startY - e.getPageY());
20441                 if (diffX > this.clickPixelThresh ||
20442                             diffY > this.clickPixelThresh) {
20443                     this.startDrag(this.startX, this.startY);
20444                 }
20445             }
20446
20447             if (this.dragThreshMet) {
20448                 this.dragCurrent.b4Drag(e);
20449                 this.dragCurrent.onDrag(e);
20450                 if(!this.dragCurrent.moveOnly){
20451                     this.fireEvents(e, false);
20452                 }
20453             }
20454
20455             this.stopEvent(e);
20456
20457             return true;
20458         },
20459
20460         /**
20461          * Iterates over all of the DragDrop elements to find ones we are
20462          * hovering over or dropping on
20463          * @method fireEvents
20464          * @param {Event} e the event
20465          * @param {boolean} isDrop is this a drop op or a mouseover op?
20466          * @private
20467          * @static
20468          */
20469         fireEvents: function(e, isDrop) {
20470             var dc = this.dragCurrent;
20471
20472             // If the user did the mouse up outside of the window, we could
20473             // get here even though we have ended the drag.
20474             if (!dc || dc.isLocked()) {
20475                 return;
20476             }
20477
20478             var pt = e.getPoint();
20479
20480             // cache the previous dragOver array
20481             var oldOvers = [];
20482
20483             var outEvts   = [];
20484             var overEvts  = [];
20485             var dropEvts  = [];
20486             var enterEvts = [];
20487
20488             // Check to see if the object(s) we were hovering over is no longer
20489             // being hovered over so we can fire the onDragOut event
20490             for (var i in this.dragOvers) {
20491
20492                 var ddo = this.dragOvers[i];
20493
20494                 if (! this.isTypeOfDD(ddo)) {
20495                     continue;
20496                 }
20497
20498                 if (! this.isOverTarget(pt, ddo, this.mode)) {
20499                     outEvts.push( ddo );
20500                 }
20501
20502                 oldOvers[i] = true;
20503                 delete this.dragOvers[i];
20504             }
20505
20506             for (var sGroup in dc.groups) {
20507
20508                 if ("string" != typeof sGroup) {
20509                     continue;
20510                 }
20511
20512                 for (i in this.ids[sGroup]) {
20513                     var oDD = this.ids[sGroup][i];
20514                     if (! this.isTypeOfDD(oDD)) {
20515                         continue;
20516                     }
20517
20518                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
20519                         if (this.isOverTarget(pt, oDD, this.mode)) {
20520                             // look for drop interactions
20521                             if (isDrop) {
20522                                 dropEvts.push( oDD );
20523                             // look for drag enter and drag over interactions
20524                             } else {
20525
20526                                 // initial drag over: dragEnter fires
20527                                 if (!oldOvers[oDD.id]) {
20528                                     enterEvts.push( oDD );
20529                                 // subsequent drag overs: dragOver fires
20530                                 } else {
20531                                     overEvts.push( oDD );
20532                                 }
20533
20534                                 this.dragOvers[oDD.id] = oDD;
20535                             }
20536                         }
20537                     }
20538                 }
20539             }
20540
20541             if (this.mode) {
20542                 if (outEvts.length) {
20543                     dc.b4DragOut(e, outEvts);
20544                     dc.onDragOut(e, outEvts);
20545                 }
20546
20547                 if (enterEvts.length) {
20548                     dc.onDragEnter(e, enterEvts);
20549                 }
20550
20551                 if (overEvts.length) {
20552                     dc.b4DragOver(e, overEvts);
20553                     dc.onDragOver(e, overEvts);
20554                 }
20555
20556                 if (dropEvts.length) {
20557                     dc.b4DragDrop(e, dropEvts);
20558                     dc.onDragDrop(e, dropEvts);
20559                 }
20560
20561             } else {
20562                 // fire dragout events
20563                 var len = 0;
20564                 for (i=0, len=outEvts.length; i<len; ++i) {
20565                     dc.b4DragOut(e, outEvts[i].id);
20566                     dc.onDragOut(e, outEvts[i].id);
20567                 }
20568
20569                 // fire enter events
20570                 for (i=0,len=enterEvts.length; i<len; ++i) {
20571                     // dc.b4DragEnter(e, oDD.id);
20572                     dc.onDragEnter(e, enterEvts[i].id);
20573                 }
20574
20575                 // fire over events
20576                 for (i=0,len=overEvts.length; i<len; ++i) {
20577                     dc.b4DragOver(e, overEvts[i].id);
20578                     dc.onDragOver(e, overEvts[i].id);
20579                 }
20580
20581                 // fire drop events
20582                 for (i=0, len=dropEvts.length; i<len; ++i) {
20583                     dc.b4DragDrop(e, dropEvts[i].id);
20584                     dc.onDragDrop(e, dropEvts[i].id);
20585                 }
20586
20587             }
20588
20589             // notify about a drop that did not find a target
20590             if (isDrop && !dropEvts.length) {
20591                 dc.onInvalidDrop(e);
20592             }
20593
20594         },
20595
20596         /**
20597          * Helper function for getting the best match from the list of drag
20598          * and drop objects returned by the drag and drop events when we are
20599          * in INTERSECT mode.  It returns either the first object that the
20600          * cursor is over, or the object that has the greatest overlap with
20601          * the dragged element.
20602          * @method getBestMatch
20603          * @param  {DragDrop[]} dds The array of drag and drop objects
20604          * targeted
20605          * @return {DragDrop}       The best single match
20606          * @static
20607          */
20608         getBestMatch: function(dds) {
20609             var winner = null;
20610             // Return null if the input is not what we expect
20611             //if (!dds || !dds.length || dds.length == 0) {
20612                // winner = null;
20613             // If there is only one item, it wins
20614             //} else if (dds.length == 1) {
20615
20616             var len = dds.length;
20617
20618             if (len == 1) {
20619                 winner = dds[0];
20620             } else {
20621                 // Loop through the targeted items
20622                 for (var i=0; i<len; ++i) {
20623                     var dd = dds[i];
20624                     // If the cursor is over the object, it wins.  If the
20625                     // cursor is over multiple matches, the first one we come
20626                     // to wins.
20627                     if (dd.cursorIsOver) {
20628                         winner = dd;
20629                         break;
20630                     // Otherwise the object with the most overlap wins
20631                     } else {
20632                         if (!winner ||
20633                             winner.overlap.getArea() < dd.overlap.getArea()) {
20634                             winner = dd;
20635                         }
20636                     }
20637                 }
20638             }
20639
20640             return winner;
20641         },
20642
20643         /**
20644          * Refreshes the cache of the top-left and bottom-right points of the
20645          * drag and drop objects in the specified group(s).  This is in the
20646          * format that is stored in the drag and drop instance, so typical
20647          * usage is:
20648          * <code>
20649          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
20650          * </code>
20651          * Alternatively:
20652          * <code>
20653          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
20654          * </code>
20655          * @TODO this really should be an indexed array.  Alternatively this
20656          * method could accept both.
20657          * @method refreshCache
20658          * @param {Object} groups an associative array of groups to refresh
20659          * @static
20660          */
20661         refreshCache: function(groups) {
20662             for (var sGroup in groups) {
20663                 if ("string" != typeof sGroup) {
20664                     continue;
20665                 }
20666                 for (var i in this.ids[sGroup]) {
20667                     var oDD = this.ids[sGroup][i];
20668
20669                     if (this.isTypeOfDD(oDD)) {
20670                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
20671                         var loc = this.getLocation(oDD);
20672                         if (loc) {
20673                             this.locationCache[oDD.id] = loc;
20674                         } else {
20675                             delete this.locationCache[oDD.id];
20676                             // this will unregister the drag and drop object if
20677                             // the element is not in a usable state
20678                             // oDD.unreg();
20679                         }
20680                     }
20681                 }
20682             }
20683         },
20684
20685         /**
20686          * This checks to make sure an element exists and is in the DOM.  The
20687          * main purpose is to handle cases where innerHTML is used to remove
20688          * drag and drop objects from the DOM.  IE provides an 'unspecified
20689          * error' when trying to access the offsetParent of such an element
20690          * @method verifyEl
20691          * @param {HTMLElement} el the element to check
20692          * @return {boolean} true if the element looks usable
20693          * @static
20694          */
20695         verifyEl: function(el) {
20696             if (el) {
20697                 var parent;
20698                 if(Roo.isIE){
20699                     try{
20700                         parent = el.offsetParent;
20701                     }catch(e){}
20702                 }else{
20703                     parent = el.offsetParent;
20704                 }
20705                 if (parent) {
20706                     return true;
20707                 }
20708             }
20709
20710             return false;
20711         },
20712
20713         /**
20714          * Returns a Region object containing the drag and drop element's position
20715          * and size, including the padding configured for it
20716          * @method getLocation
20717          * @param {DragDrop} oDD the drag and drop object to get the
20718          *                       location for
20719          * @return {Roo.lib.Region} a Region object representing the total area
20720          *                             the element occupies, including any padding
20721          *                             the instance is configured for.
20722          * @static
20723          */
20724         getLocation: function(oDD) {
20725             if (! this.isTypeOfDD(oDD)) {
20726                 return null;
20727             }
20728
20729             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
20730
20731             try {
20732                 pos= Roo.lib.Dom.getXY(el);
20733             } catch (e) { }
20734
20735             if (!pos) {
20736                 return null;
20737             }
20738
20739             x1 = pos[0];
20740             x2 = x1 + el.offsetWidth;
20741             y1 = pos[1];
20742             y2 = y1 + el.offsetHeight;
20743
20744             t = y1 - oDD.padding[0];
20745             r = x2 + oDD.padding[1];
20746             b = y2 + oDD.padding[2];
20747             l = x1 - oDD.padding[3];
20748
20749             return new Roo.lib.Region( t, r, b, l );
20750         },
20751
20752         /**
20753          * Checks the cursor location to see if it over the target
20754          * @method isOverTarget
20755          * @param {Roo.lib.Point} pt The point to evaluate
20756          * @param {DragDrop} oTarget the DragDrop object we are inspecting
20757          * @return {boolean} true if the mouse is over the target
20758          * @private
20759          * @static
20760          */
20761         isOverTarget: function(pt, oTarget, intersect) {
20762             // use cache if available
20763             var loc = this.locationCache[oTarget.id];
20764             if (!loc || !this.useCache) {
20765                 loc = this.getLocation(oTarget);
20766                 this.locationCache[oTarget.id] = loc;
20767
20768             }
20769
20770             if (!loc) {
20771                 return false;
20772             }
20773
20774             oTarget.cursorIsOver = loc.contains( pt );
20775
20776             // DragDrop is using this as a sanity check for the initial mousedown
20777             // in this case we are done.  In POINT mode, if the drag obj has no
20778             // contraints, we are also done. Otherwise we need to evaluate the
20779             // location of the target as related to the actual location of the
20780             // dragged element.
20781             var dc = this.dragCurrent;
20782             if (!dc || !dc.getTargetCoord ||
20783                     (!intersect && !dc.constrainX && !dc.constrainY)) {
20784                 return oTarget.cursorIsOver;
20785             }
20786
20787             oTarget.overlap = null;
20788
20789             // Get the current location of the drag element, this is the
20790             // location of the mouse event less the delta that represents
20791             // where the original mousedown happened on the element.  We
20792             // need to consider constraints and ticks as well.
20793             var pos = dc.getTargetCoord(pt.x, pt.y);
20794
20795             var el = dc.getDragEl();
20796             var curRegion = new Roo.lib.Region( pos.y,
20797                                                    pos.x + el.offsetWidth,
20798                                                    pos.y + el.offsetHeight,
20799                                                    pos.x );
20800
20801             var overlap = curRegion.intersect(loc);
20802
20803             if (overlap) {
20804                 oTarget.overlap = overlap;
20805                 return (intersect) ? true : oTarget.cursorIsOver;
20806             } else {
20807                 return false;
20808             }
20809         },
20810
20811         /**
20812          * unload event handler
20813          * @method _onUnload
20814          * @private
20815          * @static
20816          */
20817         _onUnload: function(e, me) {
20818             Roo.dd.DragDropMgr.unregAll();
20819         },
20820
20821         /**
20822          * Cleans up the drag and drop events and objects.
20823          * @method unregAll
20824          * @private
20825          * @static
20826          */
20827         unregAll: function() {
20828
20829             if (this.dragCurrent) {
20830                 this.stopDrag();
20831                 this.dragCurrent = null;
20832             }
20833
20834             this._execOnAll("unreg", []);
20835
20836             for (i in this.elementCache) {
20837                 delete this.elementCache[i];
20838             }
20839
20840             this.elementCache = {};
20841             this.ids = {};
20842         },
20843
20844         /**
20845          * A cache of DOM elements
20846          * @property elementCache
20847          * @private
20848          * @static
20849          */
20850         elementCache: {},
20851
20852         /**
20853          * Get the wrapper for the DOM element specified
20854          * @method getElWrapper
20855          * @param {String} id the id of the element to get
20856          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
20857          * @private
20858          * @deprecated This wrapper isn't that useful
20859          * @static
20860          */
20861         getElWrapper: function(id) {
20862             var oWrapper = this.elementCache[id];
20863             if (!oWrapper || !oWrapper.el) {
20864                 oWrapper = this.elementCache[id] =
20865                     new this.ElementWrapper(Roo.getDom(id));
20866             }
20867             return oWrapper;
20868         },
20869
20870         /**
20871          * Returns the actual DOM element
20872          * @method getElement
20873          * @param {String} id the id of the elment to get
20874          * @return {Object} The element
20875          * @deprecated use Roo.getDom instead
20876          * @static
20877          */
20878         getElement: function(id) {
20879             return Roo.getDom(id);
20880         },
20881
20882         /**
20883          * Returns the style property for the DOM element (i.e.,
20884          * document.getElById(id).style)
20885          * @method getCss
20886          * @param {String} id the id of the elment to get
20887          * @return {Object} The style property of the element
20888          * @deprecated use Roo.getDom instead
20889          * @static
20890          */
20891         getCss: function(id) {
20892             var el = Roo.getDom(id);
20893             return (el) ? el.style : null;
20894         },
20895
20896         /**
20897          * Inner class for cached elements
20898          * @class DragDropMgr.ElementWrapper
20899          * @for DragDropMgr
20900          * @private
20901          * @deprecated
20902          */
20903         ElementWrapper: function(el) {
20904                 /**
20905                  * The element
20906                  * @property el
20907                  */
20908                 this.el = el || null;
20909                 /**
20910                  * The element id
20911                  * @property id
20912                  */
20913                 this.id = this.el && el.id;
20914                 /**
20915                  * A reference to the style property
20916                  * @property css
20917                  */
20918                 this.css = this.el && el.style;
20919             },
20920
20921         /**
20922          * Returns the X position of an html element
20923          * @method getPosX
20924          * @param el the element for which to get the position
20925          * @return {int} the X coordinate
20926          * @for DragDropMgr
20927          * @deprecated use Roo.lib.Dom.getX instead
20928          * @static
20929          */
20930         getPosX: function(el) {
20931             return Roo.lib.Dom.getX(el);
20932         },
20933
20934         /**
20935          * Returns the Y position of an html element
20936          * @method getPosY
20937          * @param el the element for which to get the position
20938          * @return {int} the Y coordinate
20939          * @deprecated use Roo.lib.Dom.getY instead
20940          * @static
20941          */
20942         getPosY: function(el) {
20943             return Roo.lib.Dom.getY(el);
20944         },
20945
20946         /**
20947          * Swap two nodes.  In IE, we use the native method, for others we
20948          * emulate the IE behavior
20949          * @method swapNode
20950          * @param n1 the first node to swap
20951          * @param n2 the other node to swap
20952          * @static
20953          */
20954         swapNode: function(n1, n2) {
20955             if (n1.swapNode) {
20956                 n1.swapNode(n2);
20957             } else {
20958                 var p = n2.parentNode;
20959                 var s = n2.nextSibling;
20960
20961                 if (s == n1) {
20962                     p.insertBefore(n1, n2);
20963                 } else if (n2 == n1.nextSibling) {
20964                     p.insertBefore(n2, n1);
20965                 } else {
20966                     n1.parentNode.replaceChild(n2, n1);
20967                     p.insertBefore(n1, s);
20968                 }
20969             }
20970         },
20971
20972         /**
20973          * Returns the current scroll position
20974          * @method getScroll
20975          * @private
20976          * @static
20977          */
20978         getScroll: function () {
20979             var t, l, dde=document.documentElement, db=document.body;
20980             if (dde && (dde.scrollTop || dde.scrollLeft)) {
20981                 t = dde.scrollTop;
20982                 l = dde.scrollLeft;
20983             } else if (db) {
20984                 t = db.scrollTop;
20985                 l = db.scrollLeft;
20986             } else {
20987
20988             }
20989             return { top: t, left: l };
20990         },
20991
20992         /**
20993          * Returns the specified element style property
20994          * @method getStyle
20995          * @param {HTMLElement} el          the element
20996          * @param {string}      styleProp   the style property
20997          * @return {string} The value of the style property
20998          * @deprecated use Roo.lib.Dom.getStyle
20999          * @static
21000          */
21001         getStyle: function(el, styleProp) {
21002             return Roo.fly(el).getStyle(styleProp);
21003         },
21004
21005         /**
21006          * Gets the scrollTop
21007          * @method getScrollTop
21008          * @return {int} the document's scrollTop
21009          * @static
21010          */
21011         getScrollTop: function () { return this.getScroll().top; },
21012
21013         /**
21014          * Gets the scrollLeft
21015          * @method getScrollLeft
21016          * @return {int} the document's scrollTop
21017          * @static
21018          */
21019         getScrollLeft: function () { return this.getScroll().left; },
21020
21021         /**
21022          * Sets the x/y position of an element to the location of the
21023          * target element.
21024          * @method moveToEl
21025          * @param {HTMLElement} moveEl      The element to move
21026          * @param {HTMLElement} targetEl    The position reference element
21027          * @static
21028          */
21029         moveToEl: function (moveEl, targetEl) {
21030             var aCoord = Roo.lib.Dom.getXY(targetEl);
21031             Roo.lib.Dom.setXY(moveEl, aCoord);
21032         },
21033
21034         /**
21035          * Numeric array sort function
21036          * @method numericSort
21037          * @static
21038          */
21039         numericSort: function(a, b) { return (a - b); },
21040
21041         /**
21042          * Internal counter
21043          * @property _timeoutCount
21044          * @private
21045          * @static
21046          */
21047         _timeoutCount: 0,
21048
21049         /**
21050          * Trying to make the load order less important.  Without this we get
21051          * an error if this file is loaded before the Event Utility.
21052          * @method _addListeners
21053          * @private
21054          * @static
21055          */
21056         _addListeners: function() {
21057             var DDM = Roo.dd.DDM;
21058             if ( Roo.lib.Event && document ) {
21059                 DDM._onLoad();
21060             } else {
21061                 if (DDM._timeoutCount > 2000) {
21062                 } else {
21063                     setTimeout(DDM._addListeners, 10);
21064                     if (document && document.body) {
21065                         DDM._timeoutCount += 1;
21066                     }
21067                 }
21068             }
21069         },
21070
21071         /**
21072          * Recursively searches the immediate parent and all child nodes for
21073          * the handle element in order to determine wheter or not it was
21074          * clicked.
21075          * @method handleWasClicked
21076          * @param node the html element to inspect
21077          * @static
21078          */
21079         handleWasClicked: function(node, id) {
21080             if (this.isHandle(id, node.id)) {
21081                 return true;
21082             } else {
21083                 // check to see if this is a text node child of the one we want
21084                 var p = node.parentNode;
21085
21086                 while (p) {
21087                     if (this.isHandle(id, p.id)) {
21088                         return true;
21089                     } else {
21090                         p = p.parentNode;
21091                     }
21092                 }
21093             }
21094
21095             return false;
21096         }
21097
21098     };
21099
21100 }();
21101
21102 // shorter alias, save a few bytes
21103 Roo.dd.DDM = Roo.dd.DragDropMgr;
21104 Roo.dd.DDM._addListeners();
21105
21106 }/*
21107  * Based on:
21108  * Ext JS Library 1.1.1
21109  * Copyright(c) 2006-2007, Ext JS, LLC.
21110  *
21111  * Originally Released Under LGPL - original licence link has changed is not relivant.
21112  *
21113  * Fork - LGPL
21114  * <script type="text/javascript">
21115  */
21116
21117 /**
21118  * @class Roo.dd.DD
21119  * A DragDrop implementation where the linked element follows the
21120  * mouse cursor during a drag.
21121  * @extends Roo.dd.DragDrop
21122  * @constructor
21123  * @param {String} id the id of the linked element
21124  * @param {String} sGroup the group of related DragDrop items
21125  * @param {object} config an object containing configurable attributes
21126  *                Valid properties for DD:
21127  *                    scroll
21128  */
21129 Roo.dd.DD = function(id, sGroup, config) {
21130     if (id) {
21131         this.init(id, sGroup, config);
21132     }
21133 };
21134
21135 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
21136
21137     /**
21138      * When set to true, the utility automatically tries to scroll the browser
21139      * window wehn a drag and drop element is dragged near the viewport boundary.
21140      * Defaults to true.
21141      * @property scroll
21142      * @type boolean
21143      */
21144     scroll: true,
21145
21146     /**
21147      * Sets the pointer offset to the distance between the linked element's top
21148      * left corner and the location the element was clicked
21149      * @method autoOffset
21150      * @param {int} iPageX the X coordinate of the click
21151      * @param {int} iPageY the Y coordinate of the click
21152      */
21153     autoOffset: function(iPageX, iPageY) {
21154         var x = iPageX - this.startPageX;
21155         var y = iPageY - this.startPageY;
21156         this.setDelta(x, y);
21157     },
21158
21159     /**
21160      * Sets the pointer offset.  You can call this directly to force the
21161      * offset to be in a particular location (e.g., pass in 0,0 to set it
21162      * to the center of the object)
21163      * @method setDelta
21164      * @param {int} iDeltaX the distance from the left
21165      * @param {int} iDeltaY the distance from the top
21166      */
21167     setDelta: function(iDeltaX, iDeltaY) {
21168         this.deltaX = iDeltaX;
21169         this.deltaY = iDeltaY;
21170     },
21171
21172     /**
21173      * Sets the drag element to the location of the mousedown or click event,
21174      * maintaining the cursor location relative to the location on the element
21175      * that was clicked.  Override this if you want to place the element in a
21176      * location other than where the cursor is.
21177      * @method setDragElPos
21178      * @param {int} iPageX the X coordinate of the mousedown or drag event
21179      * @param {int} iPageY the Y coordinate of the mousedown or drag event
21180      */
21181     setDragElPos: function(iPageX, iPageY) {
21182         // the first time we do this, we are going to check to make sure
21183         // the element has css positioning
21184
21185         var el = this.getDragEl();
21186         this.alignElWithMouse(el, iPageX, iPageY);
21187     },
21188
21189     /**
21190      * Sets the element to the location of the mousedown or click event,
21191      * maintaining the cursor location relative to the location on the element
21192      * that was clicked.  Override this if you want to place the element in a
21193      * location other than where the cursor is.
21194      * @method alignElWithMouse
21195      * @param {HTMLElement} el the element to move
21196      * @param {int} iPageX the X coordinate of the mousedown or drag event
21197      * @param {int} iPageY the Y coordinate of the mousedown or drag event
21198      */
21199     alignElWithMouse: function(el, iPageX, iPageY) {
21200         var oCoord = this.getTargetCoord(iPageX, iPageY);
21201         var fly = el.dom ? el : Roo.fly(el);
21202         if (!this.deltaSetXY) {
21203             var aCoord = [oCoord.x, oCoord.y];
21204             fly.setXY(aCoord);
21205             var newLeft = fly.getLeft(true);
21206             var newTop  = fly.getTop(true);
21207             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
21208         } else {
21209             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
21210         }
21211
21212         this.cachePosition(oCoord.x, oCoord.y);
21213         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
21214         return oCoord;
21215     },
21216
21217     /**
21218      * Saves the most recent position so that we can reset the constraints and
21219      * tick marks on-demand.  We need to know this so that we can calculate the
21220      * number of pixels the element is offset from its original position.
21221      * @method cachePosition
21222      * @param iPageX the current x position (optional, this just makes it so we
21223      * don't have to look it up again)
21224      * @param iPageY the current y position (optional, this just makes it so we
21225      * don't have to look it up again)
21226      */
21227     cachePosition: function(iPageX, iPageY) {
21228         if (iPageX) {
21229             this.lastPageX = iPageX;
21230             this.lastPageY = iPageY;
21231         } else {
21232             var aCoord = Roo.lib.Dom.getXY(this.getEl());
21233             this.lastPageX = aCoord[0];
21234             this.lastPageY = aCoord[1];
21235         }
21236     },
21237
21238     /**
21239      * Auto-scroll the window if the dragged object has been moved beyond the
21240      * visible window boundary.
21241      * @method autoScroll
21242      * @param {int} x the drag element's x position
21243      * @param {int} y the drag element's y position
21244      * @param {int} h the height of the drag element
21245      * @param {int} w the width of the drag element
21246      * @private
21247      */
21248     autoScroll: function(x, y, h, w) {
21249
21250         if (this.scroll) {
21251             // The client height
21252             var clientH = Roo.lib.Dom.getViewWidth();
21253
21254             // The client width
21255             var clientW = Roo.lib.Dom.getViewHeight();
21256
21257             // The amt scrolled down
21258             var st = this.DDM.getScrollTop();
21259
21260             // The amt scrolled right
21261             var sl = this.DDM.getScrollLeft();
21262
21263             // Location of the bottom of the element
21264             var bot = h + y;
21265
21266             // Location of the right of the element
21267             var right = w + x;
21268
21269             // The distance from the cursor to the bottom of the visible area,
21270             // adjusted so that we don't scroll if the cursor is beyond the
21271             // element drag constraints
21272             var toBot = (clientH + st - y - this.deltaY);
21273
21274             // The distance from the cursor to the right of the visible area
21275             var toRight = (clientW + sl - x - this.deltaX);
21276
21277
21278             // How close to the edge the cursor must be before we scroll
21279             // var thresh = (document.all) ? 100 : 40;
21280             var thresh = 40;
21281
21282             // How many pixels to scroll per autoscroll op.  This helps to reduce
21283             // clunky scrolling. IE is more sensitive about this ... it needs this
21284             // value to be higher.
21285             var scrAmt = (document.all) ? 80 : 30;
21286
21287             // Scroll down if we are near the bottom of the visible page and the
21288             // obj extends below the crease
21289             if ( bot > clientH && toBot < thresh ) {
21290                 window.scrollTo(sl, st + scrAmt);
21291             }
21292
21293             // Scroll up if the window is scrolled down and the top of the object
21294             // goes above the top border
21295             if ( y < st && st > 0 && y - st < thresh ) {
21296                 window.scrollTo(sl, st - scrAmt);
21297             }
21298
21299             // Scroll right if the obj is beyond the right border and the cursor is
21300             // near the border.
21301             if ( right > clientW && toRight < thresh ) {
21302                 window.scrollTo(sl + scrAmt, st);
21303             }
21304
21305             // Scroll left if the window has been scrolled to the right and the obj
21306             // extends past the left border
21307             if ( x < sl && sl > 0 && x - sl < thresh ) {
21308                 window.scrollTo(sl - scrAmt, st);
21309             }
21310         }
21311     },
21312
21313     /**
21314      * Finds the location the element should be placed if we want to move
21315      * it to where the mouse location less the click offset would place us.
21316      * @method getTargetCoord
21317      * @param {int} iPageX the X coordinate of the click
21318      * @param {int} iPageY the Y coordinate of the click
21319      * @return an object that contains the coordinates (Object.x and Object.y)
21320      * @private
21321      */
21322     getTargetCoord: function(iPageX, iPageY) {
21323
21324
21325         var x = iPageX - this.deltaX;
21326         var y = iPageY - this.deltaY;
21327
21328         if (this.constrainX) {
21329             if (x < this.minX) { x = this.minX; }
21330             if (x > this.maxX) { x = this.maxX; }
21331         }
21332
21333         if (this.constrainY) {
21334             if (y < this.minY) { y = this.minY; }
21335             if (y > this.maxY) { y = this.maxY; }
21336         }
21337
21338         x = this.getTick(x, this.xTicks);
21339         y = this.getTick(y, this.yTicks);
21340
21341
21342         return {x:x, y:y};
21343     },
21344
21345     /*
21346      * Sets up config options specific to this class. Overrides
21347      * Roo.dd.DragDrop, but all versions of this method through the
21348      * inheritance chain are called
21349      */
21350     applyConfig: function() {
21351         Roo.dd.DD.superclass.applyConfig.call(this);
21352         this.scroll = (this.config.scroll !== false);
21353     },
21354
21355     /*
21356      * Event that fires prior to the onMouseDown event.  Overrides
21357      * Roo.dd.DragDrop.
21358      */
21359     b4MouseDown: function(e) {
21360         // this.resetConstraints();
21361         this.autoOffset(e.getPageX(),
21362                             e.getPageY());
21363     },
21364
21365     /*
21366      * Event that fires prior to the onDrag event.  Overrides
21367      * Roo.dd.DragDrop.
21368      */
21369     b4Drag: function(e) {
21370         this.setDragElPos(e.getPageX(),
21371                             e.getPageY());
21372     },
21373
21374     toString: function() {
21375         return ("DD " + this.id);
21376     }
21377
21378     //////////////////////////////////////////////////////////////////////////
21379     // Debugging ygDragDrop events that can be overridden
21380     //////////////////////////////////////////////////////////////////////////
21381     /*
21382     startDrag: function(x, y) {
21383     },
21384
21385     onDrag: function(e) {
21386     },
21387
21388     onDragEnter: function(e, id) {
21389     },
21390
21391     onDragOver: function(e, id) {
21392     },
21393
21394     onDragOut: function(e, id) {
21395     },
21396
21397     onDragDrop: function(e, id) {
21398     },
21399
21400     endDrag: function(e) {
21401     }
21402
21403     */
21404
21405 });/*
21406  * Based on:
21407  * Ext JS Library 1.1.1
21408  * Copyright(c) 2006-2007, Ext JS, LLC.
21409  *
21410  * Originally Released Under LGPL - original licence link has changed is not relivant.
21411  *
21412  * Fork - LGPL
21413  * <script type="text/javascript">
21414  */
21415
21416 /**
21417  * @class Roo.dd.DDProxy
21418  * A DragDrop implementation that inserts an empty, bordered div into
21419  * the document that follows the cursor during drag operations.  At the time of
21420  * the click, the frame div is resized to the dimensions of the linked html
21421  * element, and moved to the exact location of the linked element.
21422  *
21423  * References to the "frame" element refer to the single proxy element that
21424  * was created to be dragged in place of all DDProxy elements on the
21425  * page.
21426  *
21427  * @extends Roo.dd.DD
21428  * @constructor
21429  * @param {String} id the id of the linked html element
21430  * @param {String} sGroup the group of related DragDrop objects
21431  * @param {object} config an object containing configurable attributes
21432  *                Valid properties for DDProxy in addition to those in DragDrop:
21433  *                   resizeFrame, centerFrame, dragElId
21434  */
21435 Roo.dd.DDProxy = function(id, sGroup, config) {
21436     if (id) {
21437         this.init(id, sGroup, config);
21438         this.initFrame();
21439     }
21440 };
21441
21442 /**
21443  * The default drag frame div id
21444  * @property Roo.dd.DDProxy.dragElId
21445  * @type String
21446  * @static
21447  */
21448 Roo.dd.DDProxy.dragElId = "ygddfdiv";
21449
21450 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
21451
21452     /**
21453      * By default we resize the drag frame to be the same size as the element
21454      * we want to drag (this is to get the frame effect).  We can turn it off
21455      * if we want a different behavior.
21456      * @property resizeFrame
21457      * @type boolean
21458      */
21459     resizeFrame: true,
21460
21461     /**
21462      * By default the frame is positioned exactly where the drag element is, so
21463      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
21464      * you do not have constraints on the obj is to have the drag frame centered
21465      * around the cursor.  Set centerFrame to true for this effect.
21466      * @property centerFrame
21467      * @type boolean
21468      */
21469     centerFrame: false,
21470
21471     /**
21472      * Creates the proxy element if it does not yet exist
21473      * @method createFrame
21474      */
21475     createFrame: function() {
21476         var self = this;
21477         var body = document.body;
21478
21479         if (!body || !body.firstChild) {
21480             setTimeout( function() { self.createFrame(); }, 50 );
21481             return;
21482         }
21483
21484         var div = this.getDragEl();
21485
21486         if (!div) {
21487             div    = document.createElement("div");
21488             div.id = this.dragElId;
21489             var s  = div.style;
21490
21491             s.position   = "absolute";
21492             s.visibility = "hidden";
21493             s.cursor     = "move";
21494             s.border     = "2px solid #aaa";
21495             s.zIndex     = 999;
21496
21497             // appendChild can blow up IE if invoked prior to the window load event
21498             // while rendering a table.  It is possible there are other scenarios
21499             // that would cause this to happen as well.
21500             body.insertBefore(div, body.firstChild);
21501         }
21502     },
21503
21504     /**
21505      * Initialization for the drag frame element.  Must be called in the
21506      * constructor of all subclasses
21507      * @method initFrame
21508      */
21509     initFrame: function() {
21510         this.createFrame();
21511     },
21512
21513     applyConfig: function() {
21514         Roo.dd.DDProxy.superclass.applyConfig.call(this);
21515
21516         this.resizeFrame = (this.config.resizeFrame !== false);
21517         this.centerFrame = (this.config.centerFrame);
21518         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
21519     },
21520
21521     /**
21522      * Resizes the drag frame to the dimensions of the clicked object, positions
21523      * it over the object, and finally displays it
21524      * @method showFrame
21525      * @param {int} iPageX X click position
21526      * @param {int} iPageY Y click position
21527      * @private
21528      */
21529     showFrame: function(iPageX, iPageY) {
21530         var el = this.getEl();
21531         var dragEl = this.getDragEl();
21532         var s = dragEl.style;
21533
21534         this._resizeProxy();
21535
21536         if (this.centerFrame) {
21537             this.setDelta( Math.round(parseInt(s.width,  10)/2),
21538                            Math.round(parseInt(s.height, 10)/2) );
21539         }
21540
21541         this.setDragElPos(iPageX, iPageY);
21542
21543         Roo.fly(dragEl).show();
21544     },
21545
21546     /**
21547      * The proxy is automatically resized to the dimensions of the linked
21548      * element when a drag is initiated, unless resizeFrame is set to false
21549      * @method _resizeProxy
21550      * @private
21551      */
21552     _resizeProxy: function() {
21553         if (this.resizeFrame) {
21554             var el = this.getEl();
21555             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
21556         }
21557     },
21558
21559     // overrides Roo.dd.DragDrop
21560     b4MouseDown: function(e) {
21561         var x = e.getPageX();
21562         var y = e.getPageY();
21563         this.autoOffset(x, y);
21564         this.setDragElPos(x, y);
21565     },
21566
21567     // overrides Roo.dd.DragDrop
21568     b4StartDrag: function(x, y) {
21569         // show the drag frame
21570         this.showFrame(x, y);
21571     },
21572
21573     // overrides Roo.dd.DragDrop
21574     b4EndDrag: function(e) {
21575         Roo.fly(this.getDragEl()).hide();
21576     },
21577
21578     // overrides Roo.dd.DragDrop
21579     // By default we try to move the element to the last location of the frame.
21580     // This is so that the default behavior mirrors that of Roo.dd.DD.
21581     endDrag: function(e) {
21582
21583         var lel = this.getEl();
21584         var del = this.getDragEl();
21585
21586         // Show the drag frame briefly so we can get its position
21587         del.style.visibility = "";
21588
21589         this.beforeMove();
21590         // Hide the linked element before the move to get around a Safari
21591         // rendering bug.
21592         lel.style.visibility = "hidden";
21593         Roo.dd.DDM.moveToEl(lel, del);
21594         del.style.visibility = "hidden";
21595         lel.style.visibility = "";
21596
21597         this.afterDrag();
21598     },
21599
21600     beforeMove : function(){
21601
21602     },
21603
21604     afterDrag : function(){
21605
21606     },
21607
21608     toString: function() {
21609         return ("DDProxy " + this.id);
21610     }
21611
21612 });
21613 /*
21614  * Based on:
21615  * Ext JS Library 1.1.1
21616  * Copyright(c) 2006-2007, Ext JS, LLC.
21617  *
21618  * Originally Released Under LGPL - original licence link has changed is not relivant.
21619  *
21620  * Fork - LGPL
21621  * <script type="text/javascript">
21622  */
21623
21624  /**
21625  * @class Roo.dd.DDTarget
21626  * A DragDrop implementation that does not move, but can be a drop
21627  * target.  You would get the same result by simply omitting implementation
21628  * for the event callbacks, but this way we reduce the processing cost of the
21629  * event listener and the callbacks.
21630  * @extends Roo.dd.DragDrop
21631  * @constructor
21632  * @param {String} id the id of the element that is a drop target
21633  * @param {String} sGroup the group of related DragDrop objects
21634  * @param {object} config an object containing configurable attributes
21635  *                 Valid properties for DDTarget in addition to those in
21636  *                 DragDrop:
21637  *                    none
21638  */
21639 Roo.dd.DDTarget = function(id, sGroup, config) {
21640     if (id) {
21641         this.initTarget(id, sGroup, config);
21642     }
21643     if (config && (config.listeners || config.events)) { 
21644         Roo.dd.DragDrop.superclass.constructor.call(this,  { 
21645             listeners : config.listeners || {}, 
21646             events : config.events || {} 
21647         });    
21648     }
21649 };
21650
21651 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
21652 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
21653     toString: function() {
21654         return ("DDTarget " + this.id);
21655     }
21656 });
21657 /*
21658  * Based on:
21659  * Ext JS Library 1.1.1
21660  * Copyright(c) 2006-2007, Ext JS, LLC.
21661  *
21662  * Originally Released Under LGPL - original licence link has changed is not relivant.
21663  *
21664  * Fork - LGPL
21665  * <script type="text/javascript">
21666  */
21667  
21668
21669 /**
21670  * @class Roo.dd.ScrollManager
21671  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
21672  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
21673  * @singleton
21674  */
21675 Roo.dd.ScrollManager = function(){
21676     var ddm = Roo.dd.DragDropMgr;
21677     var els = {};
21678     var dragEl = null;
21679     var proc = {};
21680     
21681     
21682     
21683     var onStop = function(e){
21684         dragEl = null;
21685         clearProc();
21686     };
21687     
21688     var triggerRefresh = function(){
21689         if(ddm.dragCurrent){
21690              ddm.refreshCache(ddm.dragCurrent.groups);
21691         }
21692     };
21693     
21694     var doScroll = function(){
21695         if(ddm.dragCurrent){
21696             var dds = Roo.dd.ScrollManager;
21697             if(!dds.animate){
21698                 if(proc.el.scroll(proc.dir, dds.increment)){
21699                     triggerRefresh();
21700                 }
21701             }else{
21702                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
21703             }
21704         }
21705     };
21706     
21707     var clearProc = function(){
21708         if(proc.id){
21709             clearInterval(proc.id);
21710         }
21711         proc.id = 0;
21712         proc.el = null;
21713         proc.dir = "";
21714     };
21715     
21716     var startProc = function(el, dir){
21717          Roo.log('scroll startproc');
21718         clearProc();
21719         proc.el = el;
21720         proc.dir = dir;
21721         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
21722     };
21723     
21724     var onFire = function(e, isDrop){
21725        
21726         if(isDrop || !ddm.dragCurrent){ return; }
21727         var dds = Roo.dd.ScrollManager;
21728         if(!dragEl || dragEl != ddm.dragCurrent){
21729             dragEl = ddm.dragCurrent;
21730             // refresh regions on drag start
21731             dds.refreshCache();
21732         }
21733         
21734         var xy = Roo.lib.Event.getXY(e);
21735         var pt = new Roo.lib.Point(xy[0], xy[1]);
21736         for(var id in els){
21737             var el = els[id], r = el._region;
21738             if(r && r.contains(pt) && el.isScrollable()){
21739                 if(r.bottom - pt.y <= dds.thresh){
21740                     if(proc.el != el){
21741                         startProc(el, "down");
21742                     }
21743                     return;
21744                 }else if(r.right - pt.x <= dds.thresh){
21745                     if(proc.el != el){
21746                         startProc(el, "left");
21747                     }
21748                     return;
21749                 }else if(pt.y - r.top <= dds.thresh){
21750                     if(proc.el != el){
21751                         startProc(el, "up");
21752                     }
21753                     return;
21754                 }else if(pt.x - r.left <= dds.thresh){
21755                     if(proc.el != el){
21756                         startProc(el, "right");
21757                     }
21758                     return;
21759                 }
21760             }
21761         }
21762         clearProc();
21763     };
21764     
21765     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
21766     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
21767     
21768     return {
21769         /**
21770          * Registers new overflow element(s) to auto scroll
21771          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
21772          */
21773         register : function(el){
21774             if(el instanceof Array){
21775                 for(var i = 0, len = el.length; i < len; i++) {
21776                         this.register(el[i]);
21777                 }
21778             }else{
21779                 el = Roo.get(el);
21780                 els[el.id] = el;
21781             }
21782             Roo.dd.ScrollManager.els = els;
21783         },
21784         
21785         /**
21786          * Unregisters overflow element(s) so they are no longer scrolled
21787          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
21788          */
21789         unregister : function(el){
21790             if(el instanceof Array){
21791                 for(var i = 0, len = el.length; i < len; i++) {
21792                         this.unregister(el[i]);
21793                 }
21794             }else{
21795                 el = Roo.get(el);
21796                 delete els[el.id];
21797             }
21798         },
21799         
21800         /**
21801          * The number of pixels from the edge of a container the pointer needs to be to 
21802          * trigger scrolling (defaults to 25)
21803          * @type Number
21804          */
21805         thresh : 25,
21806         
21807         /**
21808          * The number of pixels to scroll in each scroll increment (defaults to 50)
21809          * @type Number
21810          */
21811         increment : 100,
21812         
21813         /**
21814          * The frequency of scrolls in milliseconds (defaults to 500)
21815          * @type Number
21816          */
21817         frequency : 500,
21818         
21819         /**
21820          * True to animate the scroll (defaults to true)
21821          * @type Boolean
21822          */
21823         animate: true,
21824         
21825         /**
21826          * The animation duration in seconds - 
21827          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
21828          * @type Number
21829          */
21830         animDuration: .4,
21831         
21832         /**
21833          * Manually trigger a cache refresh.
21834          */
21835         refreshCache : function(){
21836             for(var id in els){
21837                 if(typeof els[id] == 'object'){ // for people extending the object prototype
21838                     els[id]._region = els[id].getRegion();
21839                 }
21840             }
21841         }
21842     };
21843 }();/*
21844  * Based on:
21845  * Ext JS Library 1.1.1
21846  * Copyright(c) 2006-2007, Ext JS, LLC.
21847  *
21848  * Originally Released Under LGPL - original licence link has changed is not relivant.
21849  *
21850  * Fork - LGPL
21851  * <script type="text/javascript">
21852  */
21853  
21854
21855 /**
21856  * @class Roo.dd.Registry
21857  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
21858  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
21859  * @singleton
21860  */
21861 Roo.dd.Registry = function(){
21862     var elements = {}; 
21863     var handles = {}; 
21864     var autoIdSeed = 0;
21865
21866     var getId = function(el, autogen){
21867         if(typeof el == "string"){
21868             return el;
21869         }
21870         var id = el.id;
21871         if(!id && autogen !== false){
21872             id = "roodd-" + (++autoIdSeed);
21873             el.id = id;
21874         }
21875         return id;
21876     };
21877     
21878     return {
21879     /**
21880      * Register a drag drop element
21881      * @param {String|HTMLElement} element The id or DOM node to register
21882      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
21883      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
21884      * knows how to interpret, plus there are some specific properties known to the Registry that should be
21885      * populated in the data object (if applicable):
21886      * <pre>
21887 Value      Description<br />
21888 ---------  ------------------------------------------<br />
21889 handles    Array of DOM nodes that trigger dragging<br />
21890            for the element being registered<br />
21891 isHandle   True if the element passed in triggers<br />
21892            dragging itself, else false
21893 </pre>
21894      */
21895         register : function(el, data){
21896             data = data || {};
21897             if(typeof el == "string"){
21898                 el = document.getElementById(el);
21899             }
21900             data.ddel = el;
21901             elements[getId(el)] = data;
21902             if(data.isHandle !== false){
21903                 handles[data.ddel.id] = data;
21904             }
21905             if(data.handles){
21906                 var hs = data.handles;
21907                 for(var i = 0, len = hs.length; i < len; i++){
21908                         handles[getId(hs[i])] = data;
21909                 }
21910             }
21911         },
21912
21913     /**
21914      * Unregister a drag drop element
21915      * @param {String|HTMLElement}  element The id or DOM node to unregister
21916      */
21917         unregister : function(el){
21918             var id = getId(el, false);
21919             var data = elements[id];
21920             if(data){
21921                 delete elements[id];
21922                 if(data.handles){
21923                     var hs = data.handles;
21924                     for(var i = 0, len = hs.length; i < len; i++){
21925                         delete handles[getId(hs[i], false)];
21926                     }
21927                 }
21928             }
21929         },
21930
21931     /**
21932      * Returns the handle registered for a DOM Node by id
21933      * @param {String|HTMLElement} id The DOM node or id to look up
21934      * @return {Object} handle The custom handle data
21935      */
21936         getHandle : function(id){
21937             if(typeof id != "string"){ // must be element?
21938                 id = id.id;
21939             }
21940             return handles[id];
21941         },
21942
21943     /**
21944      * Returns the handle that is registered for the DOM node that is the target of the event
21945      * @param {Event} e The event
21946      * @return {Object} handle The custom handle data
21947      */
21948         getHandleFromEvent : function(e){
21949             var t = Roo.lib.Event.getTarget(e);
21950             return t ? handles[t.id] : null;
21951         },
21952
21953     /**
21954      * Returns a custom data object that is registered for a DOM node by id
21955      * @param {String|HTMLElement} id The DOM node or id to look up
21956      * @return {Object} data The custom data
21957      */
21958         getTarget : function(id){
21959             if(typeof id != "string"){ // must be element?
21960                 id = id.id;
21961             }
21962             return elements[id];
21963         },
21964
21965     /**
21966      * Returns a custom data object that is registered for the DOM node that is the target of the event
21967      * @param {Event} e The event
21968      * @return {Object} data The custom data
21969      */
21970         getTargetFromEvent : function(e){
21971             var t = Roo.lib.Event.getTarget(e);
21972             return t ? elements[t.id] || handles[t.id] : null;
21973         }
21974     };
21975 }();/*
21976  * Based on:
21977  * Ext JS Library 1.1.1
21978  * Copyright(c) 2006-2007, Ext JS, LLC.
21979  *
21980  * Originally Released Under LGPL - original licence link has changed is not relivant.
21981  *
21982  * Fork - LGPL
21983  * <script type="text/javascript">
21984  */
21985  
21986
21987 /**
21988  * @class Roo.dd.StatusProxy
21989  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
21990  * default drag proxy used by all Roo.dd components.
21991  * @constructor
21992  * @param {Object} config
21993  */
21994 Roo.dd.StatusProxy = function(config){
21995     Roo.apply(this, config);
21996     this.id = this.id || Roo.id();
21997     this.el = new Roo.Layer({
21998         dh: {
21999             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
22000                 {tag: "div", cls: "x-dd-drop-icon"},
22001                 {tag: "div", cls: "x-dd-drag-ghost"}
22002             ]
22003         }, 
22004         shadow: !config || config.shadow !== false
22005     });
22006     this.ghost = Roo.get(this.el.dom.childNodes[1]);
22007     this.dropStatus = this.dropNotAllowed;
22008 };
22009
22010 Roo.dd.StatusProxy.prototype = {
22011     /**
22012      * @cfg {String} dropAllowed
22013      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
22014      */
22015     dropAllowed : "x-dd-drop-ok",
22016     /**
22017      * @cfg {String} dropNotAllowed
22018      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
22019      */
22020     dropNotAllowed : "x-dd-drop-nodrop",
22021
22022     /**
22023      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
22024      * over the current target element.
22025      * @param {String} cssClass The css class for the new drop status indicator image
22026      */
22027     setStatus : function(cssClass){
22028         cssClass = cssClass || this.dropNotAllowed;
22029         if(this.dropStatus != cssClass){
22030             this.el.replaceClass(this.dropStatus, cssClass);
22031             this.dropStatus = cssClass;
22032         }
22033     },
22034
22035     /**
22036      * Resets the status indicator to the default dropNotAllowed value
22037      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
22038      */
22039     reset : function(clearGhost){
22040         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
22041         this.dropStatus = this.dropNotAllowed;
22042         if(clearGhost){
22043             this.ghost.update("");
22044         }
22045     },
22046
22047     /**
22048      * Updates the contents of the ghost element
22049      * @param {String} html The html that will replace the current innerHTML of the ghost element
22050      */
22051     update : function(html){
22052         if(typeof html == "string"){
22053             this.ghost.update(html);
22054         }else{
22055             this.ghost.update("");
22056             html.style.margin = "0";
22057             this.ghost.dom.appendChild(html);
22058         }
22059         // ensure float = none set?? cant remember why though.
22060         var el = this.ghost.dom.firstChild;
22061                 if(el){
22062                         Roo.fly(el).setStyle('float', 'none');
22063                 }
22064     },
22065     
22066     /**
22067      * Returns the underlying proxy {@link Roo.Layer}
22068      * @return {Roo.Layer} el
22069     */
22070     getEl : function(){
22071         return this.el;
22072     },
22073
22074     /**
22075      * Returns the ghost element
22076      * @return {Roo.Element} el
22077      */
22078     getGhost : function(){
22079         return this.ghost;
22080     },
22081
22082     /**
22083      * Hides the proxy
22084      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
22085      */
22086     hide : function(clear){
22087         this.el.hide();
22088         if(clear){
22089             this.reset(true);
22090         }
22091     },
22092
22093     /**
22094      * Stops the repair animation if it's currently running
22095      */
22096     stop : function(){
22097         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
22098             this.anim.stop();
22099         }
22100     },
22101
22102     /**
22103      * Displays this proxy
22104      */
22105     show : function(){
22106         this.el.show();
22107     },
22108
22109     /**
22110      * Force the Layer to sync its shadow and shim positions to the element
22111      */
22112     sync : function(){
22113         this.el.sync();
22114     },
22115
22116     /**
22117      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
22118      * invalid drop operation by the item being dragged.
22119      * @param {Array} xy The XY position of the element ([x, y])
22120      * @param {Function} callback The function to call after the repair is complete
22121      * @param {Object} scope The scope in which to execute the callback
22122      */
22123     repair : function(xy, callback, scope){
22124         this.callback = callback;
22125         this.scope = scope;
22126         if(xy && this.animRepair !== false){
22127             this.el.addClass("x-dd-drag-repair");
22128             this.el.hideUnders(true);
22129             this.anim = this.el.shift({
22130                 duration: this.repairDuration || .5,
22131                 easing: 'easeOut',
22132                 xy: xy,
22133                 stopFx: true,
22134                 callback: this.afterRepair,
22135                 scope: this
22136             });
22137         }else{
22138             this.afterRepair();
22139         }
22140     },
22141
22142     // private
22143     afterRepair : function(){
22144         this.hide(true);
22145         if(typeof this.callback == "function"){
22146             this.callback.call(this.scope || this);
22147         }
22148         this.callback = null;
22149         this.scope = null;
22150     }
22151 };/*
22152  * Based on:
22153  * Ext JS Library 1.1.1
22154  * Copyright(c) 2006-2007, Ext JS, LLC.
22155  *
22156  * Originally Released Under LGPL - original licence link has changed is not relivant.
22157  *
22158  * Fork - LGPL
22159  * <script type="text/javascript">
22160  */
22161
22162 /**
22163  * @class Roo.dd.DragSource
22164  * @extends Roo.dd.DDProxy
22165  * A simple class that provides the basic implementation needed to make any element draggable.
22166  * @constructor
22167  * @param {String/HTMLElement/Element} el The container element
22168  * @param {Object} config
22169  */
22170 Roo.dd.DragSource = function(el, config){
22171     this.el = Roo.get(el);
22172     this.dragData = {};
22173     
22174     Roo.apply(this, config);
22175     
22176     if(!this.proxy){
22177         this.proxy = new Roo.dd.StatusProxy();
22178     }
22179
22180     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
22181           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
22182     
22183     this.dragging = false;
22184 };
22185
22186 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
22187     /**
22188      * @cfg {String} dropAllowed
22189      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
22190      */
22191     dropAllowed : "x-dd-drop-ok",
22192     /**
22193      * @cfg {String} dropNotAllowed
22194      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
22195      */
22196     dropNotAllowed : "x-dd-drop-nodrop",
22197
22198     /**
22199      * Returns the data object associated with this drag source
22200      * @return {Object} data An object containing arbitrary data
22201      */
22202     getDragData : function(e){
22203         return this.dragData;
22204     },
22205
22206     // private
22207     onDragEnter : function(e, id){
22208         var target = Roo.dd.DragDropMgr.getDDById(id);
22209         this.cachedTarget = target;
22210         if(this.beforeDragEnter(target, e, id) !== false){
22211             if(target.isNotifyTarget){
22212                 var status = target.notifyEnter(this, e, this.dragData);
22213                 this.proxy.setStatus(status);
22214             }else{
22215                 this.proxy.setStatus(this.dropAllowed);
22216             }
22217             
22218             if(this.afterDragEnter){
22219                 /**
22220                  * An empty function by default, but provided so that you can perform a custom action
22221                  * when the dragged item enters the drop target by providing an implementation.
22222                  * @param {Roo.dd.DragDrop} target The drop target
22223                  * @param {Event} e The event object
22224                  * @param {String} id The id of the dragged element
22225                  * @method afterDragEnter
22226                  */
22227                 this.afterDragEnter(target, e, id);
22228             }
22229         }
22230     },
22231
22232     /**
22233      * An empty function by default, but provided so that you can perform a custom action
22234      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
22235      * @param {Roo.dd.DragDrop} target The drop target
22236      * @param {Event} e The event object
22237      * @param {String} id The id of the dragged element
22238      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
22239      */
22240     beforeDragEnter : function(target, e, id){
22241         return true;
22242     },
22243
22244     // private
22245     alignElWithMouse: function() {
22246         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
22247         this.proxy.sync();
22248     },
22249
22250     // private
22251     onDragOver : function(e, id){
22252         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
22253         if(this.beforeDragOver(target, e, id) !== false){
22254             if(target.isNotifyTarget){
22255                 var status = target.notifyOver(this, e, this.dragData);
22256                 this.proxy.setStatus(status);
22257             }
22258
22259             if(this.afterDragOver){
22260                 /**
22261                  * An empty function by default, but provided so that you can perform a custom action
22262                  * while the dragged item is over the drop target by providing an implementation.
22263                  * @param {Roo.dd.DragDrop} target The drop target
22264                  * @param {Event} e The event object
22265                  * @param {String} id The id of the dragged element
22266                  * @method afterDragOver
22267                  */
22268                 this.afterDragOver(target, e, id);
22269             }
22270         }
22271     },
22272
22273     /**
22274      * An empty function by default, but provided so that you can perform a custom action
22275      * while the dragged item is over the drop target and optionally cancel the onDragOver.
22276      * @param {Roo.dd.DragDrop} target The drop target
22277      * @param {Event} e The event object
22278      * @param {String} id The id of the dragged element
22279      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
22280      */
22281     beforeDragOver : function(target, e, id){
22282         return true;
22283     },
22284
22285     // private
22286     onDragOut : function(e, id){
22287         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
22288         if(this.beforeDragOut(target, e, id) !== false){
22289             if(target.isNotifyTarget){
22290                 target.notifyOut(this, e, this.dragData);
22291             }
22292             this.proxy.reset();
22293             if(this.afterDragOut){
22294                 /**
22295                  * An empty function by default, but provided so that you can perform a custom action
22296                  * after the dragged item is dragged out of the target without dropping.
22297                  * @param {Roo.dd.DragDrop} target The drop target
22298                  * @param {Event} e The event object
22299                  * @param {String} id The id of the dragged element
22300                  * @method afterDragOut
22301                  */
22302                 this.afterDragOut(target, e, id);
22303             }
22304         }
22305         this.cachedTarget = null;
22306     },
22307
22308     /**
22309      * An empty function by default, but provided so that you can perform a custom action before the dragged
22310      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
22311      * @param {Roo.dd.DragDrop} target The drop target
22312      * @param {Event} e The event object
22313      * @param {String} id The id of the dragged element
22314      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
22315      */
22316     beforeDragOut : function(target, e, id){
22317         return true;
22318     },
22319     
22320     // private
22321     onDragDrop : function(e, id){
22322         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
22323         if(this.beforeDragDrop(target, e, id) !== false){
22324             if(target.isNotifyTarget){
22325                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
22326                     this.onValidDrop(target, e, id);
22327                 }else{
22328                     this.onInvalidDrop(target, e, id);
22329                 }
22330             }else{
22331                 this.onValidDrop(target, e, id);
22332             }
22333             
22334             if(this.afterDragDrop){
22335                 /**
22336                  * An empty function by default, but provided so that you can perform a custom action
22337                  * after a valid drag drop has occurred by providing an implementation.
22338                  * @param {Roo.dd.DragDrop} target The drop target
22339                  * @param {Event} e The event object
22340                  * @param {String} id The id of the dropped element
22341                  * @method afterDragDrop
22342                  */
22343                 this.afterDragDrop(target, e, id);
22344             }
22345         }
22346         delete this.cachedTarget;
22347     },
22348
22349     /**
22350      * An empty function by default, but provided so that you can perform a custom action before the dragged
22351      * item is dropped onto the target and optionally cancel the onDragDrop.
22352      * @param {Roo.dd.DragDrop} target The drop target
22353      * @param {Event} e The event object
22354      * @param {String} id The id of the dragged element
22355      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
22356      */
22357     beforeDragDrop : function(target, e, id){
22358         return true;
22359     },
22360
22361     // private
22362     onValidDrop : function(target, e, id){
22363         this.hideProxy();
22364         if(this.afterValidDrop){
22365             /**
22366              * An empty function by default, but provided so that you can perform a custom action
22367              * after a valid drop has occurred by providing an implementation.
22368              * @param {Object} target The target DD 
22369              * @param {Event} e The event object
22370              * @param {String} id The id of the dropped element
22371              * @method afterInvalidDrop
22372              */
22373             this.afterValidDrop(target, e, id);
22374         }
22375     },
22376
22377     // private
22378     getRepairXY : function(e, data){
22379         return this.el.getXY();  
22380     },
22381
22382     // private
22383     onInvalidDrop : function(target, e, id){
22384         this.beforeInvalidDrop(target, e, id);
22385         if(this.cachedTarget){
22386             if(this.cachedTarget.isNotifyTarget){
22387                 this.cachedTarget.notifyOut(this, e, this.dragData);
22388             }
22389             this.cacheTarget = null;
22390         }
22391         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
22392
22393         if(this.afterInvalidDrop){
22394             /**
22395              * An empty function by default, but provided so that you can perform a custom action
22396              * after an invalid drop has occurred by providing an implementation.
22397              * @param {Event} e The event object
22398              * @param {String} id The id of the dropped element
22399              * @method afterInvalidDrop
22400              */
22401             this.afterInvalidDrop(e, id);
22402         }
22403     },
22404
22405     // private
22406     afterRepair : function(){
22407         if(Roo.enableFx){
22408             this.el.highlight(this.hlColor || "c3daf9");
22409         }
22410         this.dragging = false;
22411     },
22412
22413     /**
22414      * An empty function by default, but provided so that you can perform a custom action after an invalid
22415      * drop has occurred.
22416      * @param {Roo.dd.DragDrop} target The drop target
22417      * @param {Event} e The event object
22418      * @param {String} id The id of the dragged element
22419      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
22420      */
22421     beforeInvalidDrop : function(target, e, id){
22422         return true;
22423     },
22424
22425     // private
22426     handleMouseDown : function(e){
22427         if(this.dragging) {
22428             return;
22429         }
22430         var data = this.getDragData(e);
22431         if(data && this.onBeforeDrag(data, e) !== false){
22432             this.dragData = data;
22433             this.proxy.stop();
22434             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
22435         } 
22436     },
22437
22438     /**
22439      * An empty function by default, but provided so that you can perform a custom action before the initial
22440      * drag event begins and optionally cancel it.
22441      * @param {Object} data An object containing arbitrary data to be shared with drop targets
22442      * @param {Event} e The event object
22443      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
22444      */
22445     onBeforeDrag : function(data, e){
22446         return true;
22447     },
22448
22449     /**
22450      * An empty function by default, but provided so that you can perform a custom action once the initial
22451      * drag event has begun.  The drag cannot be canceled from this function.
22452      * @param {Number} x The x position of the click on the dragged object
22453      * @param {Number} y The y position of the click on the dragged object
22454      */
22455     onStartDrag : Roo.emptyFn,
22456
22457     // private - YUI override
22458     startDrag : function(x, y){
22459         this.proxy.reset();
22460         this.dragging = true;
22461         this.proxy.update("");
22462         this.onInitDrag(x, y);
22463         this.proxy.show();
22464     },
22465
22466     // private
22467     onInitDrag : function(x, y){
22468         var clone = this.el.dom.cloneNode(true);
22469         clone.id = Roo.id(); // prevent duplicate ids
22470         this.proxy.update(clone);
22471         this.onStartDrag(x, y);
22472         return true;
22473     },
22474
22475     /**
22476      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
22477      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
22478      */
22479     getProxy : function(){
22480         return this.proxy;  
22481     },
22482
22483     /**
22484      * Hides the drag source's {@link Roo.dd.StatusProxy}
22485      */
22486     hideProxy : function(){
22487         this.proxy.hide();  
22488         this.proxy.reset(true);
22489         this.dragging = false;
22490     },
22491
22492     // private
22493     triggerCacheRefresh : function(){
22494         Roo.dd.DDM.refreshCache(this.groups);
22495     },
22496
22497     // private - override to prevent hiding
22498     b4EndDrag: function(e) {
22499     },
22500
22501     // private - override to prevent moving
22502     endDrag : function(e){
22503         this.onEndDrag(this.dragData, e);
22504     },
22505
22506     // private
22507     onEndDrag : function(data, e){
22508     },
22509     
22510     // private - pin to cursor
22511     autoOffset : function(x, y) {
22512         this.setDelta(-12, -20);
22513     }    
22514 });/*
22515  * Based on:
22516  * Ext JS Library 1.1.1
22517  * Copyright(c) 2006-2007, Ext JS, LLC.
22518  *
22519  * Originally Released Under LGPL - original licence link has changed is not relivant.
22520  *
22521  * Fork - LGPL
22522  * <script type="text/javascript">
22523  */
22524
22525
22526 /**
22527  * @class Roo.dd.DropTarget
22528  * @extends Roo.dd.DDTarget
22529  * A simple class that provides the basic implementation needed to make any element a drop target that can have
22530  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
22531  * @constructor
22532  * @param {String/HTMLElement/Element} el The container element
22533  * @param {Object} config
22534  */
22535 Roo.dd.DropTarget = function(el, config){
22536     this.el = Roo.get(el);
22537     
22538     var listeners = false; ;
22539     if (config && config.listeners) {
22540         listeners= config.listeners;
22541         delete config.listeners;
22542     }
22543     Roo.apply(this, config);
22544     
22545     if(this.containerScroll){
22546         Roo.dd.ScrollManager.register(this.el);
22547     }
22548     this.addEvents( {
22549          /**
22550          * @scope Roo.dd.DropTarget
22551          */
22552          
22553          /**
22554          * @event enter
22555          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
22556          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
22557          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
22558          * 
22559          * IMPORTANT : it should set this.overClass and this.dropAllowed
22560          * 
22561          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
22562          * @param {Event} e The event
22563          * @param {Object} data An object containing arbitrary data supplied by the drag source
22564          */
22565         "enter" : true,
22566         
22567          /**
22568          * @event over
22569          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
22570          * This method will be called on every mouse movement while the drag source is over the drop target.
22571          * This default implementation simply returns the dropAllowed config value.
22572          * 
22573          * IMPORTANT : it should set this.dropAllowed
22574          * 
22575          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
22576          * @param {Event} e The event
22577          * @param {Object} data An object containing arbitrary data supplied by the drag source
22578          
22579          */
22580         "over" : true,
22581         /**
22582          * @event out
22583          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
22584          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
22585          * overClass (if any) from the drop element.
22586          * 
22587          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
22588          * @param {Event} e The event
22589          * @param {Object} data An object containing arbitrary data supplied by the drag source
22590          */
22591          "out" : true,
22592          
22593         /**
22594          * @event drop
22595          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
22596          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
22597          * implementation that does something to process the drop event and returns true so that the drag source's
22598          * repair action does not run.
22599          * 
22600          * IMPORTANT : it should set this.success
22601          * 
22602          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
22603          * @param {Event} e The event
22604          * @param {Object} data An object containing arbitrary data supplied by the drag source
22605         */
22606          "drop" : true
22607     });
22608             
22609      
22610     Roo.dd.DropTarget.superclass.constructor.call(  this, 
22611         this.el.dom, 
22612         this.ddGroup || this.group,
22613         {
22614             isTarget: true,
22615             listeners : listeners || {} 
22616            
22617         
22618         }
22619     );
22620
22621 };
22622
22623 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
22624     /**
22625      * @cfg {String} overClass
22626      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
22627      */
22628      /**
22629      * @cfg {String} ddGroup
22630      * The drag drop group to handle drop events for
22631      */
22632      
22633     /**
22634      * @cfg {String} dropAllowed
22635      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
22636      */
22637     dropAllowed : "x-dd-drop-ok",
22638     /**
22639      * @cfg {String} dropNotAllowed
22640      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
22641      */
22642     dropNotAllowed : "x-dd-drop-nodrop",
22643     /**
22644      * @cfg {boolean} success
22645      * set this after drop listener.. 
22646      */
22647     success : false,
22648     /**
22649      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
22650      * if the drop point is valid for over/enter..
22651      */
22652     valid : false,
22653     // private
22654     isTarget : true,
22655
22656     // private
22657     isNotifyTarget : true,
22658     
22659     /**
22660      * @hide
22661      */
22662     notifyEnter : function(dd, e, data)
22663     {
22664         this.valid = true;
22665         this.fireEvent('enter', dd, e, data);
22666         if(this.overClass){
22667             this.el.addClass(this.overClass);
22668         }
22669         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
22670             this.valid ? this.dropAllowed : this.dropNotAllowed
22671         );
22672     },
22673
22674     /**
22675      * @hide
22676      */
22677     notifyOver : function(dd, e, data)
22678     {
22679         this.valid = true;
22680         this.fireEvent('over', dd, e, data);
22681         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
22682             this.valid ? this.dropAllowed : this.dropNotAllowed
22683         );
22684     },
22685
22686     /**
22687      * @hide
22688      */
22689     notifyOut : function(dd, e, data)
22690     {
22691         this.fireEvent('out', dd, e, data);
22692         if(this.overClass){
22693             this.el.removeClass(this.overClass);
22694         }
22695     },
22696
22697     /**
22698      * @hide
22699      */
22700     notifyDrop : function(dd, e, data)
22701     {
22702         this.success = false;
22703         this.fireEvent('drop', dd, e, data);
22704         return this.success;
22705     }
22706 });/*
22707  * Based on:
22708  * Ext JS Library 1.1.1
22709  * Copyright(c) 2006-2007, Ext JS, LLC.
22710  *
22711  * Originally Released Under LGPL - original licence link has changed is not relivant.
22712  *
22713  * Fork - LGPL
22714  * <script type="text/javascript">
22715  */
22716
22717
22718 /**
22719  * @class Roo.dd.DragZone
22720  * @extends Roo.dd.DragSource
22721  * This class provides a container DD instance that proxies for multiple child node sources.<br />
22722  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
22723  * @constructor
22724  * @param {String/HTMLElement/Element} el The container element
22725  * @param {Object} config
22726  */
22727 Roo.dd.DragZone = function(el, config){
22728     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
22729     if(this.containerScroll){
22730         Roo.dd.ScrollManager.register(this.el);
22731     }
22732 };
22733
22734 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
22735     /**
22736      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
22737      * for auto scrolling during drag operations.
22738      */
22739     /**
22740      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
22741      * method after a failed drop (defaults to "c3daf9" - light blue)
22742      */
22743
22744     /**
22745      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
22746      * for a valid target to drag based on the mouse down. Override this method
22747      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
22748      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
22749      * @param {EventObject} e The mouse down event
22750      * @return {Object} The dragData
22751      */
22752     getDragData : function(e){
22753         return Roo.dd.Registry.getHandleFromEvent(e);
22754     },
22755     
22756     /**
22757      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
22758      * this.dragData.ddel
22759      * @param {Number} x The x position of the click on the dragged object
22760      * @param {Number} y The y position of the click on the dragged object
22761      * @return {Boolean} true to continue the drag, false to cancel
22762      */
22763     onInitDrag : function(x, y){
22764         this.proxy.update(this.dragData.ddel.cloneNode(true));
22765         this.onStartDrag(x, y);
22766         return true;
22767     },
22768     
22769     /**
22770      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
22771      */
22772     afterRepair : function(){
22773         if(Roo.enableFx){
22774             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
22775         }
22776         this.dragging = false;
22777     },
22778
22779     /**
22780      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
22781      * the XY of this.dragData.ddel
22782      * @param {EventObject} e The mouse up event
22783      * @return {Array} The xy location (e.g. [100, 200])
22784      */
22785     getRepairXY : function(e){
22786         return Roo.Element.fly(this.dragData.ddel).getXY();  
22787     }
22788 });/*
22789  * Based on:
22790  * Ext JS Library 1.1.1
22791  * Copyright(c) 2006-2007, Ext JS, LLC.
22792  *
22793  * Originally Released Under LGPL - original licence link has changed is not relivant.
22794  *
22795  * Fork - LGPL
22796  * <script type="text/javascript">
22797  */
22798 /**
22799  * @class Roo.dd.DropZone
22800  * @extends Roo.dd.DropTarget
22801  * This class provides a container DD instance that proxies for multiple child node targets.<br />
22802  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
22803  * @constructor
22804  * @param {String/HTMLElement/Element} el The container element
22805  * @param {Object} config
22806  */
22807 Roo.dd.DropZone = function(el, config){
22808     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
22809 };
22810
22811 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
22812     /**
22813      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
22814      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
22815      * provide your own custom lookup.
22816      * @param {Event} e The event
22817      * @return {Object} data The custom data
22818      */
22819     getTargetFromEvent : function(e){
22820         return Roo.dd.Registry.getTargetFromEvent(e);
22821     },
22822
22823     /**
22824      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
22825      * that it has registered.  This method has no default implementation and should be overridden to provide
22826      * node-specific processing if necessary.
22827      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
22828      * {@link #getTargetFromEvent} for this node)
22829      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22830      * @param {Event} e The event
22831      * @param {Object} data An object containing arbitrary data supplied by the drag source
22832      */
22833     onNodeEnter : function(n, dd, e, data){
22834         
22835     },
22836
22837     /**
22838      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
22839      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
22840      * overridden to provide the proper feedback.
22841      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
22842      * {@link #getTargetFromEvent} for this node)
22843      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22844      * @param {Event} e The event
22845      * @param {Object} data An object containing arbitrary data supplied by the drag source
22846      * @return {String} status The CSS class that communicates the drop status back to the source so that the
22847      * underlying {@link Roo.dd.StatusProxy} can be updated
22848      */
22849     onNodeOver : function(n, dd, e, data){
22850         return this.dropAllowed;
22851     },
22852
22853     /**
22854      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
22855      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
22856      * node-specific processing if necessary.
22857      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
22858      * {@link #getTargetFromEvent} for this node)
22859      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22860      * @param {Event} e The event
22861      * @param {Object} data An object containing arbitrary data supplied by the drag source
22862      */
22863     onNodeOut : function(n, dd, e, data){
22864         
22865     },
22866
22867     /**
22868      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
22869      * the drop node.  The default implementation returns false, so it should be overridden to provide the
22870      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
22871      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
22872      * {@link #getTargetFromEvent} for this node)
22873      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22874      * @param {Event} e The event
22875      * @param {Object} data An object containing arbitrary data supplied by the drag source
22876      * @return {Boolean} True if the drop was valid, else false
22877      */
22878     onNodeDrop : function(n, dd, e, data){
22879         return false;
22880     },
22881
22882     /**
22883      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
22884      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
22885      * it should be overridden to provide the proper feedback if necessary.
22886      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22887      * @param {Event} e The event
22888      * @param {Object} data An object containing arbitrary data supplied by the drag source
22889      * @return {String} status The CSS class that communicates the drop status back to the source so that the
22890      * underlying {@link Roo.dd.StatusProxy} can be updated
22891      */
22892     onContainerOver : function(dd, e, data){
22893         return this.dropNotAllowed;
22894     },
22895
22896     /**
22897      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
22898      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
22899      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
22900      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
22901      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22902      * @param {Event} e The event
22903      * @param {Object} data An object containing arbitrary data supplied by the drag source
22904      * @return {Boolean} True if the drop was valid, else false
22905      */
22906     onContainerDrop : function(dd, e, data){
22907         return false;
22908     },
22909
22910     /**
22911      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
22912      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
22913      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
22914      * you should override this method and provide a custom implementation.
22915      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22916      * @param {Event} e The event
22917      * @param {Object} data An object containing arbitrary data supplied by the drag source
22918      * @return {String} status The CSS class that communicates the drop status back to the source so that the
22919      * underlying {@link Roo.dd.StatusProxy} can be updated
22920      */
22921     notifyEnter : function(dd, e, data){
22922         return this.dropNotAllowed;
22923     },
22924
22925     /**
22926      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
22927      * This method will be called on every mouse movement while the drag source is over the drop zone.
22928      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
22929      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
22930      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
22931      * registered node, it will call {@link #onContainerOver}.
22932      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22933      * @param {Event} e The event
22934      * @param {Object} data An object containing arbitrary data supplied by the drag source
22935      * @return {String} status The CSS class that communicates the drop status back to the source so that the
22936      * underlying {@link Roo.dd.StatusProxy} can be updated
22937      */
22938     notifyOver : function(dd, e, data){
22939         var n = this.getTargetFromEvent(e);
22940         if(!n){ // not over valid drop target
22941             if(this.lastOverNode){
22942                 this.onNodeOut(this.lastOverNode, dd, e, data);
22943                 this.lastOverNode = null;
22944             }
22945             return this.onContainerOver(dd, e, data);
22946         }
22947         if(this.lastOverNode != n){
22948             if(this.lastOverNode){
22949                 this.onNodeOut(this.lastOverNode, dd, e, data);
22950             }
22951             this.onNodeEnter(n, dd, e, data);
22952             this.lastOverNode = n;
22953         }
22954         return this.onNodeOver(n, dd, e, data);
22955     },
22956
22957     /**
22958      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
22959      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
22960      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
22961      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
22962      * @param {Event} e The event
22963      * @param {Object} data An object containing arbitrary data supplied by the drag zone
22964      */
22965     notifyOut : function(dd, e, data){
22966         if(this.lastOverNode){
22967             this.onNodeOut(this.lastOverNode, dd, e, data);
22968             this.lastOverNode = null;
22969         }
22970     },
22971
22972     /**
22973      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
22974      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
22975      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
22976      * otherwise it will call {@link #onContainerDrop}.
22977      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22978      * @param {Event} e The event
22979      * @param {Object} data An object containing arbitrary data supplied by the drag source
22980      * @return {Boolean} True if the drop was valid, else false
22981      */
22982     notifyDrop : function(dd, e, data){
22983         if(this.lastOverNode){
22984             this.onNodeOut(this.lastOverNode, dd, e, data);
22985             this.lastOverNode = null;
22986         }
22987         var n = this.getTargetFromEvent(e);
22988         return n ?
22989             this.onNodeDrop(n, dd, e, data) :
22990             this.onContainerDrop(dd, e, data);
22991     },
22992
22993     // private
22994     triggerCacheRefresh : function(){
22995         Roo.dd.DDM.refreshCache(this.groups);
22996     }  
22997 });/*
22998  * Based on:
22999  * Ext JS Library 1.1.1
23000  * Copyright(c) 2006-2007, Ext JS, LLC.
23001  *
23002  * Originally Released Under LGPL - original licence link has changed is not relivant.
23003  *
23004  * Fork - LGPL
23005  * <script type="text/javascript">
23006  */
23007
23008
23009 /**
23010  * @class Roo.data.SortTypes
23011  * @singleton
23012  * Defines the default sorting (casting?) comparison functions used when sorting data.
23013  */
23014 Roo.data.SortTypes = {
23015     /**
23016      * Default sort that does nothing
23017      * @param {Mixed} s The value being converted
23018      * @return {Mixed} The comparison value
23019      */
23020     none : function(s){
23021         return s;
23022     },
23023     
23024     /**
23025      * The regular expression used to strip tags
23026      * @type {RegExp}
23027      * @property
23028      */
23029     stripTagsRE : /<\/?[^>]+>/gi,
23030     
23031     /**
23032      * Strips all HTML tags to sort on text only
23033      * @param {Mixed} s The value being converted
23034      * @return {String} The comparison value
23035      */
23036     asText : function(s){
23037         return String(s).replace(this.stripTagsRE, "");
23038     },
23039     
23040     /**
23041      * Strips all HTML tags to sort on text only - Case insensitive
23042      * @param {Mixed} s The value being converted
23043      * @return {String} The comparison value
23044      */
23045     asUCText : function(s){
23046         return String(s).toUpperCase().replace(this.stripTagsRE, "");
23047     },
23048     
23049     /**
23050      * Case insensitive string
23051      * @param {Mixed} s The value being converted
23052      * @return {String} The comparison value
23053      */
23054     asUCString : function(s) {
23055         return String(s).toUpperCase();
23056     },
23057     
23058     /**
23059      * Date sorting
23060      * @param {Mixed} s The value being converted
23061      * @return {Number} The comparison value
23062      */
23063     asDate : function(s) {
23064         if(!s){
23065             return 0;
23066         }
23067         if(s instanceof Date){
23068             return s.getTime();
23069         }
23070         return Date.parse(String(s));
23071     },
23072     
23073     /**
23074      * Float sorting
23075      * @param {Mixed} s The value being converted
23076      * @return {Float} The comparison value
23077      */
23078     asFloat : function(s) {
23079         var val = parseFloat(String(s).replace(/,/g, ""));
23080         if(isNaN(val)) {
23081             val = 0;
23082         }
23083         return val;
23084     },
23085     
23086     /**
23087      * Integer sorting
23088      * @param {Mixed} s The value being converted
23089      * @return {Number} The comparison value
23090      */
23091     asInt : function(s) {
23092         var val = parseInt(String(s).replace(/,/g, ""));
23093         if(isNaN(val)) {
23094             val = 0;
23095         }
23096         return val;
23097     }
23098 };/*
23099  * Based on:
23100  * Ext JS Library 1.1.1
23101  * Copyright(c) 2006-2007, Ext JS, LLC.
23102  *
23103  * Originally Released Under LGPL - original licence link has changed is not relivant.
23104  *
23105  * Fork - LGPL
23106  * <script type="text/javascript">
23107  */
23108
23109 /**
23110 * @class Roo.data.Record
23111  * Instances of this class encapsulate both record <em>definition</em> information, and record
23112  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
23113  * to access Records cached in an {@link Roo.data.Store} object.<br>
23114  * <p>
23115  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
23116  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
23117  * objects.<br>
23118  * <p>
23119  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
23120  * @constructor
23121  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
23122  * {@link #create}. The parameters are the same.
23123  * @param {Array} data An associative Array of data values keyed by the field name.
23124  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
23125  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
23126  * not specified an integer id is generated.
23127  */
23128 Roo.data.Record = function(data, id){
23129     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
23130     this.data = data;
23131 };
23132
23133 /**
23134  * Generate a constructor for a specific record layout.
23135  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
23136  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
23137  * Each field definition object may contain the following properties: <ul>
23138  * <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,
23139  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
23140  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
23141  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
23142  * is being used, then this is a string containing the javascript expression to reference the data relative to 
23143  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
23144  * to the data item relative to the record element. If the mapping expression is the same as the field name,
23145  * this may be omitted.</p></li>
23146  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
23147  * <ul><li>auto (Default, implies no conversion)</li>
23148  * <li>string</li>
23149  * <li>int</li>
23150  * <li>float</li>
23151  * <li>boolean</li>
23152  * <li>date</li></ul></p></li>
23153  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
23154  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
23155  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
23156  * by the Reader into an object that will be stored in the Record. It is passed the
23157  * following parameters:<ul>
23158  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
23159  * </ul></p></li>
23160  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
23161  * </ul>
23162  * <br>usage:<br><pre><code>
23163 var TopicRecord = Roo.data.Record.create(
23164     {name: 'title', mapping: 'topic_title'},
23165     {name: 'author', mapping: 'username'},
23166     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
23167     {name: 'lastPost', mapping: 'post_time', type: 'date'},
23168     {name: 'lastPoster', mapping: 'user2'},
23169     {name: 'excerpt', mapping: 'post_text'}
23170 );
23171
23172 var myNewRecord = new TopicRecord({
23173     title: 'Do my job please',
23174     author: 'noobie',
23175     totalPosts: 1,
23176     lastPost: new Date(),
23177     lastPoster: 'Animal',
23178     excerpt: 'No way dude!'
23179 });
23180 myStore.add(myNewRecord);
23181 </code></pre>
23182  * @method create
23183  * @static
23184  */
23185 Roo.data.Record.create = function(o){
23186     var f = function(){
23187         f.superclass.constructor.apply(this, arguments);
23188     };
23189     Roo.extend(f, Roo.data.Record);
23190     var p = f.prototype;
23191     p.fields = new Roo.util.MixedCollection(false, function(field){
23192         return field.name;
23193     });
23194     for(var i = 0, len = o.length; i < len; i++){
23195         p.fields.add(new Roo.data.Field(o[i]));
23196     }
23197     f.getField = function(name){
23198         return p.fields.get(name);  
23199     };
23200     return f;
23201 };
23202
23203 Roo.data.Record.AUTO_ID = 1000;
23204 Roo.data.Record.EDIT = 'edit';
23205 Roo.data.Record.REJECT = 'reject';
23206 Roo.data.Record.COMMIT = 'commit';
23207
23208 Roo.data.Record.prototype = {
23209     /**
23210      * Readonly flag - true if this record has been modified.
23211      * @type Boolean
23212      */
23213     dirty : false,
23214     editing : false,
23215     error: null,
23216     modified: null,
23217
23218     // private
23219     join : function(store){
23220         this.store = store;
23221     },
23222
23223     /**
23224      * Set the named field to the specified value.
23225      * @param {String} name The name of the field to set.
23226      * @param {Object} value The value to set the field to.
23227      */
23228     set : function(name, value){
23229         if(this.data[name] == value){
23230             return;
23231         }
23232         this.dirty = true;
23233         if(!this.modified){
23234             this.modified = {};
23235         }
23236         if(typeof this.modified[name] == 'undefined'){
23237             this.modified[name] = this.data[name];
23238         }
23239         this.data[name] = value;
23240         if(!this.editing && this.store){
23241             this.store.afterEdit(this);
23242         }       
23243     },
23244
23245     /**
23246      * Get the value of the named field.
23247      * @param {String} name The name of the field to get the value of.
23248      * @return {Object} The value of the field.
23249      */
23250     get : function(name){
23251         return this.data[name]; 
23252     },
23253
23254     // private
23255     beginEdit : function(){
23256         this.editing = true;
23257         this.modified = {}; 
23258     },
23259
23260     // private
23261     cancelEdit : function(){
23262         this.editing = false;
23263         delete this.modified;
23264     },
23265
23266     // private
23267     endEdit : function(){
23268         this.editing = false;
23269         if(this.dirty && this.store){
23270             this.store.afterEdit(this);
23271         }
23272     },
23273
23274     /**
23275      * Usually called by the {@link Roo.data.Store} which owns the Record.
23276      * Rejects all changes made to the Record since either creation, or the last commit operation.
23277      * Modified fields are reverted to their original values.
23278      * <p>
23279      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
23280      * of reject operations.
23281      */
23282     reject : function(){
23283         var m = this.modified;
23284         for(var n in m){
23285             if(typeof m[n] != "function"){
23286                 this.data[n] = m[n];
23287             }
23288         }
23289         this.dirty = false;
23290         delete this.modified;
23291         this.editing = false;
23292         if(this.store){
23293             this.store.afterReject(this);
23294         }
23295     },
23296
23297     /**
23298      * Usually called by the {@link Roo.data.Store} which owns the Record.
23299      * Commits all changes made to the Record since either creation, or the last commit operation.
23300      * <p>
23301      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
23302      * of commit operations.
23303      */
23304     commit : function(){
23305         this.dirty = false;
23306         delete this.modified;
23307         this.editing = false;
23308         if(this.store){
23309             this.store.afterCommit(this);
23310         }
23311     },
23312
23313     // private
23314     hasError : function(){
23315         return this.error != null;
23316     },
23317
23318     // private
23319     clearError : function(){
23320         this.error = null;
23321     },
23322
23323     /**
23324      * Creates a copy of this record.
23325      * @param {String} id (optional) A new record id if you don't want to use this record's id
23326      * @return {Record}
23327      */
23328     copy : function(newId) {
23329         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
23330     }
23331 };/*
23332  * Based on:
23333  * Ext JS Library 1.1.1
23334  * Copyright(c) 2006-2007, Ext JS, LLC.
23335  *
23336  * Originally Released Under LGPL - original licence link has changed is not relivant.
23337  *
23338  * Fork - LGPL
23339  * <script type="text/javascript">
23340  */
23341
23342
23343
23344 /**
23345  * @class Roo.data.Store
23346  * @extends Roo.util.Observable
23347  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
23348  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
23349  * <p>
23350  * 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
23351  * has no knowledge of the format of the data returned by the Proxy.<br>
23352  * <p>
23353  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
23354  * instances from the data object. These records are cached and made available through accessor functions.
23355  * @constructor
23356  * Creates a new Store.
23357  * @param {Object} config A config object containing the objects needed for the Store to access data,
23358  * and read the data into Records.
23359  */
23360 Roo.data.Store = function(config){
23361     this.data = new Roo.util.MixedCollection(false);
23362     this.data.getKey = function(o){
23363         return o.id;
23364     };
23365     this.baseParams = {};
23366     // private
23367     this.paramNames = {
23368         "start" : "start",
23369         "limit" : "limit",
23370         "sort" : "sort",
23371         "dir" : "dir",
23372         "multisort" : "_multisort"
23373     };
23374
23375     if(config && config.data){
23376         this.inlineData = config.data;
23377         delete config.data;
23378     }
23379
23380     Roo.apply(this, config);
23381     
23382     if(this.reader){ // reader passed
23383         this.reader = Roo.factory(this.reader, Roo.data);
23384         this.reader.xmodule = this.xmodule || false;
23385         if(!this.recordType){
23386             this.recordType = this.reader.recordType;
23387         }
23388         if(this.reader.onMetaChange){
23389             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
23390         }
23391     }
23392
23393     if(this.recordType){
23394         this.fields = this.recordType.prototype.fields;
23395     }
23396     this.modified = [];
23397
23398     this.addEvents({
23399         /**
23400          * @event datachanged
23401          * Fires when the data cache has changed, and a widget which is using this Store
23402          * as a Record cache should refresh its view.
23403          * @param {Store} this
23404          */
23405         datachanged : true,
23406         /**
23407          * @event metachange
23408          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
23409          * @param {Store} this
23410          * @param {Object} meta The JSON metadata
23411          */
23412         metachange : true,
23413         /**
23414          * @event add
23415          * Fires when Records have been added to the Store
23416          * @param {Store} this
23417          * @param {Roo.data.Record[]} records The array of Records added
23418          * @param {Number} index The index at which the record(s) were added
23419          */
23420         add : true,
23421         /**
23422          * @event remove
23423          * Fires when a Record has been removed from the Store
23424          * @param {Store} this
23425          * @param {Roo.data.Record} record The Record that was removed
23426          * @param {Number} index The index at which the record was removed
23427          */
23428         remove : true,
23429         /**
23430          * @event update
23431          * Fires when a Record has been updated
23432          * @param {Store} this
23433          * @param {Roo.data.Record} record The Record that was updated
23434          * @param {String} operation The update operation being performed.  Value may be one of:
23435          * <pre><code>
23436  Roo.data.Record.EDIT
23437  Roo.data.Record.REJECT
23438  Roo.data.Record.COMMIT
23439          * </code></pre>
23440          */
23441         update : true,
23442         /**
23443          * @event clear
23444          * Fires when the data cache has been cleared.
23445          * @param {Store} this
23446          */
23447         clear : true,
23448         /**
23449          * @event beforeload
23450          * Fires before a request is made for a new data object.  If the beforeload handler returns false
23451          * the load action will be canceled.
23452          * @param {Store} this
23453          * @param {Object} options The loading options that were specified (see {@link #load} for details)
23454          */
23455         beforeload : true,
23456         /**
23457          * @event beforeloadadd
23458          * Fires after a new set of Records has been loaded.
23459          * @param {Store} this
23460          * @param {Roo.data.Record[]} records The Records that were loaded
23461          * @param {Object} options The loading options that were specified (see {@link #load} for details)
23462          */
23463         beforeloadadd : true,
23464         /**
23465          * @event load
23466          * Fires after a new set of Records has been loaded, before they are added to the store.
23467          * @param {Store} this
23468          * @param {Roo.data.Record[]} records The Records that were loaded
23469          * @param {Object} options The loading options that were specified (see {@link #load} for details)
23470          * @params {Object} return from reader
23471          */
23472         load : true,
23473         /**
23474          * @event loadexception
23475          * Fires if an exception occurs in the Proxy during loading.
23476          * Called with the signature of the Proxy's "loadexception" event.
23477          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
23478          * 
23479          * @param {Proxy} 
23480          * @param {Object} return from JsonData.reader() - success, totalRecords, records
23481          * @param {Object} load options 
23482          * @param {Object} jsonData from your request (normally this contains the Exception)
23483          */
23484         loadexception : true
23485     });
23486     
23487     if(this.proxy){
23488         this.proxy = Roo.factory(this.proxy, Roo.data);
23489         this.proxy.xmodule = this.xmodule || false;
23490         this.relayEvents(this.proxy,  ["loadexception"]);
23491     }
23492     this.sortToggle = {};
23493     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
23494
23495     Roo.data.Store.superclass.constructor.call(this);
23496
23497     if(this.inlineData){
23498         this.loadData(this.inlineData);
23499         delete this.inlineData;
23500     }
23501 };
23502
23503 Roo.extend(Roo.data.Store, Roo.util.Observable, {
23504      /**
23505     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
23506     * without a remote query - used by combo/forms at present.
23507     */
23508     
23509     /**
23510     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
23511     */
23512     /**
23513     * @cfg {Array} data Inline data to be loaded when the store is initialized.
23514     */
23515     /**
23516     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
23517     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
23518     */
23519     /**
23520     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
23521     * on any HTTP request
23522     */
23523     /**
23524     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
23525     */
23526     /**
23527     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
23528     */
23529     multiSort: false,
23530     /**
23531     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
23532     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
23533     */
23534     remoteSort : false,
23535
23536     /**
23537     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
23538      * loaded or when a record is removed. (defaults to false).
23539     */
23540     pruneModifiedRecords : false,
23541
23542     // private
23543     lastOptions : null,
23544
23545     /**
23546      * Add Records to the Store and fires the add event.
23547      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
23548      */
23549     add : function(records){
23550         records = [].concat(records);
23551         for(var i = 0, len = records.length; i < len; i++){
23552             records[i].join(this);
23553         }
23554         var index = this.data.length;
23555         this.data.addAll(records);
23556         this.fireEvent("add", this, records, index);
23557     },
23558
23559     /**
23560      * Remove a Record from the Store and fires the remove event.
23561      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
23562      */
23563     remove : function(record){
23564         var index = this.data.indexOf(record);
23565         this.data.removeAt(index);
23566  
23567         if(this.pruneModifiedRecords){
23568             this.modified.remove(record);
23569         }
23570         this.fireEvent("remove", this, record, index);
23571     },
23572
23573     /**
23574      * Remove all Records from the Store and fires the clear event.
23575      */
23576     removeAll : function(){
23577         this.data.clear();
23578         if(this.pruneModifiedRecords){
23579             this.modified = [];
23580         }
23581         this.fireEvent("clear", this);
23582     },
23583
23584     /**
23585      * Inserts Records to the Store at the given index and fires the add event.
23586      * @param {Number} index The start index at which to insert the passed Records.
23587      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
23588      */
23589     insert : function(index, records){
23590         records = [].concat(records);
23591         for(var i = 0, len = records.length; i < len; i++){
23592             this.data.insert(index, records[i]);
23593             records[i].join(this);
23594         }
23595         this.fireEvent("add", this, records, index);
23596     },
23597
23598     /**
23599      * Get the index within the cache of the passed Record.
23600      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
23601      * @return {Number} The index of the passed Record. Returns -1 if not found.
23602      */
23603     indexOf : function(record){
23604         return this.data.indexOf(record);
23605     },
23606
23607     /**
23608      * Get the index within the cache of the Record with the passed id.
23609      * @param {String} id The id of the Record to find.
23610      * @return {Number} The index of the Record. Returns -1 if not found.
23611      */
23612     indexOfId : function(id){
23613         return this.data.indexOfKey(id);
23614     },
23615
23616     /**
23617      * Get the Record with the specified id.
23618      * @param {String} id The id of the Record to find.
23619      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
23620      */
23621     getById : function(id){
23622         return this.data.key(id);
23623     },
23624
23625     /**
23626      * Get the Record at the specified index.
23627      * @param {Number} index The index of the Record to find.
23628      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
23629      */
23630     getAt : function(index){
23631         return this.data.itemAt(index);
23632     },
23633
23634     /**
23635      * Returns a range of Records between specified indices.
23636      * @param {Number} startIndex (optional) The starting index (defaults to 0)
23637      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
23638      * @return {Roo.data.Record[]} An array of Records
23639      */
23640     getRange : function(start, end){
23641         return this.data.getRange(start, end);
23642     },
23643
23644     // private
23645     storeOptions : function(o){
23646         o = Roo.apply({}, o);
23647         delete o.callback;
23648         delete o.scope;
23649         this.lastOptions = o;
23650     },
23651
23652     /**
23653      * Loads the Record cache from the configured Proxy using the configured Reader.
23654      * <p>
23655      * If using remote paging, then the first load call must specify the <em>start</em>
23656      * and <em>limit</em> properties in the options.params property to establish the initial
23657      * position within the dataset, and the number of Records to cache on each read from the Proxy.
23658      * <p>
23659      * <strong>It is important to note that for remote data sources, loading is asynchronous,
23660      * and this call will return before the new data has been loaded. Perform any post-processing
23661      * in a callback function, or in a "load" event handler.</strong>
23662      * <p>
23663      * @param {Object} options An object containing properties which control loading options:<ul>
23664      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
23665      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
23666      * passed the following arguments:<ul>
23667      * <li>r : Roo.data.Record[]</li>
23668      * <li>options: Options object from the load call</li>
23669      * <li>success: Boolean success indicator</li></ul></li>
23670      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
23671      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
23672      * </ul>
23673      */
23674     load : function(options){
23675         options = options || {};
23676         if(this.fireEvent("beforeload", this, options) !== false){
23677             this.storeOptions(options);
23678             var p = Roo.apply(options.params || {}, this.baseParams);
23679             // if meta was not loaded from remote source.. try requesting it.
23680             if (!this.reader.metaFromRemote) {
23681                 p._requestMeta = 1;
23682             }
23683             if(this.sortInfo && this.remoteSort){
23684                 var pn = this.paramNames;
23685                 p[pn["sort"]] = this.sortInfo.field;
23686                 p[pn["dir"]] = this.sortInfo.direction;
23687             }
23688             if (this.multiSort) {
23689                 var pn = this.paramNames;
23690                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
23691             }
23692             
23693             this.proxy.load(p, this.reader, this.loadRecords, this, options);
23694         }
23695     },
23696
23697     /**
23698      * Reloads the Record cache from the configured Proxy using the configured Reader and
23699      * the options from the last load operation performed.
23700      * @param {Object} options (optional) An object containing properties which may override the options
23701      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
23702      * the most recently used options are reused).
23703      */
23704     reload : function(options){
23705         this.load(Roo.applyIf(options||{}, this.lastOptions));
23706     },
23707
23708     // private
23709     // Called as a callback by the Reader during a load operation.
23710     loadRecords : function(o, options, success){
23711         if(!o || success === false){
23712             if(success !== false){
23713                 this.fireEvent("load", this, [], options, o);
23714             }
23715             if(options.callback){
23716                 options.callback.call(options.scope || this, [], options, false);
23717             }
23718             return;
23719         }
23720         // if data returned failure - throw an exception.
23721         if (o.success === false) {
23722             // show a message if no listener is registered.
23723             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
23724                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
23725             }
23726             // loadmask wil be hooked into this..
23727             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
23728             return;
23729         }
23730         var r = o.records, t = o.totalRecords || r.length;
23731         
23732         this.fireEvent("beforeloadadd", this, r, options, o);
23733         
23734         if(!options || options.add !== true){
23735             if(this.pruneModifiedRecords){
23736                 this.modified = [];
23737             }
23738             for(var i = 0, len = r.length; i < len; i++){
23739                 r[i].join(this);
23740             }
23741             if(this.snapshot){
23742                 this.data = this.snapshot;
23743                 delete this.snapshot;
23744             }
23745             this.data.clear();
23746             this.data.addAll(r);
23747             this.totalLength = t;
23748             this.applySort();
23749             this.fireEvent("datachanged", this);
23750         }else{
23751             this.totalLength = Math.max(t, this.data.length+r.length);
23752             this.add(r);
23753         }
23754         
23755         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
23756                 
23757             var e = new Roo.data.Record({});
23758
23759             e.set(this.parent.displayField, this.parent.emptyTitle);
23760             e.set(this.parent.valueField, '');
23761
23762             this.insert(0, e);
23763         }
23764             
23765         this.fireEvent("load", this, r, options, o);
23766         if(options.callback){
23767             options.callback.call(options.scope || this, r, options, true);
23768         }
23769     },
23770
23771
23772     /**
23773      * Loads data from a passed data block. A Reader which understands the format of the data
23774      * must have been configured in the constructor.
23775      * @param {Object} data The data block from which to read the Records.  The format of the data expected
23776      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
23777      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
23778      */
23779     loadData : function(o, append){
23780         var r = this.reader.readRecords(o);
23781         this.loadRecords(r, {add: append}, true);
23782     },
23783     
23784      /**
23785      * using 'cn' the nested child reader read the child array into it's child stores.
23786      * @param {Object} rec The record with a 'children array
23787      */
23788     loadDataFromChildren : function(rec)
23789     {
23790         this.loadData(this.reader.toLoadData(rec));
23791     },
23792     
23793
23794     /**
23795      * Gets the number of cached records.
23796      * <p>
23797      * <em>If using paging, this may not be the total size of the dataset. If the data object
23798      * used by the Reader contains the dataset size, then the getTotalCount() function returns
23799      * the data set size</em>
23800      */
23801     getCount : function(){
23802         return this.data.length || 0;
23803     },
23804
23805     /**
23806      * Gets the total number of records in the dataset as returned by the server.
23807      * <p>
23808      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
23809      * the dataset size</em>
23810      */
23811     getTotalCount : function(){
23812         return this.totalLength || 0;
23813     },
23814
23815     /**
23816      * Returns the sort state of the Store as an object with two properties:
23817      * <pre><code>
23818  field {String} The name of the field by which the Records are sorted
23819  direction {String} The sort order, "ASC" or "DESC"
23820      * </code></pre>
23821      */
23822     getSortState : function(){
23823         return this.sortInfo;
23824     },
23825
23826     // private
23827     applySort : function(){
23828         if(this.sortInfo && !this.remoteSort){
23829             var s = this.sortInfo, f = s.field;
23830             var st = this.fields.get(f).sortType;
23831             var fn = function(r1, r2){
23832                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
23833                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
23834             };
23835             this.data.sort(s.direction, fn);
23836             if(this.snapshot && this.snapshot != this.data){
23837                 this.snapshot.sort(s.direction, fn);
23838             }
23839         }
23840     },
23841
23842     /**
23843      * Sets the default sort column and order to be used by the next load operation.
23844      * @param {String} fieldName The name of the field to sort by.
23845      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
23846      */
23847     setDefaultSort : function(field, dir){
23848         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
23849     },
23850
23851     /**
23852      * Sort the Records.
23853      * If remote sorting is used, the sort is performed on the server, and the cache is
23854      * reloaded. If local sorting is used, the cache is sorted internally.
23855      * @param {String} fieldName The name of the field to sort by.
23856      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
23857      */
23858     sort : function(fieldName, dir){
23859         var f = this.fields.get(fieldName);
23860         if(!dir){
23861             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
23862             
23863             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
23864                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
23865             }else{
23866                 dir = f.sortDir;
23867             }
23868         }
23869         this.sortToggle[f.name] = dir;
23870         this.sortInfo = {field: f.name, direction: dir};
23871         if(!this.remoteSort){
23872             this.applySort();
23873             this.fireEvent("datachanged", this);
23874         }else{
23875             this.load(this.lastOptions);
23876         }
23877     },
23878
23879     /**
23880      * Calls the specified function for each of the Records in the cache.
23881      * @param {Function} fn The function to call. The Record is passed as the first parameter.
23882      * Returning <em>false</em> aborts and exits the iteration.
23883      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
23884      */
23885     each : function(fn, scope){
23886         this.data.each(fn, scope);
23887     },
23888
23889     /**
23890      * Gets all records modified since the last commit.  Modified records are persisted across load operations
23891      * (e.g., during paging).
23892      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
23893      */
23894     getModifiedRecords : function(){
23895         return this.modified;
23896     },
23897
23898     // private
23899     createFilterFn : function(property, value, anyMatch){
23900         if(!value.exec){ // not a regex
23901             value = String(value);
23902             if(value.length == 0){
23903                 return false;
23904             }
23905             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
23906         }
23907         return function(r){
23908             return value.test(r.data[property]);
23909         };
23910     },
23911
23912     /**
23913      * Sums the value of <i>property</i> for each record between start and end and returns the result.
23914      * @param {String} property A field on your records
23915      * @param {Number} start The record index to start at (defaults to 0)
23916      * @param {Number} end The last record index to include (defaults to length - 1)
23917      * @return {Number} The sum
23918      */
23919     sum : function(property, start, end){
23920         var rs = this.data.items, v = 0;
23921         start = start || 0;
23922         end = (end || end === 0) ? end : rs.length-1;
23923
23924         for(var i = start; i <= end; i++){
23925             v += (rs[i].data[property] || 0);
23926         }
23927         return v;
23928     },
23929
23930     /**
23931      * Filter the records by a specified property.
23932      * @param {String} field A field on your records
23933      * @param {String/RegExp} value Either a string that the field
23934      * should start with or a RegExp to test against the field
23935      * @param {Boolean} anyMatch True to match any part not just the beginning
23936      */
23937     filter : function(property, value, anyMatch){
23938         var fn = this.createFilterFn(property, value, anyMatch);
23939         return fn ? this.filterBy(fn) : this.clearFilter();
23940     },
23941
23942     /**
23943      * Filter by a function. The specified function will be called with each
23944      * record in this data source. If the function returns true the record is included,
23945      * otherwise it is filtered.
23946      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
23947      * @param {Object} scope (optional) The scope of the function (defaults to this)
23948      */
23949     filterBy : function(fn, scope){
23950         this.snapshot = this.snapshot || this.data;
23951         this.data = this.queryBy(fn, scope||this);
23952         this.fireEvent("datachanged", this);
23953     },
23954
23955     /**
23956      * Query the records by a specified property.
23957      * @param {String} field A field on your records
23958      * @param {String/RegExp} value Either a string that the field
23959      * should start with or a RegExp to test against the field
23960      * @param {Boolean} anyMatch True to match any part not just the beginning
23961      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
23962      */
23963     query : function(property, value, anyMatch){
23964         var fn = this.createFilterFn(property, value, anyMatch);
23965         return fn ? this.queryBy(fn) : this.data.clone();
23966     },
23967
23968     /**
23969      * Query by a function. The specified function will be called with each
23970      * record in this data source. If the function returns true the record is included
23971      * in the results.
23972      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
23973      * @param {Object} scope (optional) The scope of the function (defaults to this)
23974       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
23975      **/
23976     queryBy : function(fn, scope){
23977         var data = this.snapshot || this.data;
23978         return data.filterBy(fn, scope||this);
23979     },
23980
23981     /**
23982      * Collects unique values for a particular dataIndex from this store.
23983      * @param {String} dataIndex The property to collect
23984      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
23985      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
23986      * @return {Array} An array of the unique values
23987      **/
23988     collect : function(dataIndex, allowNull, bypassFilter){
23989         var d = (bypassFilter === true && this.snapshot) ?
23990                 this.snapshot.items : this.data.items;
23991         var v, sv, r = [], l = {};
23992         for(var i = 0, len = d.length; i < len; i++){
23993             v = d[i].data[dataIndex];
23994             sv = String(v);
23995             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
23996                 l[sv] = true;
23997                 r[r.length] = v;
23998             }
23999         }
24000         return r;
24001     },
24002
24003     /**
24004      * Revert to a view of the Record cache with no filtering applied.
24005      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
24006      */
24007     clearFilter : function(suppressEvent){
24008         if(this.snapshot && this.snapshot != this.data){
24009             this.data = this.snapshot;
24010             delete this.snapshot;
24011             if(suppressEvent !== true){
24012                 this.fireEvent("datachanged", this);
24013             }
24014         }
24015     },
24016
24017     // private
24018     afterEdit : function(record){
24019         if(this.modified.indexOf(record) == -1){
24020             this.modified.push(record);
24021         }
24022         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
24023     },
24024     
24025     // private
24026     afterReject : function(record){
24027         this.modified.remove(record);
24028         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
24029     },
24030
24031     // private
24032     afterCommit : function(record){
24033         this.modified.remove(record);
24034         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
24035     },
24036
24037     /**
24038      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
24039      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
24040      */
24041     commitChanges : function(){
24042         var m = this.modified.slice(0);
24043         this.modified = [];
24044         for(var i = 0, len = m.length; i < len; i++){
24045             m[i].commit();
24046         }
24047     },
24048
24049     /**
24050      * Cancel outstanding changes on all changed records.
24051      */
24052     rejectChanges : function(){
24053         var m = this.modified.slice(0);
24054         this.modified = [];
24055         for(var i = 0, len = m.length; i < len; i++){
24056             m[i].reject();
24057         }
24058     },
24059
24060     onMetaChange : function(meta, rtype, o){
24061         this.recordType = rtype;
24062         this.fields = rtype.prototype.fields;
24063         delete this.snapshot;
24064         this.sortInfo = meta.sortInfo || this.sortInfo;
24065         this.modified = [];
24066         this.fireEvent('metachange', this, this.reader.meta);
24067     },
24068     
24069     moveIndex : function(data, type)
24070     {
24071         var index = this.indexOf(data);
24072         
24073         var newIndex = index + type;
24074         
24075         this.remove(data);
24076         
24077         this.insert(newIndex, data);
24078         
24079     }
24080 });/*
24081  * Based on:
24082  * Ext JS Library 1.1.1
24083  * Copyright(c) 2006-2007, Ext JS, LLC.
24084  *
24085  * Originally Released Under LGPL - original licence link has changed is not relivant.
24086  *
24087  * Fork - LGPL
24088  * <script type="text/javascript">
24089  */
24090
24091 /**
24092  * @class Roo.data.SimpleStore
24093  * @extends Roo.data.Store
24094  * Small helper class to make creating Stores from Array data easier.
24095  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
24096  * @cfg {Array} fields An array of field definition objects, or field name strings.
24097  * @cfg {Object} an existing reader (eg. copied from another store)
24098  * @cfg {Array} data The multi-dimensional array of data
24099  * @constructor
24100  * @param {Object} config
24101  */
24102 Roo.data.SimpleStore = function(config)
24103 {
24104     Roo.data.SimpleStore.superclass.constructor.call(this, {
24105         isLocal : true,
24106         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
24107                 id: config.id
24108             },
24109             Roo.data.Record.create(config.fields)
24110         ),
24111         proxy : new Roo.data.MemoryProxy(config.data)
24112     });
24113     this.load();
24114 };
24115 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
24116  * Based on:
24117  * Ext JS Library 1.1.1
24118  * Copyright(c) 2006-2007, Ext JS, LLC.
24119  *
24120  * Originally Released Under LGPL - original licence link has changed is not relivant.
24121  *
24122  * Fork - LGPL
24123  * <script type="text/javascript">
24124  */
24125
24126 /**
24127 /**
24128  * @extends Roo.data.Store
24129  * @class Roo.data.JsonStore
24130  * Small helper class to make creating Stores for JSON data easier. <br/>
24131 <pre><code>
24132 var store = new Roo.data.JsonStore({
24133     url: 'get-images.php',
24134     root: 'images',
24135     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
24136 });
24137 </code></pre>
24138  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
24139  * JsonReader and HttpProxy (unless inline data is provided).</b>
24140  * @cfg {Array} fields An array of field definition objects, or field name strings.
24141  * @constructor
24142  * @param {Object} config
24143  */
24144 Roo.data.JsonStore = function(c){
24145     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
24146         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
24147         reader: new Roo.data.JsonReader(c, c.fields)
24148     }));
24149 };
24150 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
24151  * Based on:
24152  * Ext JS Library 1.1.1
24153  * Copyright(c) 2006-2007, Ext JS, LLC.
24154  *
24155  * Originally Released Under LGPL - original licence link has changed is not relivant.
24156  *
24157  * Fork - LGPL
24158  * <script type="text/javascript">
24159  */
24160
24161  
24162 Roo.data.Field = function(config){
24163     if(typeof config == "string"){
24164         config = {name: config};
24165     }
24166     Roo.apply(this, config);
24167     
24168     if(!this.type){
24169         this.type = "auto";
24170     }
24171     
24172     var st = Roo.data.SortTypes;
24173     // named sortTypes are supported, here we look them up
24174     if(typeof this.sortType == "string"){
24175         this.sortType = st[this.sortType];
24176     }
24177     
24178     // set default sortType for strings and dates
24179     if(!this.sortType){
24180         switch(this.type){
24181             case "string":
24182                 this.sortType = st.asUCString;
24183                 break;
24184             case "date":
24185                 this.sortType = st.asDate;
24186                 break;
24187             default:
24188                 this.sortType = st.none;
24189         }
24190     }
24191
24192     // define once
24193     var stripRe = /[\$,%]/g;
24194
24195     // prebuilt conversion function for this field, instead of
24196     // switching every time we're reading a value
24197     if(!this.convert){
24198         var cv, dateFormat = this.dateFormat;
24199         switch(this.type){
24200             case "":
24201             case "auto":
24202             case undefined:
24203                 cv = function(v){ return v; };
24204                 break;
24205             case "string":
24206                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
24207                 break;
24208             case "int":
24209                 cv = function(v){
24210                     return v !== undefined && v !== null && v !== '' ?
24211                            parseInt(String(v).replace(stripRe, ""), 10) : '';
24212                     };
24213                 break;
24214             case "float":
24215                 cv = function(v){
24216                     return v !== undefined && v !== null && v !== '' ?
24217                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
24218                     };
24219                 break;
24220             case "bool":
24221             case "boolean":
24222                 cv = function(v){ return v === true || v === "true" || v == 1; };
24223                 break;
24224             case "date":
24225                 cv = function(v){
24226                     if(!v){
24227                         return '';
24228                     }
24229                     if(v instanceof Date){
24230                         return v;
24231                     }
24232                     if(dateFormat){
24233                         if(dateFormat == "timestamp"){
24234                             return new Date(v*1000);
24235                         }
24236                         return Date.parseDate(v, dateFormat);
24237                     }
24238                     var parsed = Date.parse(v);
24239                     return parsed ? new Date(parsed) : null;
24240                 };
24241              break;
24242             
24243         }
24244         this.convert = cv;
24245     }
24246 };
24247
24248 Roo.data.Field.prototype = {
24249     dateFormat: null,
24250     defaultValue: "",
24251     mapping: null,
24252     sortType : null,
24253     sortDir : "ASC"
24254 };/*
24255  * Based on:
24256  * Ext JS Library 1.1.1
24257  * Copyright(c) 2006-2007, Ext JS, LLC.
24258  *
24259  * Originally Released Under LGPL - original licence link has changed is not relivant.
24260  *
24261  * Fork - LGPL
24262  * <script type="text/javascript">
24263  */
24264  
24265 // Base class for reading structured data from a data source.  This class is intended to be
24266 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
24267
24268 /**
24269  * @class Roo.data.DataReader
24270  * Base class for reading structured data from a data source.  This class is intended to be
24271  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
24272  */
24273
24274 Roo.data.DataReader = function(meta, recordType){
24275     
24276     this.meta = meta;
24277     
24278     this.recordType = recordType instanceof Array ? 
24279         Roo.data.Record.create(recordType) : recordType;
24280 };
24281
24282 Roo.data.DataReader.prototype = {
24283     
24284     
24285     readerType : 'Data',
24286      /**
24287      * Create an empty record
24288      * @param {Object} data (optional) - overlay some values
24289      * @return {Roo.data.Record} record created.
24290      */
24291     newRow :  function(d) {
24292         var da =  {};
24293         this.recordType.prototype.fields.each(function(c) {
24294             switch( c.type) {
24295                 case 'int' : da[c.name] = 0; break;
24296                 case 'date' : da[c.name] = new Date(); break;
24297                 case 'float' : da[c.name] = 0.0; break;
24298                 case 'boolean' : da[c.name] = false; break;
24299                 default : da[c.name] = ""; break;
24300             }
24301             
24302         });
24303         return new this.recordType(Roo.apply(da, d));
24304     }
24305     
24306     
24307 };/*
24308  * Based on:
24309  * Ext JS Library 1.1.1
24310  * Copyright(c) 2006-2007, Ext JS, LLC.
24311  *
24312  * Originally Released Under LGPL - original licence link has changed is not relivant.
24313  *
24314  * Fork - LGPL
24315  * <script type="text/javascript">
24316  */
24317
24318 /**
24319  * @class Roo.data.DataProxy
24320  * @extends Roo.data.Observable
24321  * This class is an abstract base class for implementations which provide retrieval of
24322  * unformatted data objects.<br>
24323  * <p>
24324  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
24325  * (of the appropriate type which knows how to parse the data object) to provide a block of
24326  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
24327  * <p>
24328  * Custom implementations must implement the load method as described in
24329  * {@link Roo.data.HttpProxy#load}.
24330  */
24331 Roo.data.DataProxy = function(){
24332     this.addEvents({
24333         /**
24334          * @event beforeload
24335          * Fires before a network request is made to retrieve a data object.
24336          * @param {Object} This DataProxy object.
24337          * @param {Object} params The params parameter to the load function.
24338          */
24339         beforeload : true,
24340         /**
24341          * @event load
24342          * Fires before the load method's callback is called.
24343          * @param {Object} This DataProxy object.
24344          * @param {Object} o The data object.
24345          * @param {Object} arg The callback argument object passed to the load function.
24346          */
24347         load : true,
24348         /**
24349          * @event loadexception
24350          * Fires if an Exception occurs during data retrieval.
24351          * @param {Object} This DataProxy object.
24352          * @param {Object} o The data object.
24353          * @param {Object} arg The callback argument object passed to the load function.
24354          * @param {Object} e The Exception.
24355          */
24356         loadexception : true
24357     });
24358     Roo.data.DataProxy.superclass.constructor.call(this);
24359 };
24360
24361 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
24362
24363     /**
24364      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
24365      */
24366 /*
24367  * Based on:
24368  * Ext JS Library 1.1.1
24369  * Copyright(c) 2006-2007, Ext JS, LLC.
24370  *
24371  * Originally Released Under LGPL - original licence link has changed is not relivant.
24372  *
24373  * Fork - LGPL
24374  * <script type="text/javascript">
24375  */
24376 /**
24377  * @class Roo.data.MemoryProxy
24378  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
24379  * to the Reader when its load method is called.
24380  * @constructor
24381  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
24382  */
24383 Roo.data.MemoryProxy = function(data){
24384     if (data.data) {
24385         data = data.data;
24386     }
24387     Roo.data.MemoryProxy.superclass.constructor.call(this);
24388     this.data = data;
24389 };
24390
24391 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
24392     
24393     /**
24394      * Load data from the requested source (in this case an in-memory
24395      * data object passed to the constructor), read the data object into
24396      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
24397      * process that block using the passed callback.
24398      * @param {Object} params This parameter is not used by the MemoryProxy class.
24399      * @param {Roo.data.DataReader} reader The Reader object which converts the data
24400      * object into a block of Roo.data.Records.
24401      * @param {Function} callback The function into which to pass the block of Roo.data.records.
24402      * The function must be passed <ul>
24403      * <li>The Record block object</li>
24404      * <li>The "arg" argument from the load function</li>
24405      * <li>A boolean success indicator</li>
24406      * </ul>
24407      * @param {Object} scope The scope in which to call the callback
24408      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
24409      */
24410     load : function(params, reader, callback, scope, arg){
24411         params = params || {};
24412         var result;
24413         try {
24414             result = reader.readRecords(params.data ? params.data :this.data);
24415         }catch(e){
24416             this.fireEvent("loadexception", this, arg, null, e);
24417             callback.call(scope, null, arg, false);
24418             return;
24419         }
24420         callback.call(scope, result, arg, true);
24421     },
24422     
24423     // private
24424     update : function(params, records){
24425         
24426     }
24427 });/*
24428  * Based on:
24429  * Ext JS Library 1.1.1
24430  * Copyright(c) 2006-2007, Ext JS, LLC.
24431  *
24432  * Originally Released Under LGPL - original licence link has changed is not relivant.
24433  *
24434  * Fork - LGPL
24435  * <script type="text/javascript">
24436  */
24437 /**
24438  * @class Roo.data.HttpProxy
24439  * @extends Roo.data.DataProxy
24440  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
24441  * configured to reference a certain URL.<br><br>
24442  * <p>
24443  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
24444  * from which the running page was served.<br><br>
24445  * <p>
24446  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
24447  * <p>
24448  * Be aware that to enable the browser to parse an XML document, the server must set
24449  * the Content-Type header in the HTTP response to "text/xml".
24450  * @constructor
24451  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
24452  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
24453  * will be used to make the request.
24454  */
24455 Roo.data.HttpProxy = function(conn){
24456     Roo.data.HttpProxy.superclass.constructor.call(this);
24457     // is conn a conn config or a real conn?
24458     this.conn = conn;
24459     this.useAjax = !conn || !conn.events;
24460   
24461 };
24462
24463 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
24464     // thse are take from connection...
24465     
24466     /**
24467      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
24468      */
24469     /**
24470      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
24471      * extra parameters to each request made by this object. (defaults to undefined)
24472      */
24473     /**
24474      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
24475      *  to each request made by this object. (defaults to undefined)
24476      */
24477     /**
24478      * @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)
24479      */
24480     /**
24481      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
24482      */
24483      /**
24484      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
24485      * @type Boolean
24486      */
24487   
24488
24489     /**
24490      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
24491      * @type Boolean
24492      */
24493     /**
24494      * Return the {@link Roo.data.Connection} object being used by this Proxy.
24495      * @return {Connection} The Connection object. This object may be used to subscribe to events on
24496      * a finer-grained basis than the DataProxy events.
24497      */
24498     getConnection : function(){
24499         return this.useAjax ? Roo.Ajax : this.conn;
24500     },
24501
24502     /**
24503      * Load data from the configured {@link Roo.data.Connection}, read the data object into
24504      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
24505      * process that block using the passed callback.
24506      * @param {Object} params An object containing properties which are to be used as HTTP parameters
24507      * for the request to the remote server.
24508      * @param {Roo.data.DataReader} reader The Reader object which converts the data
24509      * object into a block of Roo.data.Records.
24510      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
24511      * The function must be passed <ul>
24512      * <li>The Record block object</li>
24513      * <li>The "arg" argument from the load function</li>
24514      * <li>A boolean success indicator</li>
24515      * </ul>
24516      * @param {Object} scope The scope in which to call the callback
24517      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
24518      */
24519     load : function(params, reader, callback, scope, arg){
24520         if(this.fireEvent("beforeload", this, params) !== false){
24521             var  o = {
24522                 params : params || {},
24523                 request: {
24524                     callback : callback,
24525                     scope : scope,
24526                     arg : arg
24527                 },
24528                 reader: reader,
24529                 callback : this.loadResponse,
24530                 scope: this
24531             };
24532             if(this.useAjax){
24533                 Roo.applyIf(o, this.conn);
24534                 if(this.activeRequest){
24535                     Roo.Ajax.abort(this.activeRequest);
24536                 }
24537                 this.activeRequest = Roo.Ajax.request(o);
24538             }else{
24539                 this.conn.request(o);
24540             }
24541         }else{
24542             callback.call(scope||this, null, arg, false);
24543         }
24544     },
24545
24546     // private
24547     loadResponse : function(o, success, response){
24548         delete this.activeRequest;
24549         if(!success){
24550             this.fireEvent("loadexception", this, o, response);
24551             o.request.callback.call(o.request.scope, null, o.request.arg, false);
24552             return;
24553         }
24554         var result;
24555         try {
24556             result = o.reader.read(response);
24557         }catch(e){
24558             this.fireEvent("loadexception", this, o, response, e);
24559             o.request.callback.call(o.request.scope, null, o.request.arg, false);
24560             return;
24561         }
24562         
24563         this.fireEvent("load", this, o, o.request.arg);
24564         o.request.callback.call(o.request.scope, result, o.request.arg, true);
24565     },
24566
24567     // private
24568     update : function(dataSet){
24569
24570     },
24571
24572     // private
24573     updateResponse : function(dataSet){
24574
24575     }
24576 });/*
24577  * Based on:
24578  * Ext JS Library 1.1.1
24579  * Copyright(c) 2006-2007, Ext JS, LLC.
24580  *
24581  * Originally Released Under LGPL - original licence link has changed is not relivant.
24582  *
24583  * Fork - LGPL
24584  * <script type="text/javascript">
24585  */
24586
24587 /**
24588  * @class Roo.data.ScriptTagProxy
24589  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
24590  * other than the originating domain of the running page.<br><br>
24591  * <p>
24592  * <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
24593  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
24594  * <p>
24595  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
24596  * source code that is used as the source inside a &lt;script> tag.<br><br>
24597  * <p>
24598  * In order for the browser to process the returned data, the server must wrap the data object
24599  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
24600  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
24601  * depending on whether the callback name was passed:
24602  * <p>
24603  * <pre><code>
24604 boolean scriptTag = false;
24605 String cb = request.getParameter("callback");
24606 if (cb != null) {
24607     scriptTag = true;
24608     response.setContentType("text/javascript");
24609 } else {
24610     response.setContentType("application/x-json");
24611 }
24612 Writer out = response.getWriter();
24613 if (scriptTag) {
24614     out.write(cb + "(");
24615 }
24616 out.print(dataBlock.toJsonString());
24617 if (scriptTag) {
24618     out.write(");");
24619 }
24620 </pre></code>
24621  *
24622  * @constructor
24623  * @param {Object} config A configuration object.
24624  */
24625 Roo.data.ScriptTagProxy = function(config){
24626     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
24627     Roo.apply(this, config);
24628     this.head = document.getElementsByTagName("head")[0];
24629 };
24630
24631 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
24632
24633 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
24634     /**
24635      * @cfg {String} url The URL from which to request the data object.
24636      */
24637     /**
24638      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
24639      */
24640     timeout : 30000,
24641     /**
24642      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
24643      * the server the name of the callback function set up by the load call to process the returned data object.
24644      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
24645      * javascript output which calls this named function passing the data object as its only parameter.
24646      */
24647     callbackParam : "callback",
24648     /**
24649      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
24650      * name to the request.
24651      */
24652     nocache : true,
24653
24654     /**
24655      * Load data from the configured URL, read the data object into
24656      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
24657      * process that block using the passed callback.
24658      * @param {Object} params An object containing properties which are to be used as HTTP parameters
24659      * for the request to the remote server.
24660      * @param {Roo.data.DataReader} reader The Reader object which converts the data
24661      * object into a block of Roo.data.Records.
24662      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
24663      * The function must be passed <ul>
24664      * <li>The Record block object</li>
24665      * <li>The "arg" argument from the load function</li>
24666      * <li>A boolean success indicator</li>
24667      * </ul>
24668      * @param {Object} scope The scope in which to call the callback
24669      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
24670      */
24671     load : function(params, reader, callback, scope, arg){
24672         if(this.fireEvent("beforeload", this, params) !== false){
24673
24674             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
24675
24676             var url = this.url;
24677             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
24678             if(this.nocache){
24679                 url += "&_dc=" + (new Date().getTime());
24680             }
24681             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
24682             var trans = {
24683                 id : transId,
24684                 cb : "stcCallback"+transId,
24685                 scriptId : "stcScript"+transId,
24686                 params : params,
24687                 arg : arg,
24688                 url : url,
24689                 callback : callback,
24690                 scope : scope,
24691                 reader : reader
24692             };
24693             var conn = this;
24694
24695             window[trans.cb] = function(o){
24696                 conn.handleResponse(o, trans);
24697             };
24698
24699             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
24700
24701             if(this.autoAbort !== false){
24702                 this.abort();
24703             }
24704
24705             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
24706
24707             var script = document.createElement("script");
24708             script.setAttribute("src", url);
24709             script.setAttribute("type", "text/javascript");
24710             script.setAttribute("id", trans.scriptId);
24711             this.head.appendChild(script);
24712
24713             this.trans = trans;
24714         }else{
24715             callback.call(scope||this, null, arg, false);
24716         }
24717     },
24718
24719     // private
24720     isLoading : function(){
24721         return this.trans ? true : false;
24722     },
24723
24724     /**
24725      * Abort the current server request.
24726      */
24727     abort : function(){
24728         if(this.isLoading()){
24729             this.destroyTrans(this.trans);
24730         }
24731     },
24732
24733     // private
24734     destroyTrans : function(trans, isLoaded){
24735         this.head.removeChild(document.getElementById(trans.scriptId));
24736         clearTimeout(trans.timeoutId);
24737         if(isLoaded){
24738             window[trans.cb] = undefined;
24739             try{
24740                 delete window[trans.cb];
24741             }catch(e){}
24742         }else{
24743             // if hasn't been loaded, wait for load to remove it to prevent script error
24744             window[trans.cb] = function(){
24745                 window[trans.cb] = undefined;
24746                 try{
24747                     delete window[trans.cb];
24748                 }catch(e){}
24749             };
24750         }
24751     },
24752
24753     // private
24754     handleResponse : function(o, trans){
24755         this.trans = false;
24756         this.destroyTrans(trans, true);
24757         var result;
24758         try {
24759             result = trans.reader.readRecords(o);
24760         }catch(e){
24761             this.fireEvent("loadexception", this, o, trans.arg, e);
24762             trans.callback.call(trans.scope||window, null, trans.arg, false);
24763             return;
24764         }
24765         this.fireEvent("load", this, o, trans.arg);
24766         trans.callback.call(trans.scope||window, result, trans.arg, true);
24767     },
24768
24769     // private
24770     handleFailure : function(trans){
24771         this.trans = false;
24772         this.destroyTrans(trans, false);
24773         this.fireEvent("loadexception", this, null, trans.arg);
24774         trans.callback.call(trans.scope||window, null, trans.arg, false);
24775     }
24776 });/*
24777  * Based on:
24778  * Ext JS Library 1.1.1
24779  * Copyright(c) 2006-2007, Ext JS, LLC.
24780  *
24781  * Originally Released Under LGPL - original licence link has changed is not relivant.
24782  *
24783  * Fork - LGPL
24784  * <script type="text/javascript">
24785  */
24786
24787 /**
24788  * @class Roo.data.JsonReader
24789  * @extends Roo.data.DataReader
24790  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
24791  * based on mappings in a provided Roo.data.Record constructor.
24792  * 
24793  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
24794  * in the reply previously. 
24795  * 
24796  * <p>
24797  * Example code:
24798  * <pre><code>
24799 var RecordDef = Roo.data.Record.create([
24800     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
24801     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
24802 ]);
24803 var myReader = new Roo.data.JsonReader({
24804     totalProperty: "results",    // The property which contains the total dataset size (optional)
24805     root: "rows",                // The property which contains an Array of row objects
24806     id: "id"                     // The property within each row object that provides an ID for the record (optional)
24807 }, RecordDef);
24808 </code></pre>
24809  * <p>
24810  * This would consume a JSON file like this:
24811  * <pre><code>
24812 { 'results': 2, 'rows': [
24813     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
24814     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
24815 }
24816 </code></pre>
24817  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
24818  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
24819  * paged from the remote server.
24820  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
24821  * @cfg {String} root name of the property which contains the Array of row objects.
24822  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
24823  * @cfg {Array} fields Array of field definition objects
24824  * @constructor
24825  * Create a new JsonReader
24826  * @param {Object} meta Metadata configuration options
24827  * @param {Object} recordType Either an Array of field definition objects,
24828  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
24829  */
24830 Roo.data.JsonReader = function(meta, recordType){
24831     
24832     meta = meta || {};
24833     // set some defaults:
24834     Roo.applyIf(meta, {
24835         totalProperty: 'total',
24836         successProperty : 'success',
24837         root : 'data',
24838         id : 'id'
24839     });
24840     
24841     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
24842 };
24843 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
24844     
24845     readerType : 'Json',
24846     
24847     /**
24848      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
24849      * Used by Store query builder to append _requestMeta to params.
24850      * 
24851      */
24852     metaFromRemote : false,
24853     /**
24854      * This method is only used by a DataProxy which has retrieved data from a remote server.
24855      * @param {Object} response The XHR object which contains the JSON data in its responseText.
24856      * @return {Object} data A data block which is used by an Roo.data.Store object as
24857      * a cache of Roo.data.Records.
24858      */
24859     read : function(response){
24860         var json = response.responseText;
24861        
24862         var o = /* eval:var:o */ eval("("+json+")");
24863         if(!o) {
24864             throw {message: "JsonReader.read: Json object not found"};
24865         }
24866         
24867         if(o.metaData){
24868             
24869             delete this.ef;
24870             this.metaFromRemote = true;
24871             this.meta = o.metaData;
24872             this.recordType = Roo.data.Record.create(o.metaData.fields);
24873             this.onMetaChange(this.meta, this.recordType, o);
24874         }
24875         return this.readRecords(o);
24876     },
24877
24878     // private function a store will implement
24879     onMetaChange : function(meta, recordType, o){
24880
24881     },
24882
24883     /**
24884          * @ignore
24885          */
24886     simpleAccess: function(obj, subsc) {
24887         return obj[subsc];
24888     },
24889
24890         /**
24891          * @ignore
24892          */
24893     getJsonAccessor: function(){
24894         var re = /[\[\.]/;
24895         return function(expr) {
24896             try {
24897                 return(re.test(expr))
24898                     ? new Function("obj", "return obj." + expr)
24899                     : function(obj){
24900                         return obj[expr];
24901                     };
24902             } catch(e){}
24903             return Roo.emptyFn;
24904         };
24905     }(),
24906
24907     /**
24908      * Create a data block containing Roo.data.Records from an XML document.
24909      * @param {Object} o An object which contains an Array of row objects in the property specified
24910      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
24911      * which contains the total size of the dataset.
24912      * @return {Object} data A data block which is used by an Roo.data.Store object as
24913      * a cache of Roo.data.Records.
24914      */
24915     readRecords : function(o){
24916         /**
24917          * After any data loads, the raw JSON data is available for further custom processing.
24918          * @type Object
24919          */
24920         this.o = o;
24921         var s = this.meta, Record = this.recordType,
24922             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
24923
24924 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
24925         if (!this.ef) {
24926             if(s.totalProperty) {
24927                     this.getTotal = this.getJsonAccessor(s.totalProperty);
24928                 }
24929                 if(s.successProperty) {
24930                     this.getSuccess = this.getJsonAccessor(s.successProperty);
24931                 }
24932                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
24933                 if (s.id) {
24934                         var g = this.getJsonAccessor(s.id);
24935                         this.getId = function(rec) {
24936                                 var r = g(rec);  
24937                                 return (r === undefined || r === "") ? null : r;
24938                         };
24939                 } else {
24940                         this.getId = function(){return null;};
24941                 }
24942             this.ef = [];
24943             for(var jj = 0; jj < fl; jj++){
24944                 f = fi[jj];
24945                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
24946                 this.ef[jj] = this.getJsonAccessor(map);
24947             }
24948         }
24949
24950         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
24951         if(s.totalProperty){
24952             var vt = parseInt(this.getTotal(o), 10);
24953             if(!isNaN(vt)){
24954                 totalRecords = vt;
24955             }
24956         }
24957         if(s.successProperty){
24958             var vs = this.getSuccess(o);
24959             if(vs === false || vs === 'false'){
24960                 success = false;
24961             }
24962         }
24963         var records = [];
24964         for(var i = 0; i < c; i++){
24965                 var n = root[i];
24966             var values = {};
24967             var id = this.getId(n);
24968             for(var j = 0; j < fl; j++){
24969                 f = fi[j];
24970             var v = this.ef[j](n);
24971             if (!f.convert) {
24972                 Roo.log('missing convert for ' + f.name);
24973                 Roo.log(f);
24974                 continue;
24975             }
24976             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
24977             }
24978             var record = new Record(values, id);
24979             record.json = n;
24980             records[i] = record;
24981         }
24982         return {
24983             raw : o,
24984             success : success,
24985             records : records,
24986             totalRecords : totalRecords
24987         };
24988     },
24989     // used when loading children.. @see loadDataFromChildren
24990     toLoadData: function(rec)
24991     {
24992         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
24993         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
24994         return { data : data, total : data.length };
24995         
24996     }
24997 });/*
24998  * Based on:
24999  * Ext JS Library 1.1.1
25000  * Copyright(c) 2006-2007, Ext JS, LLC.
25001  *
25002  * Originally Released Under LGPL - original licence link has changed is not relivant.
25003  *
25004  * Fork - LGPL
25005  * <script type="text/javascript">
25006  */
25007
25008 /**
25009  * @class Roo.data.XmlReader
25010  * @extends Roo.data.DataReader
25011  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
25012  * based on mappings in a provided Roo.data.Record constructor.<br><br>
25013  * <p>
25014  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
25015  * header in the HTTP response must be set to "text/xml".</em>
25016  * <p>
25017  * Example code:
25018  * <pre><code>
25019 var RecordDef = Roo.data.Record.create([
25020    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
25021    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
25022 ]);
25023 var myReader = new Roo.data.XmlReader({
25024    totalRecords: "results", // The element which contains the total dataset size (optional)
25025    record: "row",           // The repeated element which contains row information
25026    id: "id"                 // The element within the row that provides an ID for the record (optional)
25027 }, RecordDef);
25028 </code></pre>
25029  * <p>
25030  * This would consume an XML file like this:
25031  * <pre><code>
25032 &lt;?xml?>
25033 &lt;dataset>
25034  &lt;results>2&lt;/results>
25035  &lt;row>
25036    &lt;id>1&lt;/id>
25037    &lt;name>Bill&lt;/name>
25038    &lt;occupation>Gardener&lt;/occupation>
25039  &lt;/row>
25040  &lt;row>
25041    &lt;id>2&lt;/id>
25042    &lt;name>Ben&lt;/name>
25043    &lt;occupation>Horticulturalist&lt;/occupation>
25044  &lt;/row>
25045 &lt;/dataset>
25046 </code></pre>
25047  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
25048  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
25049  * paged from the remote server.
25050  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
25051  * @cfg {String} success The DomQuery path to the success attribute used by forms.
25052  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
25053  * a record identifier value.
25054  * @constructor
25055  * Create a new XmlReader
25056  * @param {Object} meta Metadata configuration options
25057  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
25058  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
25059  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
25060  */
25061 Roo.data.XmlReader = function(meta, recordType){
25062     meta = meta || {};
25063     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
25064 };
25065 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
25066     
25067     readerType : 'Xml',
25068     
25069     /**
25070      * This method is only used by a DataProxy which has retrieved data from a remote server.
25071          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
25072          * to contain a method called 'responseXML' that returns an XML document object.
25073      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
25074      * a cache of Roo.data.Records.
25075      */
25076     read : function(response){
25077         var doc = response.responseXML;
25078         if(!doc) {
25079             throw {message: "XmlReader.read: XML Document not available"};
25080         }
25081         return this.readRecords(doc);
25082     },
25083
25084     /**
25085      * Create a data block containing Roo.data.Records from an XML document.
25086          * @param {Object} doc A parsed XML document.
25087      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
25088      * a cache of Roo.data.Records.
25089      */
25090     readRecords : function(doc){
25091         /**
25092          * After any data loads/reads, the raw XML Document is available for further custom processing.
25093          * @type XMLDocument
25094          */
25095         this.xmlData = doc;
25096         var root = doc.documentElement || doc;
25097         var q = Roo.DomQuery;
25098         var recordType = this.recordType, fields = recordType.prototype.fields;
25099         var sid = this.meta.id;
25100         var totalRecords = 0, success = true;
25101         if(this.meta.totalRecords){
25102             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
25103         }
25104         
25105         if(this.meta.success){
25106             var sv = q.selectValue(this.meta.success, root, true);
25107             success = sv !== false && sv !== 'false';
25108         }
25109         var records = [];
25110         var ns = q.select(this.meta.record, root);
25111         for(var i = 0, len = ns.length; i < len; i++) {
25112                 var n = ns[i];
25113                 var values = {};
25114                 var id = sid ? q.selectValue(sid, n) : undefined;
25115                 for(var j = 0, jlen = fields.length; j < jlen; j++){
25116                     var f = fields.items[j];
25117                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
25118                     v = f.convert(v);
25119                     values[f.name] = v;
25120                 }
25121                 var record = new recordType(values, id);
25122                 record.node = n;
25123                 records[records.length] = record;
25124             }
25125
25126             return {
25127                 success : success,
25128                 records : records,
25129                 totalRecords : totalRecords || records.length
25130             };
25131     }
25132 });/*
25133  * Based on:
25134  * Ext JS Library 1.1.1
25135  * Copyright(c) 2006-2007, Ext JS, LLC.
25136  *
25137  * Originally Released Under LGPL - original licence link has changed is not relivant.
25138  *
25139  * Fork - LGPL
25140  * <script type="text/javascript">
25141  */
25142
25143 /**
25144  * @class Roo.data.ArrayReader
25145  * @extends Roo.data.DataReader
25146  * Data reader class to create an Array of Roo.data.Record objects from an Array.
25147  * Each element of that Array represents a row of data fields. The
25148  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
25149  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
25150  * <p>
25151  * Example code:.
25152  * <pre><code>
25153 var RecordDef = Roo.data.Record.create([
25154     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
25155     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
25156 ]);
25157 var myReader = new Roo.data.ArrayReader({
25158     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
25159 }, RecordDef);
25160 </code></pre>
25161  * <p>
25162  * This would consume an Array like this:
25163  * <pre><code>
25164 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
25165   </code></pre>
25166  
25167  * @constructor
25168  * Create a new JsonReader
25169  * @param {Object} meta Metadata configuration options.
25170  * @param {Object|Array} recordType Either an Array of field definition objects
25171  * 
25172  * @cfg {Array} fields Array of field definition objects
25173  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
25174  * as specified to {@link Roo.data.Record#create},
25175  * or an {@link Roo.data.Record} object
25176  *
25177  * 
25178  * created using {@link Roo.data.Record#create}.
25179  */
25180 Roo.data.ArrayReader = function(meta, recordType)
25181 {    
25182     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
25183 };
25184
25185 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
25186     
25187       /**
25188      * Create a data block containing Roo.data.Records from an XML document.
25189      * @param {Object} o An Array of row objects which represents the dataset.
25190      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
25191      * a cache of Roo.data.Records.
25192      */
25193     readRecords : function(o)
25194     {
25195         var sid = this.meta ? this.meta.id : null;
25196         var recordType = this.recordType, fields = recordType.prototype.fields;
25197         var records = [];
25198         var root = o;
25199         for(var i = 0; i < root.length; i++){
25200                 var n = root[i];
25201             var values = {};
25202             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
25203             for(var j = 0, jlen = fields.length; j < jlen; j++){
25204                 var f = fields.items[j];
25205                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
25206                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
25207                 v = f.convert(v);
25208                 values[f.name] = v;
25209             }
25210             var record = new recordType(values, id);
25211             record.json = n;
25212             records[records.length] = record;
25213         }
25214         return {
25215             records : records,
25216             totalRecords : records.length
25217         };
25218     },
25219     // used when loading children.. @see loadDataFromChildren
25220     toLoadData: function(rec)
25221     {
25222         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
25223         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
25224         
25225     }
25226     
25227     
25228 });/*
25229  * Based on:
25230  * Ext JS Library 1.1.1
25231  * Copyright(c) 2006-2007, Ext JS, LLC.
25232  *
25233  * Originally Released Under LGPL - original licence link has changed is not relivant.
25234  *
25235  * Fork - LGPL
25236  * <script type="text/javascript">
25237  */
25238
25239
25240 /**
25241  * @class Roo.data.Tree
25242  * @extends Roo.util.Observable
25243  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
25244  * in the tree have most standard DOM functionality.
25245  * @constructor
25246  * @param {Node} root (optional) The root node
25247  */
25248 Roo.data.Tree = function(root){
25249    this.nodeHash = {};
25250    /**
25251     * The root node for this tree
25252     * @type Node
25253     */
25254    this.root = null;
25255    if(root){
25256        this.setRootNode(root);
25257    }
25258    this.addEvents({
25259        /**
25260         * @event append
25261         * Fires when a new child node is appended to a node in this tree.
25262         * @param {Tree} tree The owner tree
25263         * @param {Node} parent The parent node
25264         * @param {Node} node The newly appended node
25265         * @param {Number} index The index of the newly appended node
25266         */
25267        "append" : true,
25268        /**
25269         * @event remove
25270         * Fires when a child node is removed from a node in this tree.
25271         * @param {Tree} tree The owner tree
25272         * @param {Node} parent The parent node
25273         * @param {Node} node The child node removed
25274         */
25275        "remove" : true,
25276        /**
25277         * @event move
25278         * Fires when a node is moved to a new location in the tree
25279         * @param {Tree} tree The owner tree
25280         * @param {Node} node The node moved
25281         * @param {Node} oldParent The old parent of this node
25282         * @param {Node} newParent The new parent of this node
25283         * @param {Number} index The index it was moved to
25284         */
25285        "move" : true,
25286        /**
25287         * @event insert
25288         * Fires when a new child node is inserted in a node in this tree.
25289         * @param {Tree} tree The owner tree
25290         * @param {Node} parent The parent node
25291         * @param {Node} node The child node inserted
25292         * @param {Node} refNode The child node the node was inserted before
25293         */
25294        "insert" : true,
25295        /**
25296         * @event beforeappend
25297         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
25298         * @param {Tree} tree The owner tree
25299         * @param {Node} parent The parent node
25300         * @param {Node} node The child node to be appended
25301         */
25302        "beforeappend" : true,
25303        /**
25304         * @event beforeremove
25305         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
25306         * @param {Tree} tree The owner tree
25307         * @param {Node} parent The parent node
25308         * @param {Node} node The child node to be removed
25309         */
25310        "beforeremove" : true,
25311        /**
25312         * @event beforemove
25313         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
25314         * @param {Tree} tree The owner tree
25315         * @param {Node} node The node being moved
25316         * @param {Node} oldParent The parent of the node
25317         * @param {Node} newParent The new parent the node is moving to
25318         * @param {Number} index The index it is being moved to
25319         */
25320        "beforemove" : true,
25321        /**
25322         * @event beforeinsert
25323         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
25324         * @param {Tree} tree The owner tree
25325         * @param {Node} parent The parent node
25326         * @param {Node} node The child node to be inserted
25327         * @param {Node} refNode The child node the node is being inserted before
25328         */
25329        "beforeinsert" : true
25330    });
25331
25332     Roo.data.Tree.superclass.constructor.call(this);
25333 };
25334
25335 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
25336     pathSeparator: "/",
25337
25338     proxyNodeEvent : function(){
25339         return this.fireEvent.apply(this, arguments);
25340     },
25341
25342     /**
25343      * Returns the root node for this tree.
25344      * @return {Node}
25345      */
25346     getRootNode : function(){
25347         return this.root;
25348     },
25349
25350     /**
25351      * Sets the root node for this tree.
25352      * @param {Node} node
25353      * @return {Node}
25354      */
25355     setRootNode : function(node){
25356         this.root = node;
25357         node.ownerTree = this;
25358         node.isRoot = true;
25359         this.registerNode(node);
25360         return node;
25361     },
25362
25363     /**
25364      * Gets a node in this tree by its id.
25365      * @param {String} id
25366      * @return {Node}
25367      */
25368     getNodeById : function(id){
25369         return this.nodeHash[id];
25370     },
25371
25372     registerNode : function(node){
25373         this.nodeHash[node.id] = node;
25374     },
25375
25376     unregisterNode : function(node){
25377         delete this.nodeHash[node.id];
25378     },
25379
25380     toString : function(){
25381         return "[Tree"+(this.id?" "+this.id:"")+"]";
25382     }
25383 });
25384
25385 /**
25386  * @class Roo.data.Node
25387  * @extends Roo.util.Observable
25388  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
25389  * @cfg {String} id The id for this node. If one is not specified, one is generated.
25390  * @constructor
25391  * @param {Object} attributes The attributes/config for the node
25392  */
25393 Roo.data.Node = function(attributes){
25394     /**
25395      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
25396      * @type {Object}
25397      */
25398     this.attributes = attributes || {};
25399     this.leaf = this.attributes.leaf;
25400     /**
25401      * The node id. @type String
25402      */
25403     this.id = this.attributes.id;
25404     if(!this.id){
25405         this.id = Roo.id(null, "ynode-");
25406         this.attributes.id = this.id;
25407     }
25408      
25409     
25410     /**
25411      * All child nodes of this node. @type Array
25412      */
25413     this.childNodes = [];
25414     if(!this.childNodes.indexOf){ // indexOf is a must
25415         this.childNodes.indexOf = function(o){
25416             for(var i = 0, len = this.length; i < len; i++){
25417                 if(this[i] == o) {
25418                     return i;
25419                 }
25420             }
25421             return -1;
25422         };
25423     }
25424     /**
25425      * The parent node for this node. @type Node
25426      */
25427     this.parentNode = null;
25428     /**
25429      * The first direct child node of this node, or null if this node has no child nodes. @type Node
25430      */
25431     this.firstChild = null;
25432     /**
25433      * The last direct child node of this node, or null if this node has no child nodes. @type Node
25434      */
25435     this.lastChild = null;
25436     /**
25437      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
25438      */
25439     this.previousSibling = null;
25440     /**
25441      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
25442      */
25443     this.nextSibling = null;
25444
25445     this.addEvents({
25446        /**
25447         * @event append
25448         * Fires when a new child node is appended
25449         * @param {Tree} tree The owner tree
25450         * @param {Node} this This node
25451         * @param {Node} node The newly appended node
25452         * @param {Number} index The index of the newly appended node
25453         */
25454        "append" : true,
25455        /**
25456         * @event remove
25457         * Fires when a child node is removed
25458         * @param {Tree} tree The owner tree
25459         * @param {Node} this This node
25460         * @param {Node} node The removed node
25461         */
25462        "remove" : true,
25463        /**
25464         * @event move
25465         * Fires when this node is moved to a new location in the tree
25466         * @param {Tree} tree The owner tree
25467         * @param {Node} this This node
25468         * @param {Node} oldParent The old parent of this node
25469         * @param {Node} newParent The new parent of this node
25470         * @param {Number} index The index it was moved to
25471         */
25472        "move" : true,
25473        /**
25474         * @event insert
25475         * Fires when a new child node is inserted.
25476         * @param {Tree} tree The owner tree
25477         * @param {Node} this This node
25478         * @param {Node} node The child node inserted
25479         * @param {Node} refNode The child node the node was inserted before
25480         */
25481        "insert" : true,
25482        /**
25483         * @event beforeappend
25484         * Fires before a new child is appended, return false to cancel the append.
25485         * @param {Tree} tree The owner tree
25486         * @param {Node} this This node
25487         * @param {Node} node The child node to be appended
25488         */
25489        "beforeappend" : true,
25490        /**
25491         * @event beforeremove
25492         * Fires before a child is removed, return false to cancel the remove.
25493         * @param {Tree} tree The owner tree
25494         * @param {Node} this This node
25495         * @param {Node} node The child node to be removed
25496         */
25497        "beforeremove" : true,
25498        /**
25499         * @event beforemove
25500         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
25501         * @param {Tree} tree The owner tree
25502         * @param {Node} this This node
25503         * @param {Node} oldParent The parent of this node
25504         * @param {Node} newParent The new parent this node is moving to
25505         * @param {Number} index The index it is being moved to
25506         */
25507        "beforemove" : true,
25508        /**
25509         * @event beforeinsert
25510         * Fires before a new child is inserted, return false to cancel the insert.
25511         * @param {Tree} tree The owner tree
25512         * @param {Node} this This node
25513         * @param {Node} node The child node to be inserted
25514         * @param {Node} refNode The child node the node is being inserted before
25515         */
25516        "beforeinsert" : true
25517    });
25518     this.listeners = this.attributes.listeners;
25519     Roo.data.Node.superclass.constructor.call(this);
25520 };
25521
25522 Roo.extend(Roo.data.Node, Roo.util.Observable, {
25523     fireEvent : function(evtName){
25524         // first do standard event for this node
25525         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
25526             return false;
25527         }
25528         // then bubble it up to the tree if the event wasn't cancelled
25529         var ot = this.getOwnerTree();
25530         if(ot){
25531             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
25532                 return false;
25533             }
25534         }
25535         return true;
25536     },
25537
25538     /**
25539      * Returns true if this node is a leaf
25540      * @return {Boolean}
25541      */
25542     isLeaf : function(){
25543         return this.leaf === true;
25544     },
25545
25546     // private
25547     setFirstChild : function(node){
25548         this.firstChild = node;
25549     },
25550
25551     //private
25552     setLastChild : function(node){
25553         this.lastChild = node;
25554     },
25555
25556
25557     /**
25558      * Returns true if this node is the last child of its parent
25559      * @return {Boolean}
25560      */
25561     isLast : function(){
25562        return (!this.parentNode ? true : this.parentNode.lastChild == this);
25563     },
25564
25565     /**
25566      * Returns true if this node is the first child of its parent
25567      * @return {Boolean}
25568      */
25569     isFirst : function(){
25570        return (!this.parentNode ? true : this.parentNode.firstChild == this);
25571     },
25572
25573     hasChildNodes : function(){
25574         return !this.isLeaf() && this.childNodes.length > 0;
25575     },
25576
25577     /**
25578      * Insert node(s) as the last child node of this node.
25579      * @param {Node/Array} node The node or Array of nodes to append
25580      * @return {Node} The appended node if single append, or null if an array was passed
25581      */
25582     appendChild : function(node){
25583         var multi = false;
25584         if(node instanceof Array){
25585             multi = node;
25586         }else if(arguments.length > 1){
25587             multi = arguments;
25588         }
25589         
25590         // if passed an array or multiple args do them one by one
25591         if(multi){
25592             for(var i = 0, len = multi.length; i < len; i++) {
25593                 this.appendChild(multi[i]);
25594             }
25595         }else{
25596             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
25597                 return false;
25598             }
25599             var index = this.childNodes.length;
25600             var oldParent = node.parentNode;
25601             // it's a move, make sure we move it cleanly
25602             if(oldParent){
25603                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
25604                     return false;
25605                 }
25606                 oldParent.removeChild(node);
25607             }
25608             
25609             index = this.childNodes.length;
25610             if(index == 0){
25611                 this.setFirstChild(node);
25612             }
25613             this.childNodes.push(node);
25614             node.parentNode = this;
25615             var ps = this.childNodes[index-1];
25616             if(ps){
25617                 node.previousSibling = ps;
25618                 ps.nextSibling = node;
25619             }else{
25620                 node.previousSibling = null;
25621             }
25622             node.nextSibling = null;
25623             this.setLastChild(node);
25624             node.setOwnerTree(this.getOwnerTree());
25625             this.fireEvent("append", this.ownerTree, this, node, index);
25626             if(this.ownerTree) {
25627                 this.ownerTree.fireEvent("appendnode", this, node, index);
25628             }
25629             if(oldParent){
25630                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
25631             }
25632             return node;
25633         }
25634     },
25635
25636     /**
25637      * Removes a child node from this node.
25638      * @param {Node} node The node to remove
25639      * @return {Node} The removed node
25640      */
25641     removeChild : function(node){
25642         var index = this.childNodes.indexOf(node);
25643         if(index == -1){
25644             return false;
25645         }
25646         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
25647             return false;
25648         }
25649
25650         // remove it from childNodes collection
25651         this.childNodes.splice(index, 1);
25652
25653         // update siblings
25654         if(node.previousSibling){
25655             node.previousSibling.nextSibling = node.nextSibling;
25656         }
25657         if(node.nextSibling){
25658             node.nextSibling.previousSibling = node.previousSibling;
25659         }
25660
25661         // update child refs
25662         if(this.firstChild == node){
25663             this.setFirstChild(node.nextSibling);
25664         }
25665         if(this.lastChild == node){
25666             this.setLastChild(node.previousSibling);
25667         }
25668
25669         node.setOwnerTree(null);
25670         // clear any references from the node
25671         node.parentNode = null;
25672         node.previousSibling = null;
25673         node.nextSibling = null;
25674         this.fireEvent("remove", this.ownerTree, this, node);
25675         return node;
25676     },
25677
25678     /**
25679      * Inserts the first node before the second node in this nodes childNodes collection.
25680      * @param {Node} node The node to insert
25681      * @param {Node} refNode The node to insert before (if null the node is appended)
25682      * @return {Node} The inserted node
25683      */
25684     insertBefore : function(node, refNode){
25685         if(!refNode){ // like standard Dom, refNode can be null for append
25686             return this.appendChild(node);
25687         }
25688         // nothing to do
25689         if(node == refNode){
25690             return false;
25691         }
25692
25693         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
25694             return false;
25695         }
25696         var index = this.childNodes.indexOf(refNode);
25697         var oldParent = node.parentNode;
25698         var refIndex = index;
25699
25700         // when moving internally, indexes will change after remove
25701         if(oldParent == this && this.childNodes.indexOf(node) < index){
25702             refIndex--;
25703         }
25704
25705         // it's a move, make sure we move it cleanly
25706         if(oldParent){
25707             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
25708                 return false;
25709             }
25710             oldParent.removeChild(node);
25711         }
25712         if(refIndex == 0){
25713             this.setFirstChild(node);
25714         }
25715         this.childNodes.splice(refIndex, 0, node);
25716         node.parentNode = this;
25717         var ps = this.childNodes[refIndex-1];
25718         if(ps){
25719             node.previousSibling = ps;
25720             ps.nextSibling = node;
25721         }else{
25722             node.previousSibling = null;
25723         }
25724         node.nextSibling = refNode;
25725         refNode.previousSibling = node;
25726         node.setOwnerTree(this.getOwnerTree());
25727         this.fireEvent("insert", this.ownerTree, this, node, refNode);
25728         if(oldParent){
25729             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
25730         }
25731         return node;
25732     },
25733
25734     /**
25735      * Returns the child node at the specified index.
25736      * @param {Number} index
25737      * @return {Node}
25738      */
25739     item : function(index){
25740         return this.childNodes[index];
25741     },
25742
25743     /**
25744      * Replaces one child node in this node with another.
25745      * @param {Node} newChild The replacement node
25746      * @param {Node} oldChild The node to replace
25747      * @return {Node} The replaced node
25748      */
25749     replaceChild : function(newChild, oldChild){
25750         this.insertBefore(newChild, oldChild);
25751         this.removeChild(oldChild);
25752         return oldChild;
25753     },
25754
25755     /**
25756      * Returns the index of a child node
25757      * @param {Node} node
25758      * @return {Number} The index of the node or -1 if it was not found
25759      */
25760     indexOf : function(child){
25761         return this.childNodes.indexOf(child);
25762     },
25763
25764     /**
25765      * Returns the tree this node is in.
25766      * @return {Tree}
25767      */
25768     getOwnerTree : function(){
25769         // if it doesn't have one, look for one
25770         if(!this.ownerTree){
25771             var p = this;
25772             while(p){
25773                 if(p.ownerTree){
25774                     this.ownerTree = p.ownerTree;
25775                     break;
25776                 }
25777                 p = p.parentNode;
25778             }
25779         }
25780         return this.ownerTree;
25781     },
25782
25783     /**
25784      * Returns depth of this node (the root node has a depth of 0)
25785      * @return {Number}
25786      */
25787     getDepth : function(){
25788         var depth = 0;
25789         var p = this;
25790         while(p.parentNode){
25791             ++depth;
25792             p = p.parentNode;
25793         }
25794         return depth;
25795     },
25796
25797     // private
25798     setOwnerTree : function(tree){
25799         // if it's move, we need to update everyone
25800         if(tree != this.ownerTree){
25801             if(this.ownerTree){
25802                 this.ownerTree.unregisterNode(this);
25803             }
25804             this.ownerTree = tree;
25805             var cs = this.childNodes;
25806             for(var i = 0, len = cs.length; i < len; i++) {
25807                 cs[i].setOwnerTree(tree);
25808             }
25809             if(tree){
25810                 tree.registerNode(this);
25811             }
25812         }
25813     },
25814
25815     /**
25816      * Returns the path for this node. The path can be used to expand or select this node programmatically.
25817      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
25818      * @return {String} The path
25819      */
25820     getPath : function(attr){
25821         attr = attr || "id";
25822         var p = this.parentNode;
25823         var b = [this.attributes[attr]];
25824         while(p){
25825             b.unshift(p.attributes[attr]);
25826             p = p.parentNode;
25827         }
25828         var sep = this.getOwnerTree().pathSeparator;
25829         return sep + b.join(sep);
25830     },
25831
25832     /**
25833      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
25834      * function call will be the scope provided or the current node. The arguments to the function
25835      * will be the args provided or the current node. If the function returns false at any point,
25836      * the bubble is stopped.
25837      * @param {Function} fn The function to call
25838      * @param {Object} scope (optional) The scope of the function (defaults to current node)
25839      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
25840      */
25841     bubble : function(fn, scope, args){
25842         var p = this;
25843         while(p){
25844             if(fn.call(scope || p, args || p) === false){
25845                 break;
25846             }
25847             p = p.parentNode;
25848         }
25849     },
25850
25851     /**
25852      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
25853      * function call will be the scope provided or the current node. The arguments to the function
25854      * will be the args provided or the current node. If the function returns false at any point,
25855      * the cascade is stopped on that branch.
25856      * @param {Function} fn The function to call
25857      * @param {Object} scope (optional) The scope of the function (defaults to current node)
25858      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
25859      */
25860     cascade : function(fn, scope, args){
25861         if(fn.call(scope || this, args || this) !== false){
25862             var cs = this.childNodes;
25863             for(var i = 0, len = cs.length; i < len; i++) {
25864                 cs[i].cascade(fn, scope, args);
25865             }
25866         }
25867     },
25868
25869     /**
25870      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
25871      * function call will be the scope provided or the current node. The arguments to the function
25872      * will be the args provided or the current node. If the function returns false at any point,
25873      * the iteration stops.
25874      * @param {Function} fn The function to call
25875      * @param {Object} scope (optional) The scope of the function (defaults to current node)
25876      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
25877      */
25878     eachChild : function(fn, scope, args){
25879         var cs = this.childNodes;
25880         for(var i = 0, len = cs.length; i < len; i++) {
25881                 if(fn.call(scope || this, args || cs[i]) === false){
25882                     break;
25883                 }
25884         }
25885     },
25886
25887     /**
25888      * Finds the first child that has the attribute with the specified value.
25889      * @param {String} attribute The attribute name
25890      * @param {Mixed} value The value to search for
25891      * @return {Node} The found child or null if none was found
25892      */
25893     findChild : function(attribute, value){
25894         var cs = this.childNodes;
25895         for(var i = 0, len = cs.length; i < len; i++) {
25896                 if(cs[i].attributes[attribute] == value){
25897                     return cs[i];
25898                 }
25899         }
25900         return null;
25901     },
25902
25903     /**
25904      * Finds the first child by a custom function. The child matches if the function passed
25905      * returns true.
25906      * @param {Function} fn
25907      * @param {Object} scope (optional)
25908      * @return {Node} The found child or null if none was found
25909      */
25910     findChildBy : function(fn, scope){
25911         var cs = this.childNodes;
25912         for(var i = 0, len = cs.length; i < len; i++) {
25913                 if(fn.call(scope||cs[i], cs[i]) === true){
25914                     return cs[i];
25915                 }
25916         }
25917         return null;
25918     },
25919
25920     /**
25921      * Sorts this nodes children using the supplied sort function
25922      * @param {Function} fn
25923      * @param {Object} scope (optional)
25924      */
25925     sort : function(fn, scope){
25926         var cs = this.childNodes;
25927         var len = cs.length;
25928         if(len > 0){
25929             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
25930             cs.sort(sortFn);
25931             for(var i = 0; i < len; i++){
25932                 var n = cs[i];
25933                 n.previousSibling = cs[i-1];
25934                 n.nextSibling = cs[i+1];
25935                 if(i == 0){
25936                     this.setFirstChild(n);
25937                 }
25938                 if(i == len-1){
25939                     this.setLastChild(n);
25940                 }
25941             }
25942         }
25943     },
25944
25945     /**
25946      * Returns true if this node is an ancestor (at any point) of the passed node.
25947      * @param {Node} node
25948      * @return {Boolean}
25949      */
25950     contains : function(node){
25951         return node.isAncestor(this);
25952     },
25953
25954     /**
25955      * Returns true if the passed node is an ancestor (at any point) of this node.
25956      * @param {Node} node
25957      * @return {Boolean}
25958      */
25959     isAncestor : function(node){
25960         var p = this.parentNode;
25961         while(p){
25962             if(p == node){
25963                 return true;
25964             }
25965             p = p.parentNode;
25966         }
25967         return false;
25968     },
25969
25970     toString : function(){
25971         return "[Node"+(this.id?" "+this.id:"")+"]";
25972     }
25973 });/*
25974  * Based on:
25975  * Ext JS Library 1.1.1
25976  * Copyright(c) 2006-2007, Ext JS, LLC.
25977  *
25978  * Originally Released Under LGPL - original licence link has changed is not relivant.
25979  *
25980  * Fork - LGPL
25981  * <script type="text/javascript">
25982  */
25983
25984
25985 /**
25986  * @class Roo.Shadow
25987  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
25988  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
25989  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
25990  * @constructor
25991  * Create a new Shadow
25992  * @param {Object} config The config object
25993  */
25994 Roo.Shadow = function(config){
25995     Roo.apply(this, config);
25996     if(typeof this.mode != "string"){
25997         this.mode = this.defaultMode;
25998     }
25999     var o = this.offset, a = {h: 0};
26000     var rad = Math.floor(this.offset/2);
26001     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
26002         case "drop":
26003             a.w = 0;
26004             a.l = a.t = o;
26005             a.t -= 1;
26006             if(Roo.isIE){
26007                 a.l -= this.offset + rad;
26008                 a.t -= this.offset + rad;
26009                 a.w -= rad;
26010                 a.h -= rad;
26011                 a.t += 1;
26012             }
26013         break;
26014         case "sides":
26015             a.w = (o*2);
26016             a.l = -o;
26017             a.t = o-1;
26018             if(Roo.isIE){
26019                 a.l -= (this.offset - rad);
26020                 a.t -= this.offset + rad;
26021                 a.l += 1;
26022                 a.w -= (this.offset - rad)*2;
26023                 a.w -= rad + 1;
26024                 a.h -= 1;
26025             }
26026         break;
26027         case "frame":
26028             a.w = a.h = (o*2);
26029             a.l = a.t = -o;
26030             a.t += 1;
26031             a.h -= 2;
26032             if(Roo.isIE){
26033                 a.l -= (this.offset - rad);
26034                 a.t -= (this.offset - rad);
26035                 a.l += 1;
26036                 a.w -= (this.offset + rad + 1);
26037                 a.h -= (this.offset + rad);
26038                 a.h += 1;
26039             }
26040         break;
26041     };
26042
26043     this.adjusts = a;
26044 };
26045
26046 Roo.Shadow.prototype = {
26047     /**
26048      * @cfg {String} mode
26049      * The shadow display mode.  Supports the following options:<br />
26050      * sides: Shadow displays on both sides and bottom only<br />
26051      * frame: Shadow displays equally on all four sides<br />
26052      * drop: Traditional bottom-right drop shadow (default)
26053      */
26054     /**
26055      * @cfg {String} offset
26056      * The number of pixels to offset the shadow from the element (defaults to 4)
26057      */
26058     offset: 4,
26059
26060     // private
26061     defaultMode: "drop",
26062
26063     /**
26064      * Displays the shadow under the target element
26065      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
26066      */
26067     show : function(target){
26068         target = Roo.get(target);
26069         if(!this.el){
26070             this.el = Roo.Shadow.Pool.pull();
26071             if(this.el.dom.nextSibling != target.dom){
26072                 this.el.insertBefore(target);
26073             }
26074         }
26075         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
26076         if(Roo.isIE){
26077             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
26078         }
26079         this.realign(
26080             target.getLeft(true),
26081             target.getTop(true),
26082             target.getWidth(),
26083             target.getHeight()
26084         );
26085         this.el.dom.style.display = "block";
26086     },
26087
26088     /**
26089      * Returns true if the shadow is visible, else false
26090      */
26091     isVisible : function(){
26092         return this.el ? true : false;  
26093     },
26094
26095     /**
26096      * Direct alignment when values are already available. Show must be called at least once before
26097      * calling this method to ensure it is initialized.
26098      * @param {Number} left The target element left position
26099      * @param {Number} top The target element top position
26100      * @param {Number} width The target element width
26101      * @param {Number} height The target element height
26102      */
26103     realign : function(l, t, w, h){
26104         if(!this.el){
26105             return;
26106         }
26107         var a = this.adjusts, d = this.el.dom, s = d.style;
26108         var iea = 0;
26109         s.left = (l+a.l)+"px";
26110         s.top = (t+a.t)+"px";
26111         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
26112  
26113         if(s.width != sws || s.height != shs){
26114             s.width = sws;
26115             s.height = shs;
26116             if(!Roo.isIE){
26117                 var cn = d.childNodes;
26118                 var sww = Math.max(0, (sw-12))+"px";
26119                 cn[0].childNodes[1].style.width = sww;
26120                 cn[1].childNodes[1].style.width = sww;
26121                 cn[2].childNodes[1].style.width = sww;
26122                 cn[1].style.height = Math.max(0, (sh-12))+"px";
26123             }
26124         }
26125     },
26126
26127     /**
26128      * Hides this shadow
26129      */
26130     hide : function(){
26131         if(this.el){
26132             this.el.dom.style.display = "none";
26133             Roo.Shadow.Pool.push(this.el);
26134             delete this.el;
26135         }
26136     },
26137
26138     /**
26139      * Adjust the z-index of this shadow
26140      * @param {Number} zindex The new z-index
26141      */
26142     setZIndex : function(z){
26143         this.zIndex = z;
26144         if(this.el){
26145             this.el.setStyle("z-index", z);
26146         }
26147     }
26148 };
26149
26150 // Private utility class that manages the internal Shadow cache
26151 Roo.Shadow.Pool = function(){
26152     var p = [];
26153     var markup = Roo.isIE ?
26154                  '<div class="x-ie-shadow"></div>' :
26155                  '<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>';
26156     return {
26157         pull : function(){
26158             var sh = p.shift();
26159             if(!sh){
26160                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
26161                 sh.autoBoxAdjust = false;
26162             }
26163             return sh;
26164         },
26165
26166         push : function(sh){
26167             p.push(sh);
26168         }
26169     };
26170 }();/*
26171  * Based on:
26172  * Ext JS Library 1.1.1
26173  * Copyright(c) 2006-2007, Ext JS, LLC.
26174  *
26175  * Originally Released Under LGPL - original licence link has changed is not relivant.
26176  *
26177  * Fork - LGPL
26178  * <script type="text/javascript">
26179  */
26180
26181
26182 /**
26183  * @class Roo.SplitBar
26184  * @extends Roo.util.Observable
26185  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
26186  * <br><br>
26187  * Usage:
26188  * <pre><code>
26189 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
26190                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
26191 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
26192 split.minSize = 100;
26193 split.maxSize = 600;
26194 split.animate = true;
26195 split.on('moved', splitterMoved);
26196 </code></pre>
26197  * @constructor
26198  * Create a new SplitBar
26199  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
26200  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
26201  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
26202  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
26203                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
26204                         position of the SplitBar).
26205  */
26206 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
26207     
26208     /** @private */
26209     this.el = Roo.get(dragElement, true);
26210     this.el.dom.unselectable = "on";
26211     /** @private */
26212     this.resizingEl = Roo.get(resizingElement, true);
26213
26214     /**
26215      * @private
26216      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
26217      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
26218      * @type Number
26219      */
26220     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
26221     
26222     /**
26223      * The minimum size of the resizing element. (Defaults to 0)
26224      * @type Number
26225      */
26226     this.minSize = 0;
26227     
26228     /**
26229      * The maximum size of the resizing element. (Defaults to 2000)
26230      * @type Number
26231      */
26232     this.maxSize = 2000;
26233     
26234     /**
26235      * Whether to animate the transition to the new size
26236      * @type Boolean
26237      */
26238     this.animate = false;
26239     
26240     /**
26241      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
26242      * @type Boolean
26243      */
26244     this.useShim = false;
26245     
26246     /** @private */
26247     this.shim = null;
26248     
26249     if(!existingProxy){
26250         /** @private */
26251         this.proxy = Roo.SplitBar.createProxy(this.orientation);
26252     }else{
26253         this.proxy = Roo.get(existingProxy).dom;
26254     }
26255     /** @private */
26256     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
26257     
26258     /** @private */
26259     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
26260     
26261     /** @private */
26262     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
26263     
26264     /** @private */
26265     this.dragSpecs = {};
26266     
26267     /**
26268      * @private The adapter to use to positon and resize elements
26269      */
26270     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
26271     this.adapter.init(this);
26272     
26273     if(this.orientation == Roo.SplitBar.HORIZONTAL){
26274         /** @private */
26275         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
26276         this.el.addClass("x-splitbar-h");
26277     }else{
26278         /** @private */
26279         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
26280         this.el.addClass("x-splitbar-v");
26281     }
26282     
26283     this.addEvents({
26284         /**
26285          * @event resize
26286          * Fires when the splitter is moved (alias for {@link #event-moved})
26287          * @param {Roo.SplitBar} this
26288          * @param {Number} newSize the new width or height
26289          */
26290         "resize" : true,
26291         /**
26292          * @event moved
26293          * Fires when the splitter is moved
26294          * @param {Roo.SplitBar} this
26295          * @param {Number} newSize the new width or height
26296          */
26297         "moved" : true,
26298         /**
26299          * @event beforeresize
26300          * Fires before the splitter is dragged
26301          * @param {Roo.SplitBar} this
26302          */
26303         "beforeresize" : true,
26304
26305         "beforeapply" : true
26306     });
26307
26308     Roo.util.Observable.call(this);
26309 };
26310
26311 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
26312     onStartProxyDrag : function(x, y){
26313         this.fireEvent("beforeresize", this);
26314         if(!this.overlay){
26315             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
26316             o.unselectable();
26317             o.enableDisplayMode("block");
26318             // all splitbars share the same overlay
26319             Roo.SplitBar.prototype.overlay = o;
26320         }
26321         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
26322         this.overlay.show();
26323         Roo.get(this.proxy).setDisplayed("block");
26324         var size = this.adapter.getElementSize(this);
26325         this.activeMinSize = this.getMinimumSize();;
26326         this.activeMaxSize = this.getMaximumSize();;
26327         var c1 = size - this.activeMinSize;
26328         var c2 = Math.max(this.activeMaxSize - size, 0);
26329         if(this.orientation == Roo.SplitBar.HORIZONTAL){
26330             this.dd.resetConstraints();
26331             this.dd.setXConstraint(
26332                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
26333                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
26334             );
26335             this.dd.setYConstraint(0, 0);
26336         }else{
26337             this.dd.resetConstraints();
26338             this.dd.setXConstraint(0, 0);
26339             this.dd.setYConstraint(
26340                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
26341                 this.placement == Roo.SplitBar.TOP ? c2 : c1
26342             );
26343          }
26344         this.dragSpecs.startSize = size;
26345         this.dragSpecs.startPoint = [x, y];
26346         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
26347     },
26348     
26349     /** 
26350      * @private Called after the drag operation by the DDProxy
26351      */
26352     onEndProxyDrag : function(e){
26353         Roo.get(this.proxy).setDisplayed(false);
26354         var endPoint = Roo.lib.Event.getXY(e);
26355         if(this.overlay){
26356             this.overlay.hide();
26357         }
26358         var newSize;
26359         if(this.orientation == Roo.SplitBar.HORIZONTAL){
26360             newSize = this.dragSpecs.startSize + 
26361                 (this.placement == Roo.SplitBar.LEFT ?
26362                     endPoint[0] - this.dragSpecs.startPoint[0] :
26363                     this.dragSpecs.startPoint[0] - endPoint[0]
26364                 );
26365         }else{
26366             newSize = this.dragSpecs.startSize + 
26367                 (this.placement == Roo.SplitBar.TOP ?
26368                     endPoint[1] - this.dragSpecs.startPoint[1] :
26369                     this.dragSpecs.startPoint[1] - endPoint[1]
26370                 );
26371         }
26372         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
26373         if(newSize != this.dragSpecs.startSize){
26374             if(this.fireEvent('beforeapply', this, newSize) !== false){
26375                 this.adapter.setElementSize(this, newSize);
26376                 this.fireEvent("moved", this, newSize);
26377                 this.fireEvent("resize", this, newSize);
26378             }
26379         }
26380     },
26381     
26382     /**
26383      * Get the adapter this SplitBar uses
26384      * @return The adapter object
26385      */
26386     getAdapter : function(){
26387         return this.adapter;
26388     },
26389     
26390     /**
26391      * Set the adapter this SplitBar uses
26392      * @param {Object} adapter A SplitBar adapter object
26393      */
26394     setAdapter : function(adapter){
26395         this.adapter = adapter;
26396         this.adapter.init(this);
26397     },
26398     
26399     /**
26400      * Gets the minimum size for the resizing element
26401      * @return {Number} The minimum size
26402      */
26403     getMinimumSize : function(){
26404         return this.minSize;
26405     },
26406     
26407     /**
26408      * Sets the minimum size for the resizing element
26409      * @param {Number} minSize The minimum size
26410      */
26411     setMinimumSize : function(minSize){
26412         this.minSize = minSize;
26413     },
26414     
26415     /**
26416      * Gets the maximum size for the resizing element
26417      * @return {Number} The maximum size
26418      */
26419     getMaximumSize : function(){
26420         return this.maxSize;
26421     },
26422     
26423     /**
26424      * Sets the maximum size for the resizing element
26425      * @param {Number} maxSize The maximum size
26426      */
26427     setMaximumSize : function(maxSize){
26428         this.maxSize = maxSize;
26429     },
26430     
26431     /**
26432      * Sets the initialize size for the resizing element
26433      * @param {Number} size The initial size
26434      */
26435     setCurrentSize : function(size){
26436         var oldAnimate = this.animate;
26437         this.animate = false;
26438         this.adapter.setElementSize(this, size);
26439         this.animate = oldAnimate;
26440     },
26441     
26442     /**
26443      * Destroy this splitbar. 
26444      * @param {Boolean} removeEl True to remove the element
26445      */
26446     destroy : function(removeEl){
26447         if(this.shim){
26448             this.shim.remove();
26449         }
26450         this.dd.unreg();
26451         this.proxy.parentNode.removeChild(this.proxy);
26452         if(removeEl){
26453             this.el.remove();
26454         }
26455     }
26456 });
26457
26458 /**
26459  * @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.
26460  */
26461 Roo.SplitBar.createProxy = function(dir){
26462     var proxy = new Roo.Element(document.createElement("div"));
26463     proxy.unselectable();
26464     var cls = 'x-splitbar-proxy';
26465     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
26466     document.body.appendChild(proxy.dom);
26467     return proxy.dom;
26468 };
26469
26470 /** 
26471  * @class Roo.SplitBar.BasicLayoutAdapter
26472  * Default Adapter. It assumes the splitter and resizing element are not positioned
26473  * elements and only gets/sets the width of the element. Generally used for table based layouts.
26474  */
26475 Roo.SplitBar.BasicLayoutAdapter = function(){
26476 };
26477
26478 Roo.SplitBar.BasicLayoutAdapter.prototype = {
26479     // do nothing for now
26480     init : function(s){
26481     
26482     },
26483     /**
26484      * Called before drag operations to get the current size of the resizing element. 
26485      * @param {Roo.SplitBar} s The SplitBar using this adapter
26486      */
26487      getElementSize : function(s){
26488         if(s.orientation == Roo.SplitBar.HORIZONTAL){
26489             return s.resizingEl.getWidth();
26490         }else{
26491             return s.resizingEl.getHeight();
26492         }
26493     },
26494     
26495     /**
26496      * Called after drag operations to set the size of the resizing element.
26497      * @param {Roo.SplitBar} s The SplitBar using this adapter
26498      * @param {Number} newSize The new size to set
26499      * @param {Function} onComplete A function to be invoked when resizing is complete
26500      */
26501     setElementSize : function(s, newSize, onComplete){
26502         if(s.orientation == Roo.SplitBar.HORIZONTAL){
26503             if(!s.animate){
26504                 s.resizingEl.setWidth(newSize);
26505                 if(onComplete){
26506                     onComplete(s, newSize);
26507                 }
26508             }else{
26509                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
26510             }
26511         }else{
26512             
26513             if(!s.animate){
26514                 s.resizingEl.setHeight(newSize);
26515                 if(onComplete){
26516                     onComplete(s, newSize);
26517                 }
26518             }else{
26519                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
26520             }
26521         }
26522     }
26523 };
26524
26525 /** 
26526  *@class Roo.SplitBar.AbsoluteLayoutAdapter
26527  * @extends Roo.SplitBar.BasicLayoutAdapter
26528  * Adapter that  moves the splitter element to align with the resized sizing element. 
26529  * Used with an absolute positioned SplitBar.
26530  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
26531  * document.body, make sure you assign an id to the body element.
26532  */
26533 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
26534     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
26535     this.container = Roo.get(container);
26536 };
26537
26538 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
26539     init : function(s){
26540         this.basic.init(s);
26541     },
26542     
26543     getElementSize : function(s){
26544         return this.basic.getElementSize(s);
26545     },
26546     
26547     setElementSize : function(s, newSize, onComplete){
26548         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
26549     },
26550     
26551     moveSplitter : function(s){
26552         var yes = Roo.SplitBar;
26553         switch(s.placement){
26554             case yes.LEFT:
26555                 s.el.setX(s.resizingEl.getRight());
26556                 break;
26557             case yes.RIGHT:
26558                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
26559                 break;
26560             case yes.TOP:
26561                 s.el.setY(s.resizingEl.getBottom());
26562                 break;
26563             case yes.BOTTOM:
26564                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
26565                 break;
26566         }
26567     }
26568 };
26569
26570 /**
26571  * Orientation constant - Create a vertical SplitBar
26572  * @static
26573  * @type Number
26574  */
26575 Roo.SplitBar.VERTICAL = 1;
26576
26577 /**
26578  * Orientation constant - Create a horizontal SplitBar
26579  * @static
26580  * @type Number
26581  */
26582 Roo.SplitBar.HORIZONTAL = 2;
26583
26584 /**
26585  * Placement constant - The resizing element is to the left of the splitter element
26586  * @static
26587  * @type Number
26588  */
26589 Roo.SplitBar.LEFT = 1;
26590
26591 /**
26592  * Placement constant - The resizing element is to the right of the splitter element
26593  * @static
26594  * @type Number
26595  */
26596 Roo.SplitBar.RIGHT = 2;
26597
26598 /**
26599  * Placement constant - The resizing element is positioned above the splitter element
26600  * @static
26601  * @type Number
26602  */
26603 Roo.SplitBar.TOP = 3;
26604
26605 /**
26606  * Placement constant - The resizing element is positioned under splitter element
26607  * @static
26608  * @type Number
26609  */
26610 Roo.SplitBar.BOTTOM = 4;
26611 /*
26612  * Based on:
26613  * Ext JS Library 1.1.1
26614  * Copyright(c) 2006-2007, Ext JS, LLC.
26615  *
26616  * Originally Released Under LGPL - original licence link has changed is not relivant.
26617  *
26618  * Fork - LGPL
26619  * <script type="text/javascript">
26620  */
26621
26622 /**
26623  * @class Roo.View
26624  * @extends Roo.util.Observable
26625  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
26626  * This class also supports single and multi selection modes. <br>
26627  * Create a data model bound view:
26628  <pre><code>
26629  var store = new Roo.data.Store(...);
26630
26631  var view = new Roo.View({
26632     el : "my-element",
26633     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
26634  
26635     singleSelect: true,
26636     selectedClass: "ydataview-selected",
26637     store: store
26638  });
26639
26640  // listen for node click?
26641  view.on("click", function(vw, index, node, e){
26642  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
26643  });
26644
26645  // load XML data
26646  dataModel.load("foobar.xml");
26647  </code></pre>
26648  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
26649  * <br><br>
26650  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
26651  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
26652  * 
26653  * Note: old style constructor is still suported (container, template, config)
26654  * 
26655  * @constructor
26656  * Create a new View
26657  * @param {Object} config The config object
26658  * 
26659  */
26660 Roo.View = function(config, depreciated_tpl, depreciated_config){
26661     
26662     this.parent = false;
26663     
26664     if (typeof(depreciated_tpl) == 'undefined') {
26665         // new way.. - universal constructor.
26666         Roo.apply(this, config);
26667         this.el  = Roo.get(this.el);
26668     } else {
26669         // old format..
26670         this.el  = Roo.get(config);
26671         this.tpl = depreciated_tpl;
26672         Roo.apply(this, depreciated_config);
26673     }
26674     this.wrapEl  = this.el.wrap().wrap();
26675     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
26676     
26677     
26678     if(typeof(this.tpl) == "string"){
26679         this.tpl = new Roo.Template(this.tpl);
26680     } else {
26681         // support xtype ctors..
26682         this.tpl = new Roo.factory(this.tpl, Roo);
26683     }
26684     
26685     
26686     this.tpl.compile();
26687     
26688     /** @private */
26689     this.addEvents({
26690         /**
26691          * @event beforeclick
26692          * Fires before a click is processed. Returns false to cancel the default action.
26693          * @param {Roo.View} this
26694          * @param {Number} index The index of the target node
26695          * @param {HTMLElement} node The target node
26696          * @param {Roo.EventObject} e The raw event object
26697          */
26698             "beforeclick" : true,
26699         /**
26700          * @event click
26701          * Fires when a template node is clicked.
26702          * @param {Roo.View} this
26703          * @param {Number} index The index of the target node
26704          * @param {HTMLElement} node The target node
26705          * @param {Roo.EventObject} e The raw event object
26706          */
26707             "click" : true,
26708         /**
26709          * @event dblclick
26710          * Fires when a template node is double clicked.
26711          * @param {Roo.View} this
26712          * @param {Number} index The index of the target node
26713          * @param {HTMLElement} node The target node
26714          * @param {Roo.EventObject} e The raw event object
26715          */
26716             "dblclick" : true,
26717         /**
26718          * @event contextmenu
26719          * Fires when a template node is right clicked.
26720          * @param {Roo.View} this
26721          * @param {Number} index The index of the target node
26722          * @param {HTMLElement} node The target node
26723          * @param {Roo.EventObject} e The raw event object
26724          */
26725             "contextmenu" : true,
26726         /**
26727          * @event selectionchange
26728          * Fires when the selected nodes change.
26729          * @param {Roo.View} this
26730          * @param {Array} selections Array of the selected nodes
26731          */
26732             "selectionchange" : true,
26733     
26734         /**
26735          * @event beforeselect
26736          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
26737          * @param {Roo.View} this
26738          * @param {HTMLElement} node The node to be selected
26739          * @param {Array} selections Array of currently selected nodes
26740          */
26741             "beforeselect" : true,
26742         /**
26743          * @event preparedata
26744          * Fires on every row to render, to allow you to change the data.
26745          * @param {Roo.View} this
26746          * @param {Object} data to be rendered (change this)
26747          */
26748           "preparedata" : true
26749           
26750           
26751         });
26752
26753
26754
26755     this.el.on({
26756         "click": this.onClick,
26757         "dblclick": this.onDblClick,
26758         "contextmenu": this.onContextMenu,
26759         scope:this
26760     });
26761
26762     this.selections = [];
26763     this.nodes = [];
26764     this.cmp = new Roo.CompositeElementLite([]);
26765     if(this.store){
26766         this.store = Roo.factory(this.store, Roo.data);
26767         this.setStore(this.store, true);
26768     }
26769     
26770     if ( this.footer && this.footer.xtype) {
26771            
26772          var fctr = this.wrapEl.appendChild(document.createElement("div"));
26773         
26774         this.footer.dataSource = this.store;
26775         this.footer.container = fctr;
26776         this.footer = Roo.factory(this.footer, Roo);
26777         fctr.insertFirst(this.el);
26778         
26779         // this is a bit insane - as the paging toolbar seems to detach the el..
26780 //        dom.parentNode.parentNode.parentNode
26781          // they get detached?
26782     }
26783     
26784     
26785     Roo.View.superclass.constructor.call(this);
26786     
26787     
26788 };
26789
26790 Roo.extend(Roo.View, Roo.util.Observable, {
26791     
26792      /**
26793      * @cfg {Roo.data.Store} store Data store to load data from.
26794      */
26795     store : false,
26796     
26797     /**
26798      * @cfg {String|Roo.Element} el The container element.
26799      */
26800     el : '',
26801     
26802     /**
26803      * @cfg {String|Roo.Template} tpl The template used by this View 
26804      */
26805     tpl : false,
26806     /**
26807      * @cfg {String} dataName the named area of the template to use as the data area
26808      *                          Works with domtemplates roo-name="name"
26809      */
26810     dataName: false,
26811     /**
26812      * @cfg {String} selectedClass The css class to add to selected nodes
26813      */
26814     selectedClass : "x-view-selected",
26815      /**
26816      * @cfg {String} emptyText The empty text to show when nothing is loaded.
26817      */
26818     emptyText : "",
26819     
26820     /**
26821      * @cfg {String} text to display on mask (default Loading)
26822      */
26823     mask : false,
26824     /**
26825      * @cfg {Boolean} multiSelect Allow multiple selection
26826      */
26827     multiSelect : false,
26828     /**
26829      * @cfg {Boolean} singleSelect Allow single selection
26830      */
26831     singleSelect:  false,
26832     
26833     /**
26834      * @cfg {Boolean} toggleSelect - selecting 
26835      */
26836     toggleSelect : false,
26837     
26838     /**
26839      * @cfg {Boolean} tickable - selecting 
26840      */
26841     tickable : false,
26842     
26843     /**
26844      * Returns the element this view is bound to.
26845      * @return {Roo.Element}
26846      */
26847     getEl : function(){
26848         return this.wrapEl;
26849     },
26850     
26851     
26852
26853     /**
26854      * Refreshes the view. - called by datachanged on the store. - do not call directly.
26855      */
26856     refresh : function(){
26857         //Roo.log('refresh');
26858         var t = this.tpl;
26859         
26860         // if we are using something like 'domtemplate', then
26861         // the what gets used is:
26862         // t.applySubtemplate(NAME, data, wrapping data..)
26863         // the outer template then get' applied with
26864         //     the store 'extra data'
26865         // and the body get's added to the
26866         //      roo-name="data" node?
26867         //      <span class='roo-tpl-{name}'></span> ?????
26868         
26869         
26870         
26871         this.clearSelections();
26872         this.el.update("");
26873         var html = [];
26874         var records = this.store.getRange();
26875         if(records.length < 1) {
26876             
26877             // is this valid??  = should it render a template??
26878             
26879             this.el.update(this.emptyText);
26880             return;
26881         }
26882         var el = this.el;
26883         if (this.dataName) {
26884             this.el.update(t.apply(this.store.meta)); //????
26885             el = this.el.child('.roo-tpl-' + this.dataName);
26886         }
26887         
26888         for(var i = 0, len = records.length; i < len; i++){
26889             var data = this.prepareData(records[i].data, i, records[i]);
26890             this.fireEvent("preparedata", this, data, i, records[i]);
26891             
26892             var d = Roo.apply({}, data);
26893             
26894             if(this.tickable){
26895                 Roo.apply(d, {'roo-id' : Roo.id()});
26896                 
26897                 var _this = this;
26898             
26899                 Roo.each(this.parent.item, function(item){
26900                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
26901                         return;
26902                     }
26903                     Roo.apply(d, {'roo-data-checked' : 'checked'});
26904                 });
26905             }
26906             
26907             html[html.length] = Roo.util.Format.trim(
26908                 this.dataName ?
26909                     t.applySubtemplate(this.dataName, d, this.store.meta) :
26910                     t.apply(d)
26911             );
26912         }
26913         
26914         
26915         
26916         el.update(html.join(""));
26917         this.nodes = el.dom.childNodes;
26918         this.updateIndexes(0);
26919     },
26920     
26921
26922     /**
26923      * Function to override to reformat the data that is sent to
26924      * the template for each node.
26925      * DEPRICATED - use the preparedata event handler.
26926      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
26927      * a JSON object for an UpdateManager bound view).
26928      */
26929     prepareData : function(data, index, record)
26930     {
26931         this.fireEvent("preparedata", this, data, index, record);
26932         return data;
26933     },
26934
26935     onUpdate : function(ds, record){
26936         // Roo.log('on update');   
26937         this.clearSelections();
26938         var index = this.store.indexOf(record);
26939         var n = this.nodes[index];
26940         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
26941         n.parentNode.removeChild(n);
26942         this.updateIndexes(index, index);
26943     },
26944
26945     
26946     
26947 // --------- FIXME     
26948     onAdd : function(ds, records, index)
26949     {
26950         //Roo.log(['on Add', ds, records, index] );        
26951         this.clearSelections();
26952         if(this.nodes.length == 0){
26953             this.refresh();
26954             return;
26955         }
26956         var n = this.nodes[index];
26957         for(var i = 0, len = records.length; i < len; i++){
26958             var d = this.prepareData(records[i].data, i, records[i]);
26959             if(n){
26960                 this.tpl.insertBefore(n, d);
26961             }else{
26962                 
26963                 this.tpl.append(this.el, d);
26964             }
26965         }
26966         this.updateIndexes(index);
26967     },
26968
26969     onRemove : function(ds, record, index){
26970        // Roo.log('onRemove');
26971         this.clearSelections();
26972         var el = this.dataName  ?
26973             this.el.child('.roo-tpl-' + this.dataName) :
26974             this.el; 
26975         
26976         el.dom.removeChild(this.nodes[index]);
26977         this.updateIndexes(index);
26978     },
26979
26980     /**
26981      * Refresh an individual node.
26982      * @param {Number} index
26983      */
26984     refreshNode : function(index){
26985         this.onUpdate(this.store, this.store.getAt(index));
26986     },
26987
26988     updateIndexes : function(startIndex, endIndex){
26989         var ns = this.nodes;
26990         startIndex = startIndex || 0;
26991         endIndex = endIndex || ns.length - 1;
26992         for(var i = startIndex; i <= endIndex; i++){
26993             ns[i].nodeIndex = i;
26994         }
26995     },
26996
26997     /**
26998      * Changes the data store this view uses and refresh the view.
26999      * @param {Store} store
27000      */
27001     setStore : function(store, initial){
27002         if(!initial && this.store){
27003             this.store.un("datachanged", this.refresh);
27004             this.store.un("add", this.onAdd);
27005             this.store.un("remove", this.onRemove);
27006             this.store.un("update", this.onUpdate);
27007             this.store.un("clear", this.refresh);
27008             this.store.un("beforeload", this.onBeforeLoad);
27009             this.store.un("load", this.onLoad);
27010             this.store.un("loadexception", this.onLoad);
27011         }
27012         if(store){
27013           
27014             store.on("datachanged", this.refresh, this);
27015             store.on("add", this.onAdd, this);
27016             store.on("remove", this.onRemove, this);
27017             store.on("update", this.onUpdate, this);
27018             store.on("clear", this.refresh, this);
27019             store.on("beforeload", this.onBeforeLoad, this);
27020             store.on("load", this.onLoad, this);
27021             store.on("loadexception", this.onLoad, this);
27022         }
27023         
27024         if(store){
27025             this.refresh();
27026         }
27027     },
27028     /**
27029      * onbeforeLoad - masks the loading area.
27030      *
27031      */
27032     onBeforeLoad : function(store,opts)
27033     {
27034          //Roo.log('onBeforeLoad');   
27035         if (!opts.add) {
27036             this.el.update("");
27037         }
27038         this.el.mask(this.mask ? this.mask : "Loading" ); 
27039     },
27040     onLoad : function ()
27041     {
27042         this.el.unmask();
27043     },
27044     
27045
27046     /**
27047      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
27048      * @param {HTMLElement} node
27049      * @return {HTMLElement} The template node
27050      */
27051     findItemFromChild : function(node){
27052         var el = this.dataName  ?
27053             this.el.child('.roo-tpl-' + this.dataName,true) :
27054             this.el.dom; 
27055         
27056         if(!node || node.parentNode == el){
27057                     return node;
27058             }
27059             var p = node.parentNode;
27060             while(p && p != el){
27061             if(p.parentNode == el){
27062                 return p;
27063             }
27064             p = p.parentNode;
27065         }
27066             return null;
27067     },
27068
27069     /** @ignore */
27070     onClick : function(e){
27071         var item = this.findItemFromChild(e.getTarget());
27072         if(item){
27073             var index = this.indexOf(item);
27074             if(this.onItemClick(item, index, e) !== false){
27075                 this.fireEvent("click", this, index, item, e);
27076             }
27077         }else{
27078             this.clearSelections();
27079         }
27080     },
27081
27082     /** @ignore */
27083     onContextMenu : function(e){
27084         var item = this.findItemFromChild(e.getTarget());
27085         if(item){
27086             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
27087         }
27088     },
27089
27090     /** @ignore */
27091     onDblClick : function(e){
27092         var item = this.findItemFromChild(e.getTarget());
27093         if(item){
27094             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
27095         }
27096     },
27097
27098     onItemClick : function(item, index, e)
27099     {
27100         if(this.fireEvent("beforeclick", this, index, item, e) === false){
27101             return false;
27102         }
27103         if (this.toggleSelect) {
27104             var m = this.isSelected(item) ? 'unselect' : 'select';
27105             //Roo.log(m);
27106             var _t = this;
27107             _t[m](item, true, false);
27108             return true;
27109         }
27110         if(this.multiSelect || this.singleSelect){
27111             if(this.multiSelect && e.shiftKey && this.lastSelection){
27112                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
27113             }else{
27114                 this.select(item, this.multiSelect && e.ctrlKey);
27115                 this.lastSelection = item;
27116             }
27117             
27118             if(!this.tickable){
27119                 e.preventDefault();
27120             }
27121             
27122         }
27123         return true;
27124     },
27125
27126     /**
27127      * Get the number of selected nodes.
27128      * @return {Number}
27129      */
27130     getSelectionCount : function(){
27131         return this.selections.length;
27132     },
27133
27134     /**
27135      * Get the currently selected nodes.
27136      * @return {Array} An array of HTMLElements
27137      */
27138     getSelectedNodes : function(){
27139         return this.selections;
27140     },
27141
27142     /**
27143      * Get the indexes of the selected nodes.
27144      * @return {Array}
27145      */
27146     getSelectedIndexes : function(){
27147         var indexes = [], s = this.selections;
27148         for(var i = 0, len = s.length; i < len; i++){
27149             indexes.push(s[i].nodeIndex);
27150         }
27151         return indexes;
27152     },
27153
27154     /**
27155      * Clear all selections
27156      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
27157      */
27158     clearSelections : function(suppressEvent){
27159         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
27160             this.cmp.elements = this.selections;
27161             this.cmp.removeClass(this.selectedClass);
27162             this.selections = [];
27163             if(!suppressEvent){
27164                 this.fireEvent("selectionchange", this, this.selections);
27165             }
27166         }
27167     },
27168
27169     /**
27170      * Returns true if the passed node is selected
27171      * @param {HTMLElement/Number} node The node or node index
27172      * @return {Boolean}
27173      */
27174     isSelected : function(node){
27175         var s = this.selections;
27176         if(s.length < 1){
27177             return false;
27178         }
27179         node = this.getNode(node);
27180         return s.indexOf(node) !== -1;
27181     },
27182
27183     /**
27184      * Selects nodes.
27185      * @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
27186      * @param {Boolean} keepExisting (optional) true to keep existing selections
27187      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
27188      */
27189     select : function(nodeInfo, keepExisting, suppressEvent){
27190         if(nodeInfo instanceof Array){
27191             if(!keepExisting){
27192                 this.clearSelections(true);
27193             }
27194             for(var i = 0, len = nodeInfo.length; i < len; i++){
27195                 this.select(nodeInfo[i], true, true);
27196             }
27197             return;
27198         } 
27199         var node = this.getNode(nodeInfo);
27200         if(!node || this.isSelected(node)){
27201             return; // already selected.
27202         }
27203         if(!keepExisting){
27204             this.clearSelections(true);
27205         }
27206         
27207         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
27208             Roo.fly(node).addClass(this.selectedClass);
27209             this.selections.push(node);
27210             if(!suppressEvent){
27211                 this.fireEvent("selectionchange", this, this.selections);
27212             }
27213         }
27214         
27215         
27216     },
27217       /**
27218      * Unselects nodes.
27219      * @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
27220      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
27221      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
27222      */
27223     unselect : function(nodeInfo, keepExisting, suppressEvent)
27224     {
27225         if(nodeInfo instanceof Array){
27226             Roo.each(this.selections, function(s) {
27227                 this.unselect(s, nodeInfo);
27228             }, this);
27229             return;
27230         }
27231         var node = this.getNode(nodeInfo);
27232         if(!node || !this.isSelected(node)){
27233             //Roo.log("not selected");
27234             return; // not selected.
27235         }
27236         // fireevent???
27237         var ns = [];
27238         Roo.each(this.selections, function(s) {
27239             if (s == node ) {
27240                 Roo.fly(node).removeClass(this.selectedClass);
27241
27242                 return;
27243             }
27244             ns.push(s);
27245         },this);
27246         
27247         this.selections= ns;
27248         this.fireEvent("selectionchange", this, this.selections);
27249     },
27250
27251     /**
27252      * Gets a template node.
27253      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
27254      * @return {HTMLElement} The node or null if it wasn't found
27255      */
27256     getNode : function(nodeInfo){
27257         if(typeof nodeInfo == "string"){
27258             return document.getElementById(nodeInfo);
27259         }else if(typeof nodeInfo == "number"){
27260             return this.nodes[nodeInfo];
27261         }
27262         return nodeInfo;
27263     },
27264
27265     /**
27266      * Gets a range template nodes.
27267      * @param {Number} startIndex
27268      * @param {Number} endIndex
27269      * @return {Array} An array of nodes
27270      */
27271     getNodes : function(start, end){
27272         var ns = this.nodes;
27273         start = start || 0;
27274         end = typeof end == "undefined" ? ns.length - 1 : end;
27275         var nodes = [];
27276         if(start <= end){
27277             for(var i = start; i <= end; i++){
27278                 nodes.push(ns[i]);
27279             }
27280         } else{
27281             for(var i = start; i >= end; i--){
27282                 nodes.push(ns[i]);
27283             }
27284         }
27285         return nodes;
27286     },
27287
27288     /**
27289      * Finds the index of the passed node
27290      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
27291      * @return {Number} The index of the node or -1
27292      */
27293     indexOf : function(node){
27294         node = this.getNode(node);
27295         if(typeof node.nodeIndex == "number"){
27296             return node.nodeIndex;
27297         }
27298         var ns = this.nodes;
27299         for(var i = 0, len = ns.length; i < len; i++){
27300             if(ns[i] == node){
27301                 return i;
27302             }
27303         }
27304         return -1;
27305     }
27306 });
27307 /*
27308  * Based on:
27309  * Ext JS Library 1.1.1
27310  * Copyright(c) 2006-2007, Ext JS, LLC.
27311  *
27312  * Originally Released Under LGPL - original licence link has changed is not relivant.
27313  *
27314  * Fork - LGPL
27315  * <script type="text/javascript">
27316  */
27317
27318 /**
27319  * @class Roo.JsonView
27320  * @extends Roo.View
27321  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
27322 <pre><code>
27323 var view = new Roo.JsonView({
27324     container: "my-element",
27325     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
27326     multiSelect: true, 
27327     jsonRoot: "data" 
27328 });
27329
27330 // listen for node click?
27331 view.on("click", function(vw, index, node, e){
27332     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
27333 });
27334
27335 // direct load of JSON data
27336 view.load("foobar.php");
27337
27338 // Example from my blog list
27339 var tpl = new Roo.Template(
27340     '&lt;div class="entry"&gt;' +
27341     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
27342     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
27343     "&lt;/div&gt;&lt;hr /&gt;"
27344 );
27345
27346 var moreView = new Roo.JsonView({
27347     container :  "entry-list", 
27348     template : tpl,
27349     jsonRoot: "posts"
27350 });
27351 moreView.on("beforerender", this.sortEntries, this);
27352 moreView.load({
27353     url: "/blog/get-posts.php",
27354     params: "allposts=true",
27355     text: "Loading Blog Entries..."
27356 });
27357 </code></pre>
27358
27359 * Note: old code is supported with arguments : (container, template, config)
27360
27361
27362  * @constructor
27363  * Create a new JsonView
27364  * 
27365  * @param {Object} config The config object
27366  * 
27367  */
27368 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
27369     
27370     
27371     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
27372
27373     var um = this.el.getUpdateManager();
27374     um.setRenderer(this);
27375     um.on("update", this.onLoad, this);
27376     um.on("failure", this.onLoadException, this);
27377
27378     /**
27379      * @event beforerender
27380      * Fires before rendering of the downloaded JSON data.
27381      * @param {Roo.JsonView} this
27382      * @param {Object} data The JSON data loaded
27383      */
27384     /**
27385      * @event load
27386      * Fires when data is loaded.
27387      * @param {Roo.JsonView} this
27388      * @param {Object} data The JSON data loaded
27389      * @param {Object} response The raw Connect response object
27390      */
27391     /**
27392      * @event loadexception
27393      * Fires when loading fails.
27394      * @param {Roo.JsonView} this
27395      * @param {Object} response The raw Connect response object
27396      */
27397     this.addEvents({
27398         'beforerender' : true,
27399         'load' : true,
27400         'loadexception' : true
27401     });
27402 };
27403 Roo.extend(Roo.JsonView, Roo.View, {
27404     /**
27405      * @type {String} The root property in the loaded JSON object that contains the data
27406      */
27407     jsonRoot : "",
27408
27409     /**
27410      * Refreshes the view.
27411      */
27412     refresh : function(){
27413         this.clearSelections();
27414         this.el.update("");
27415         var html = [];
27416         var o = this.jsonData;
27417         if(o && o.length > 0){
27418             for(var i = 0, len = o.length; i < len; i++){
27419                 var data = this.prepareData(o[i], i, o);
27420                 html[html.length] = this.tpl.apply(data);
27421             }
27422         }else{
27423             html.push(this.emptyText);
27424         }
27425         this.el.update(html.join(""));
27426         this.nodes = this.el.dom.childNodes;
27427         this.updateIndexes(0);
27428     },
27429
27430     /**
27431      * 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.
27432      * @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:
27433      <pre><code>
27434      view.load({
27435          url: "your-url.php",
27436          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
27437          callback: yourFunction,
27438          scope: yourObject, //(optional scope)
27439          discardUrl: false,
27440          nocache: false,
27441          text: "Loading...",
27442          timeout: 30,
27443          scripts: false
27444      });
27445      </code></pre>
27446      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
27447      * 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.
27448      * @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}
27449      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
27450      * @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.
27451      */
27452     load : function(){
27453         var um = this.el.getUpdateManager();
27454         um.update.apply(um, arguments);
27455     },
27456
27457     // note - render is a standard framework call...
27458     // using it for the response is really flaky... - it's called by UpdateManager normally, except when called by the XComponent/addXtype.
27459     render : function(el, response){
27460         
27461         this.clearSelections();
27462         this.el.update("");
27463         var o;
27464         try{
27465             if (response != '') {
27466                 o = Roo.util.JSON.decode(response.responseText);
27467                 if(this.jsonRoot){
27468                     
27469                     o = o[this.jsonRoot];
27470                 }
27471             }
27472         } catch(e){
27473         }
27474         /**
27475          * The current JSON data or null
27476          */
27477         this.jsonData = o;
27478         this.beforeRender();
27479         this.refresh();
27480     },
27481
27482 /**
27483  * Get the number of records in the current JSON dataset
27484  * @return {Number}
27485  */
27486     getCount : function(){
27487         return this.jsonData ? this.jsonData.length : 0;
27488     },
27489
27490 /**
27491  * Returns the JSON object for the specified node(s)
27492  * @param {HTMLElement/Array} node The node or an array of nodes
27493  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
27494  * you get the JSON object for the node
27495  */
27496     getNodeData : function(node){
27497         if(node instanceof Array){
27498             var data = [];
27499             for(var i = 0, len = node.length; i < len; i++){
27500                 data.push(this.getNodeData(node[i]));
27501             }
27502             return data;
27503         }
27504         return this.jsonData[this.indexOf(node)] || null;
27505     },
27506
27507     beforeRender : function(){
27508         this.snapshot = this.jsonData;
27509         if(this.sortInfo){
27510             this.sort.apply(this, this.sortInfo);
27511         }
27512         this.fireEvent("beforerender", this, this.jsonData);
27513     },
27514
27515     onLoad : function(el, o){
27516         this.fireEvent("load", this, this.jsonData, o);
27517     },
27518
27519     onLoadException : function(el, o){
27520         this.fireEvent("loadexception", this, o);
27521     },
27522
27523 /**
27524  * Filter the data by a specific property.
27525  * @param {String} property A property on your JSON objects
27526  * @param {String/RegExp} value Either string that the property values
27527  * should start with, or a RegExp to test against the property
27528  */
27529     filter : function(property, value){
27530         if(this.jsonData){
27531             var data = [];
27532             var ss = this.snapshot;
27533             if(typeof value == "string"){
27534                 var vlen = value.length;
27535                 if(vlen == 0){
27536                     this.clearFilter();
27537                     return;
27538                 }
27539                 value = value.toLowerCase();
27540                 for(var i = 0, len = ss.length; i < len; i++){
27541                     var o = ss[i];
27542                     if(o[property].substr(0, vlen).toLowerCase() == value){
27543                         data.push(o);
27544                     }
27545                 }
27546             } else if(value.exec){ // regex?
27547                 for(var i = 0, len = ss.length; i < len; i++){
27548                     var o = ss[i];
27549                     if(value.test(o[property])){
27550                         data.push(o);
27551                     }
27552                 }
27553             } else{
27554                 return;
27555             }
27556             this.jsonData = data;
27557             this.refresh();
27558         }
27559     },
27560
27561 /**
27562  * Filter by a function. The passed function will be called with each
27563  * object in the current dataset. If the function returns true the value is kept,
27564  * otherwise it is filtered.
27565  * @param {Function} fn
27566  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
27567  */
27568     filterBy : function(fn, scope){
27569         if(this.jsonData){
27570             var data = [];
27571             var ss = this.snapshot;
27572             for(var i = 0, len = ss.length; i < len; i++){
27573                 var o = ss[i];
27574                 if(fn.call(scope || this, o)){
27575                     data.push(o);
27576                 }
27577             }
27578             this.jsonData = data;
27579             this.refresh();
27580         }
27581     },
27582
27583 /**
27584  * Clears the current filter.
27585  */
27586     clearFilter : function(){
27587         if(this.snapshot && this.jsonData != this.snapshot){
27588             this.jsonData = this.snapshot;
27589             this.refresh();
27590         }
27591     },
27592
27593
27594 /**
27595  * Sorts the data for this view and refreshes it.
27596  * @param {String} property A property on your JSON objects to sort on
27597  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
27598  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
27599  */
27600     sort : function(property, dir, sortType){
27601         this.sortInfo = Array.prototype.slice.call(arguments, 0);
27602         if(this.jsonData){
27603             var p = property;
27604             var dsc = dir && dir.toLowerCase() == "desc";
27605             var f = function(o1, o2){
27606                 var v1 = sortType ? sortType(o1[p]) : o1[p];
27607                 var v2 = sortType ? sortType(o2[p]) : o2[p];
27608                 ;
27609                 if(v1 < v2){
27610                     return dsc ? +1 : -1;
27611                 } else if(v1 > v2){
27612                     return dsc ? -1 : +1;
27613                 } else{
27614                     return 0;
27615                 }
27616             };
27617             this.jsonData.sort(f);
27618             this.refresh();
27619             if(this.jsonData != this.snapshot){
27620                 this.snapshot.sort(f);
27621             }
27622         }
27623     }
27624 });/*
27625  * Based on:
27626  * Ext JS Library 1.1.1
27627  * Copyright(c) 2006-2007, Ext JS, LLC.
27628  *
27629  * Originally Released Under LGPL - original licence link has changed is not relivant.
27630  *
27631  * Fork - LGPL
27632  * <script type="text/javascript">
27633  */
27634  
27635
27636 /**
27637  * @class Roo.ColorPalette
27638  * @extends Roo.Component
27639  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
27640  * Here's an example of typical usage:
27641  * <pre><code>
27642 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
27643 cp.render('my-div');
27644
27645 cp.on('select', function(palette, selColor){
27646     // do something with selColor
27647 });
27648 </code></pre>
27649  * @constructor
27650  * Create a new ColorPalette
27651  * @param {Object} config The config object
27652  */
27653 Roo.ColorPalette = function(config){
27654     Roo.ColorPalette.superclass.constructor.call(this, config);
27655     this.addEvents({
27656         /**
27657              * @event select
27658              * Fires when a color is selected
27659              * @param {ColorPalette} this
27660              * @param {String} color The 6-digit color hex code (without the # symbol)
27661              */
27662         select: true
27663     });
27664
27665     if(this.handler){
27666         this.on("select", this.handler, this.scope, true);
27667     }
27668 };
27669 Roo.extend(Roo.ColorPalette, Roo.Component, {
27670     /**
27671      * @cfg {String} itemCls
27672      * The CSS class to apply to the containing element (defaults to "x-color-palette")
27673      */
27674     itemCls : "x-color-palette",
27675     /**
27676      * @cfg {String} value
27677      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
27678      * the hex codes are case-sensitive.
27679      */
27680     value : null,
27681     clickEvent:'click',
27682     // private
27683     ctype: "Roo.ColorPalette",
27684
27685     /**
27686      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
27687      */
27688     allowReselect : false,
27689
27690     /**
27691      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
27692      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
27693      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
27694      * of colors with the width setting until the box is symmetrical.</p>
27695      * <p>You can override individual colors if needed:</p>
27696      * <pre><code>
27697 var cp = new Roo.ColorPalette();
27698 cp.colors[0] = "FF0000";  // change the first box to red
27699 </code></pre>
27700
27701 Or you can provide a custom array of your own for complete control:
27702 <pre><code>
27703 var cp = new Roo.ColorPalette();
27704 cp.colors = ["000000", "993300", "333300"];
27705 </code></pre>
27706      * @type Array
27707      */
27708     colors : [
27709         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
27710         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
27711         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
27712         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
27713         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
27714     ],
27715
27716     // private
27717     onRender : function(container, position){
27718         var t = new Roo.MasterTemplate(
27719             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
27720         );
27721         var c = this.colors;
27722         for(var i = 0, len = c.length; i < len; i++){
27723             t.add([c[i]]);
27724         }
27725         var el = document.createElement("div");
27726         el.className = this.itemCls;
27727         t.overwrite(el);
27728         container.dom.insertBefore(el, position);
27729         this.el = Roo.get(el);
27730         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
27731         if(this.clickEvent != 'click'){
27732             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
27733         }
27734     },
27735
27736     // private
27737     afterRender : function(){
27738         Roo.ColorPalette.superclass.afterRender.call(this);
27739         if(this.value){
27740             var s = this.value;
27741             this.value = null;
27742             this.select(s);
27743         }
27744     },
27745
27746     // private
27747     handleClick : function(e, t){
27748         e.preventDefault();
27749         if(!this.disabled){
27750             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
27751             this.select(c.toUpperCase());
27752         }
27753     },
27754
27755     /**
27756      * Selects the specified color in the palette (fires the select event)
27757      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
27758      */
27759     select : function(color){
27760         color = color.replace("#", "");
27761         if(color != this.value || this.allowReselect){
27762             var el = this.el;
27763             if(this.value){
27764                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
27765             }
27766             el.child("a.color-"+color).addClass("x-color-palette-sel");
27767             this.value = color;
27768             this.fireEvent("select", this, color);
27769         }
27770     }
27771 });/*
27772  * Based on:
27773  * Ext JS Library 1.1.1
27774  * Copyright(c) 2006-2007, Ext JS, LLC.
27775  *
27776  * Originally Released Under LGPL - original licence link has changed is not relivant.
27777  *
27778  * Fork - LGPL
27779  * <script type="text/javascript">
27780  */
27781  
27782 /**
27783  * @class Roo.DatePicker
27784  * @extends Roo.Component
27785  * Simple date picker class.
27786  * @constructor
27787  * Create a new DatePicker
27788  * @param {Object} config The config object
27789  */
27790 Roo.DatePicker = function(config){
27791     Roo.DatePicker.superclass.constructor.call(this, config);
27792
27793     this.value = config && config.value ?
27794                  config.value.clearTime() : new Date().clearTime();
27795
27796     this.addEvents({
27797         /**
27798              * @event select
27799              * Fires when a date is selected
27800              * @param {DatePicker} this
27801              * @param {Date} date The selected date
27802              */
27803         'select': true,
27804         /**
27805              * @event monthchange
27806              * Fires when the displayed month changes 
27807              * @param {DatePicker} this
27808              * @param {Date} date The selected month
27809              */
27810         'monthchange': true
27811     });
27812
27813     if(this.handler){
27814         this.on("select", this.handler,  this.scope || this);
27815     }
27816     // build the disabledDatesRE
27817     if(!this.disabledDatesRE && this.disabledDates){
27818         var dd = this.disabledDates;
27819         var re = "(?:";
27820         for(var i = 0; i < dd.length; i++){
27821             re += dd[i];
27822             if(i != dd.length-1) {
27823                 re += "|";
27824             }
27825         }
27826         this.disabledDatesRE = new RegExp(re + ")");
27827     }
27828 };
27829
27830 Roo.extend(Roo.DatePicker, Roo.Component, {
27831     /**
27832      * @cfg {String} todayText
27833      * The text to display on the button that selects the current date (defaults to "Today")
27834      */
27835     todayText : "Today",
27836     /**
27837      * @cfg {String} okText
27838      * The text to display on the ok button
27839      */
27840     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
27841     /**
27842      * @cfg {String} cancelText
27843      * The text to display on the cancel button
27844      */
27845     cancelText : "Cancel",
27846     /**
27847      * @cfg {String} todayTip
27848      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
27849      */
27850     todayTip : "{0} (Spacebar)",
27851     /**
27852      * @cfg {Date} minDate
27853      * Minimum allowable date (JavaScript date object, defaults to null)
27854      */
27855     minDate : null,
27856     /**
27857      * @cfg {Date} maxDate
27858      * Maximum allowable date (JavaScript date object, defaults to null)
27859      */
27860     maxDate : null,
27861     /**
27862      * @cfg {String} minText
27863      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
27864      */
27865     minText : "This date is before the minimum date",
27866     /**
27867      * @cfg {String} maxText
27868      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
27869      */
27870     maxText : "This date is after the maximum date",
27871     /**
27872      * @cfg {String} format
27873      * The default date format string which can be overriden for localization support.  The format must be
27874      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
27875      */
27876     format : "m/d/y",
27877     /**
27878      * @cfg {Array} disabledDays
27879      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
27880      */
27881     disabledDays : null,
27882     /**
27883      * @cfg {String} disabledDaysText
27884      * The tooltip to display when the date falls on a disabled day (defaults to "")
27885      */
27886     disabledDaysText : "",
27887     /**
27888      * @cfg {RegExp} disabledDatesRE
27889      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
27890      */
27891     disabledDatesRE : null,
27892     /**
27893      * @cfg {String} disabledDatesText
27894      * The tooltip text to display when the date falls on a disabled date (defaults to "")
27895      */
27896     disabledDatesText : "",
27897     /**
27898      * @cfg {Boolean} constrainToViewport
27899      * True to constrain the date picker to the viewport (defaults to true)
27900      */
27901     constrainToViewport : true,
27902     /**
27903      * @cfg {Array} monthNames
27904      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
27905      */
27906     monthNames : Date.monthNames,
27907     /**
27908      * @cfg {Array} dayNames
27909      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
27910      */
27911     dayNames : Date.dayNames,
27912     /**
27913      * @cfg {String} nextText
27914      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
27915      */
27916     nextText: 'Next Month (Control+Right)',
27917     /**
27918      * @cfg {String} prevText
27919      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
27920      */
27921     prevText: 'Previous Month (Control+Left)',
27922     /**
27923      * @cfg {String} monthYearText
27924      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
27925      */
27926     monthYearText: 'Choose a month (Control+Up/Down to move years)',
27927     /**
27928      * @cfg {Number} startDay
27929      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
27930      */
27931     startDay : 0,
27932     /**
27933      * @cfg {Bool} showClear
27934      * Show a clear button (usefull for date form elements that can be blank.)
27935      */
27936     
27937     showClear: false,
27938     
27939     /**
27940      * Sets the value of the date field
27941      * @param {Date} value The date to set
27942      */
27943     setValue : function(value){
27944         var old = this.value;
27945         
27946         if (typeof(value) == 'string') {
27947          
27948             value = Date.parseDate(value, this.format);
27949         }
27950         if (!value) {
27951             value = new Date();
27952         }
27953         
27954         this.value = value.clearTime(true);
27955         if(this.el){
27956             this.update(this.value);
27957         }
27958     },
27959
27960     /**
27961      * Gets the current selected value of the date field
27962      * @return {Date} The selected date
27963      */
27964     getValue : function(){
27965         return this.value;
27966     },
27967
27968     // private
27969     focus : function(){
27970         if(this.el){
27971             this.update(this.activeDate);
27972         }
27973     },
27974
27975     // privateval
27976     onRender : function(container, position){
27977         
27978         var m = [
27979              '<table cellspacing="0">',
27980                 '<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>',
27981                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
27982         var dn = this.dayNames;
27983         for(var i = 0; i < 7; i++){
27984             var d = this.startDay+i;
27985             if(d > 6){
27986                 d = d-7;
27987             }
27988             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
27989         }
27990         m[m.length] = "</tr></thead><tbody><tr>";
27991         for(var i = 0; i < 42; i++) {
27992             if(i % 7 == 0 && i != 0){
27993                 m[m.length] = "</tr><tr>";
27994             }
27995             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
27996         }
27997         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
27998             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
27999
28000         var el = document.createElement("div");
28001         el.className = "x-date-picker";
28002         el.innerHTML = m.join("");
28003
28004         container.dom.insertBefore(el, position);
28005
28006         this.el = Roo.get(el);
28007         this.eventEl = Roo.get(el.firstChild);
28008
28009         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
28010             handler: this.showPrevMonth,
28011             scope: this,
28012             preventDefault:true,
28013             stopDefault:true
28014         });
28015
28016         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
28017             handler: this.showNextMonth,
28018             scope: this,
28019             preventDefault:true,
28020             stopDefault:true
28021         });
28022
28023         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
28024
28025         this.monthPicker = this.el.down('div.x-date-mp');
28026         this.monthPicker.enableDisplayMode('block');
28027         
28028         var kn = new Roo.KeyNav(this.eventEl, {
28029             "left" : function(e){
28030                 e.ctrlKey ?
28031                     this.showPrevMonth() :
28032                     this.update(this.activeDate.add("d", -1));
28033             },
28034
28035             "right" : function(e){
28036                 e.ctrlKey ?
28037                     this.showNextMonth() :
28038                     this.update(this.activeDate.add("d", 1));
28039             },
28040
28041             "up" : function(e){
28042                 e.ctrlKey ?
28043                     this.showNextYear() :
28044                     this.update(this.activeDate.add("d", -7));
28045             },
28046
28047             "down" : function(e){
28048                 e.ctrlKey ?
28049                     this.showPrevYear() :
28050                     this.update(this.activeDate.add("d", 7));
28051             },
28052
28053             "pageUp" : function(e){
28054                 this.showNextMonth();
28055             },
28056
28057             "pageDown" : function(e){
28058                 this.showPrevMonth();
28059             },
28060
28061             "enter" : function(e){
28062                 e.stopPropagation();
28063                 return true;
28064             },
28065
28066             scope : this
28067         });
28068
28069         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
28070
28071         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
28072
28073         this.el.unselectable();
28074         
28075         this.cells = this.el.select("table.x-date-inner tbody td");
28076         this.textNodes = this.el.query("table.x-date-inner tbody span");
28077
28078         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
28079             text: "&#160;",
28080             tooltip: this.monthYearText
28081         });
28082
28083         this.mbtn.on('click', this.showMonthPicker, this);
28084         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
28085
28086
28087         var today = (new Date()).dateFormat(this.format);
28088         
28089         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
28090         if (this.showClear) {
28091             baseTb.add( new Roo.Toolbar.Fill());
28092         }
28093         baseTb.add({
28094             text: String.format(this.todayText, today),
28095             tooltip: String.format(this.todayTip, today),
28096             handler: this.selectToday,
28097             scope: this
28098         });
28099         
28100         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
28101             
28102         //});
28103         if (this.showClear) {
28104             
28105             baseTb.add( new Roo.Toolbar.Fill());
28106             baseTb.add({
28107                 text: '&#160;',
28108                 cls: 'x-btn-icon x-btn-clear',
28109                 handler: function() {
28110                     //this.value = '';
28111                     this.fireEvent("select", this, '');
28112                 },
28113                 scope: this
28114             });
28115         }
28116         
28117         
28118         if(Roo.isIE){
28119             this.el.repaint();
28120         }
28121         this.update(this.value);
28122     },
28123
28124     createMonthPicker : function(){
28125         if(!this.monthPicker.dom.firstChild){
28126             var buf = ['<table border="0" cellspacing="0">'];
28127             for(var i = 0; i < 6; i++){
28128                 buf.push(
28129                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
28130                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
28131                     i == 0 ?
28132                     '<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>' :
28133                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
28134                 );
28135             }
28136             buf.push(
28137                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
28138                     this.okText,
28139                     '</button><button type="button" class="x-date-mp-cancel">',
28140                     this.cancelText,
28141                     '</button></td></tr>',
28142                 '</table>'
28143             );
28144             this.monthPicker.update(buf.join(''));
28145             this.monthPicker.on('click', this.onMonthClick, this);
28146             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
28147
28148             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
28149             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
28150
28151             this.mpMonths.each(function(m, a, i){
28152                 i += 1;
28153                 if((i%2) == 0){
28154                     m.dom.xmonth = 5 + Math.round(i * .5);
28155                 }else{
28156                     m.dom.xmonth = Math.round((i-1) * .5);
28157                 }
28158             });
28159         }
28160     },
28161
28162     showMonthPicker : function(){
28163         this.createMonthPicker();
28164         var size = this.el.getSize();
28165         this.monthPicker.setSize(size);
28166         this.monthPicker.child('table').setSize(size);
28167
28168         this.mpSelMonth = (this.activeDate || this.value).getMonth();
28169         this.updateMPMonth(this.mpSelMonth);
28170         this.mpSelYear = (this.activeDate || this.value).getFullYear();
28171         this.updateMPYear(this.mpSelYear);
28172
28173         this.monthPicker.slideIn('t', {duration:.2});
28174     },
28175
28176     updateMPYear : function(y){
28177         this.mpyear = y;
28178         var ys = this.mpYears.elements;
28179         for(var i = 1; i <= 10; i++){
28180             var td = ys[i-1], y2;
28181             if((i%2) == 0){
28182                 y2 = y + Math.round(i * .5);
28183                 td.firstChild.innerHTML = y2;
28184                 td.xyear = y2;
28185             }else{
28186                 y2 = y - (5-Math.round(i * .5));
28187                 td.firstChild.innerHTML = y2;
28188                 td.xyear = y2;
28189             }
28190             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
28191         }
28192     },
28193
28194     updateMPMonth : function(sm){
28195         this.mpMonths.each(function(m, a, i){
28196             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
28197         });
28198     },
28199
28200     selectMPMonth: function(m){
28201         
28202     },
28203
28204     onMonthClick : function(e, t){
28205         e.stopEvent();
28206         var el = new Roo.Element(t), pn;
28207         if(el.is('button.x-date-mp-cancel')){
28208             this.hideMonthPicker();
28209         }
28210         else if(el.is('button.x-date-mp-ok')){
28211             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
28212             this.hideMonthPicker();
28213         }
28214         else if(pn = el.up('td.x-date-mp-month', 2)){
28215             this.mpMonths.removeClass('x-date-mp-sel');
28216             pn.addClass('x-date-mp-sel');
28217             this.mpSelMonth = pn.dom.xmonth;
28218         }
28219         else if(pn = el.up('td.x-date-mp-year', 2)){
28220             this.mpYears.removeClass('x-date-mp-sel');
28221             pn.addClass('x-date-mp-sel');
28222             this.mpSelYear = pn.dom.xyear;
28223         }
28224         else if(el.is('a.x-date-mp-prev')){
28225             this.updateMPYear(this.mpyear-10);
28226         }
28227         else if(el.is('a.x-date-mp-next')){
28228             this.updateMPYear(this.mpyear+10);
28229         }
28230     },
28231
28232     onMonthDblClick : function(e, t){
28233         e.stopEvent();
28234         var el = new Roo.Element(t), pn;
28235         if(pn = el.up('td.x-date-mp-month', 2)){
28236             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
28237             this.hideMonthPicker();
28238         }
28239         else if(pn = el.up('td.x-date-mp-year', 2)){
28240             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
28241             this.hideMonthPicker();
28242         }
28243     },
28244
28245     hideMonthPicker : function(disableAnim){
28246         if(this.monthPicker){
28247             if(disableAnim === true){
28248                 this.monthPicker.hide();
28249             }else{
28250                 this.monthPicker.slideOut('t', {duration:.2});
28251             }
28252         }
28253     },
28254
28255     // private
28256     showPrevMonth : function(e){
28257         this.update(this.activeDate.add("mo", -1));
28258     },
28259
28260     // private
28261     showNextMonth : function(e){
28262         this.update(this.activeDate.add("mo", 1));
28263     },
28264
28265     // private
28266     showPrevYear : function(){
28267         this.update(this.activeDate.add("y", -1));
28268     },
28269
28270     // private
28271     showNextYear : function(){
28272         this.update(this.activeDate.add("y", 1));
28273     },
28274
28275     // private
28276     handleMouseWheel : function(e){
28277         var delta = e.getWheelDelta();
28278         if(delta > 0){
28279             this.showPrevMonth();
28280             e.stopEvent();
28281         } else if(delta < 0){
28282             this.showNextMonth();
28283             e.stopEvent();
28284         }
28285     },
28286
28287     // private
28288     handleDateClick : function(e, t){
28289         e.stopEvent();
28290         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
28291             this.setValue(new Date(t.dateValue));
28292             this.fireEvent("select", this, this.value);
28293         }
28294     },
28295
28296     // private
28297     selectToday : function(){
28298         this.setValue(new Date().clearTime());
28299         this.fireEvent("select", this, this.value);
28300     },
28301
28302     // private
28303     update : function(date)
28304     {
28305         var vd = this.activeDate;
28306         this.activeDate = date;
28307         if(vd && this.el){
28308             var t = date.getTime();
28309             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
28310                 this.cells.removeClass("x-date-selected");
28311                 this.cells.each(function(c){
28312                    if(c.dom.firstChild.dateValue == t){
28313                        c.addClass("x-date-selected");
28314                        setTimeout(function(){
28315                             try{c.dom.firstChild.focus();}catch(e){}
28316                        }, 50);
28317                        return false;
28318                    }
28319                 });
28320                 return;
28321             }
28322         }
28323         
28324         var days = date.getDaysInMonth();
28325         var firstOfMonth = date.getFirstDateOfMonth();
28326         var startingPos = firstOfMonth.getDay()-this.startDay;
28327
28328         if(startingPos <= this.startDay){
28329             startingPos += 7;
28330         }
28331
28332         var pm = date.add("mo", -1);
28333         var prevStart = pm.getDaysInMonth()-startingPos;
28334
28335         var cells = this.cells.elements;
28336         var textEls = this.textNodes;
28337         days += startingPos;
28338
28339         // convert everything to numbers so it's fast
28340         var day = 86400000;
28341         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
28342         var today = new Date().clearTime().getTime();
28343         var sel = date.clearTime().getTime();
28344         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
28345         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
28346         var ddMatch = this.disabledDatesRE;
28347         var ddText = this.disabledDatesText;
28348         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
28349         var ddaysText = this.disabledDaysText;
28350         var format = this.format;
28351
28352         var setCellClass = function(cal, cell){
28353             cell.title = "";
28354             var t = d.getTime();
28355             cell.firstChild.dateValue = t;
28356             if(t == today){
28357                 cell.className += " x-date-today";
28358                 cell.title = cal.todayText;
28359             }
28360             if(t == sel){
28361                 cell.className += " x-date-selected";
28362                 setTimeout(function(){
28363                     try{cell.firstChild.focus();}catch(e){}
28364                 }, 50);
28365             }
28366             // disabling
28367             if(t < min) {
28368                 cell.className = " x-date-disabled";
28369                 cell.title = cal.minText;
28370                 return;
28371             }
28372             if(t > max) {
28373                 cell.className = " x-date-disabled";
28374                 cell.title = cal.maxText;
28375                 return;
28376             }
28377             if(ddays){
28378                 if(ddays.indexOf(d.getDay()) != -1){
28379                     cell.title = ddaysText;
28380                     cell.className = " x-date-disabled";
28381                 }
28382             }
28383             if(ddMatch && format){
28384                 var fvalue = d.dateFormat(format);
28385                 if(ddMatch.test(fvalue)){
28386                     cell.title = ddText.replace("%0", fvalue);
28387                     cell.className = " x-date-disabled";
28388                 }
28389             }
28390         };
28391
28392         var i = 0;
28393         for(; i < startingPos; i++) {
28394             textEls[i].innerHTML = (++prevStart);
28395             d.setDate(d.getDate()+1);
28396             cells[i].className = "x-date-prevday";
28397             setCellClass(this, cells[i]);
28398         }
28399         for(; i < days; i++){
28400             intDay = i - startingPos + 1;
28401             textEls[i].innerHTML = (intDay);
28402             d.setDate(d.getDate()+1);
28403             cells[i].className = "x-date-active";
28404             setCellClass(this, cells[i]);
28405         }
28406         var extraDays = 0;
28407         for(; i < 42; i++) {
28408              textEls[i].innerHTML = (++extraDays);
28409              d.setDate(d.getDate()+1);
28410              cells[i].className = "x-date-nextday";
28411              setCellClass(this, cells[i]);
28412         }
28413
28414         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
28415         this.fireEvent('monthchange', this, date);
28416         
28417         if(!this.internalRender){
28418             var main = this.el.dom.firstChild;
28419             var w = main.offsetWidth;
28420             this.el.setWidth(w + this.el.getBorderWidth("lr"));
28421             Roo.fly(main).setWidth(w);
28422             this.internalRender = true;
28423             // opera does not respect the auto grow header center column
28424             // then, after it gets a width opera refuses to recalculate
28425             // without a second pass
28426             if(Roo.isOpera && !this.secondPass){
28427                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
28428                 this.secondPass = true;
28429                 this.update.defer(10, this, [date]);
28430             }
28431         }
28432         
28433         
28434     }
28435 });        /*
28436  * Based on:
28437  * Ext JS Library 1.1.1
28438  * Copyright(c) 2006-2007, Ext JS, LLC.
28439  *
28440  * Originally Released Under LGPL - original licence link has changed is not relivant.
28441  *
28442  * Fork - LGPL
28443  * <script type="text/javascript">
28444  */
28445 /**
28446  * @class Roo.TabPanel
28447  * @extends Roo.util.Observable
28448  * A lightweight tab container.
28449  * <br><br>
28450  * Usage:
28451  * <pre><code>
28452 // basic tabs 1, built from existing content
28453 var tabs = new Roo.TabPanel("tabs1");
28454 tabs.addTab("script", "View Script");
28455 tabs.addTab("markup", "View Markup");
28456 tabs.activate("script");
28457
28458 // more advanced tabs, built from javascript
28459 var jtabs = new Roo.TabPanel("jtabs");
28460 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
28461
28462 // set up the UpdateManager
28463 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
28464 var updater = tab2.getUpdateManager();
28465 updater.setDefaultUrl("ajax1.htm");
28466 tab2.on('activate', updater.refresh, updater, true);
28467
28468 // Use setUrl for Ajax loading
28469 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
28470 tab3.setUrl("ajax2.htm", null, true);
28471
28472 // Disabled tab
28473 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
28474 tab4.disable();
28475
28476 jtabs.activate("jtabs-1");
28477  * </code></pre>
28478  * @constructor
28479  * Create a new TabPanel.
28480  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
28481  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
28482  */
28483 Roo.TabPanel = function(container, config){
28484     /**
28485     * The container element for this TabPanel.
28486     * @type Roo.Element
28487     */
28488     this.el = Roo.get(container, true);
28489     if(config){
28490         if(typeof config == "boolean"){
28491             this.tabPosition = config ? "bottom" : "top";
28492         }else{
28493             Roo.apply(this, config);
28494         }
28495     }
28496     if(this.tabPosition == "bottom"){
28497         this.bodyEl = Roo.get(this.createBody(this.el.dom));
28498         this.el.addClass("x-tabs-bottom");
28499     }
28500     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
28501     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
28502     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
28503     if(Roo.isIE){
28504         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
28505     }
28506     if(this.tabPosition != "bottom"){
28507         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
28508          * @type Roo.Element
28509          */
28510         this.bodyEl = Roo.get(this.createBody(this.el.dom));
28511         this.el.addClass("x-tabs-top");
28512     }
28513     this.items = [];
28514
28515     this.bodyEl.setStyle("position", "relative");
28516
28517     this.active = null;
28518     this.activateDelegate = this.activate.createDelegate(this);
28519
28520     this.addEvents({
28521         /**
28522          * @event tabchange
28523          * Fires when the active tab changes
28524          * @param {Roo.TabPanel} this
28525          * @param {Roo.TabPanelItem} activePanel The new active tab
28526          */
28527         "tabchange": true,
28528         /**
28529          * @event beforetabchange
28530          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
28531          * @param {Roo.TabPanel} this
28532          * @param {Object} e Set cancel to true on this object to cancel the tab change
28533          * @param {Roo.TabPanelItem} tab The tab being changed to
28534          */
28535         "beforetabchange" : true
28536     });
28537
28538     Roo.EventManager.onWindowResize(this.onResize, this);
28539     this.cpad = this.el.getPadding("lr");
28540     this.hiddenCount = 0;
28541
28542
28543     // toolbar on the tabbar support...
28544     if (this.toolbar) {
28545         var tcfg = this.toolbar;
28546         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
28547         this.toolbar = new Roo.Toolbar(tcfg);
28548         if (Roo.isSafari) {
28549             var tbl = tcfg.container.child('table', true);
28550             tbl.setAttribute('width', '100%');
28551         }
28552         
28553     }
28554    
28555
28556
28557     Roo.TabPanel.superclass.constructor.call(this);
28558 };
28559
28560 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
28561     /*
28562      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
28563      */
28564     tabPosition : "top",
28565     /*
28566      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
28567      */
28568     currentTabWidth : 0,
28569     /*
28570      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
28571      */
28572     minTabWidth : 40,
28573     /*
28574      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
28575      */
28576     maxTabWidth : 250,
28577     /*
28578      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
28579      */
28580     preferredTabWidth : 175,
28581     /*
28582      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
28583      */
28584     resizeTabs : false,
28585     /*
28586      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
28587      */
28588     monitorResize : true,
28589     /*
28590      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
28591      */
28592     toolbar : false,
28593
28594     /**
28595      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
28596      * @param {String} id The id of the div to use <b>or create</b>
28597      * @param {String} text The text for the tab
28598      * @param {String} content (optional) Content to put in the TabPanelItem body
28599      * @param {Boolean} closable (optional) True to create a close icon on the tab
28600      * @return {Roo.TabPanelItem} The created TabPanelItem
28601      */
28602     addTab : function(id, text, content, closable){
28603         var item = new Roo.TabPanelItem(this, id, text, closable);
28604         this.addTabItem(item);
28605         if(content){
28606             item.setContent(content);
28607         }
28608         return item;
28609     },
28610
28611     /**
28612      * Returns the {@link Roo.TabPanelItem} with the specified id/index
28613      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
28614      * @return {Roo.TabPanelItem}
28615      */
28616     getTab : function(id){
28617         return this.items[id];
28618     },
28619
28620     /**
28621      * Hides the {@link Roo.TabPanelItem} with the specified id/index
28622      * @param {String/Number} id The id or index of the TabPanelItem to hide.
28623      */
28624     hideTab : function(id){
28625         var t = this.items[id];
28626         if(!t.isHidden()){
28627            t.setHidden(true);
28628            this.hiddenCount++;
28629            this.autoSizeTabs();
28630         }
28631     },
28632
28633     /**
28634      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
28635      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
28636      */
28637     unhideTab : function(id){
28638         var t = this.items[id];
28639         if(t.isHidden()){
28640            t.setHidden(false);
28641            this.hiddenCount--;
28642            this.autoSizeTabs();
28643         }
28644     },
28645
28646     /**
28647      * Adds an existing {@link Roo.TabPanelItem}.
28648      * @param {Roo.TabPanelItem} item The TabPanelItem to add
28649      */
28650     addTabItem : function(item){
28651         this.items[item.id] = item;
28652         this.items.push(item);
28653         if(this.resizeTabs){
28654            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
28655            this.autoSizeTabs();
28656         }else{
28657             item.autoSize();
28658         }
28659     },
28660
28661     /**
28662      * Removes a {@link Roo.TabPanelItem}.
28663      * @param {String/Number} id The id or index of the TabPanelItem to remove.
28664      */
28665     removeTab : function(id){
28666         var items = this.items;
28667         var tab = items[id];
28668         if(!tab) { return; }
28669         var index = items.indexOf(tab);
28670         if(this.active == tab && items.length > 1){
28671             var newTab = this.getNextAvailable(index);
28672             if(newTab) {
28673                 newTab.activate();
28674             }
28675         }
28676         this.stripEl.dom.removeChild(tab.pnode.dom);
28677         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
28678             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
28679         }
28680         items.splice(index, 1);
28681         delete this.items[tab.id];
28682         tab.fireEvent("close", tab);
28683         tab.purgeListeners();
28684         this.autoSizeTabs();
28685     },
28686
28687     getNextAvailable : function(start){
28688         var items = this.items;
28689         var index = start;
28690         // look for a next tab that will slide over to
28691         // replace the one being removed
28692         while(index < items.length){
28693             var item = items[++index];
28694             if(item && !item.isHidden()){
28695                 return item;
28696             }
28697         }
28698         // if one isn't found select the previous tab (on the left)
28699         index = start;
28700         while(index >= 0){
28701             var item = items[--index];
28702             if(item && !item.isHidden()){
28703                 return item;
28704             }
28705         }
28706         return null;
28707     },
28708
28709     /**
28710      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
28711      * @param {String/Number} id The id or index of the TabPanelItem to disable.
28712      */
28713     disableTab : function(id){
28714         var tab = this.items[id];
28715         if(tab && this.active != tab){
28716             tab.disable();
28717         }
28718     },
28719
28720     /**
28721      * Enables a {@link Roo.TabPanelItem} that is disabled.
28722      * @param {String/Number} id The id or index of the TabPanelItem to enable.
28723      */
28724     enableTab : function(id){
28725         var tab = this.items[id];
28726         tab.enable();
28727     },
28728
28729     /**
28730      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
28731      * @param {String/Number} id The id or index of the TabPanelItem to activate.
28732      * @return {Roo.TabPanelItem} The TabPanelItem.
28733      */
28734     activate : function(id){
28735         var tab = this.items[id];
28736         if(!tab){
28737             return null;
28738         }
28739         if(tab == this.active || tab.disabled){
28740             return tab;
28741         }
28742         var e = {};
28743         this.fireEvent("beforetabchange", this, e, tab);
28744         if(e.cancel !== true && !tab.disabled){
28745             if(this.active){
28746                 this.active.hide();
28747             }
28748             this.active = this.items[id];
28749             this.active.show();
28750             this.fireEvent("tabchange", this, this.active);
28751         }
28752         return tab;
28753     },
28754
28755     /**
28756      * Gets the active {@link Roo.TabPanelItem}.
28757      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
28758      */
28759     getActiveTab : function(){
28760         return this.active;
28761     },
28762
28763     /**
28764      * Updates the tab body element to fit the height of the container element
28765      * for overflow scrolling
28766      * @param {Number} targetHeight (optional) Override the starting height from the elements height
28767      */
28768     syncHeight : function(targetHeight){
28769         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
28770         var bm = this.bodyEl.getMargins();
28771         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
28772         this.bodyEl.setHeight(newHeight);
28773         return newHeight;
28774     },
28775
28776     onResize : function(){
28777         if(this.monitorResize){
28778             this.autoSizeTabs();
28779         }
28780     },
28781
28782     /**
28783      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
28784      */
28785     beginUpdate : function(){
28786         this.updating = true;
28787     },
28788
28789     /**
28790      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
28791      */
28792     endUpdate : function(){
28793         this.updating = false;
28794         this.autoSizeTabs();
28795     },
28796
28797     /**
28798      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
28799      */
28800     autoSizeTabs : function(){
28801         var count = this.items.length;
28802         var vcount = count - this.hiddenCount;
28803         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
28804             return;
28805         }
28806         var w = Math.max(this.el.getWidth() - this.cpad, 10);
28807         var availWidth = Math.floor(w / vcount);
28808         var b = this.stripBody;
28809         if(b.getWidth() > w){
28810             var tabs = this.items;
28811             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
28812             if(availWidth < this.minTabWidth){
28813                 /*if(!this.sleft){    // incomplete scrolling code
28814                     this.createScrollButtons();
28815                 }
28816                 this.showScroll();
28817                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
28818             }
28819         }else{
28820             if(this.currentTabWidth < this.preferredTabWidth){
28821                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
28822             }
28823         }
28824     },
28825
28826     /**
28827      * Returns the number of tabs in this TabPanel.
28828      * @return {Number}
28829      */
28830      getCount : function(){
28831          return this.items.length;
28832      },
28833
28834     /**
28835      * Resizes all the tabs to the passed width
28836      * @param {Number} The new width
28837      */
28838     setTabWidth : function(width){
28839         this.currentTabWidth = width;
28840         for(var i = 0, len = this.items.length; i < len; i++) {
28841                 if(!this.items[i].isHidden()) {
28842                 this.items[i].setWidth(width);
28843             }
28844         }
28845     },
28846
28847     /**
28848      * Destroys this TabPanel
28849      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
28850      */
28851     destroy : function(removeEl){
28852         Roo.EventManager.removeResizeListener(this.onResize, this);
28853         for(var i = 0, len = this.items.length; i < len; i++){
28854             this.items[i].purgeListeners();
28855         }
28856         if(removeEl === true){
28857             this.el.update("");
28858             this.el.remove();
28859         }
28860     }
28861 });
28862
28863 /**
28864  * @class Roo.TabPanelItem
28865  * @extends Roo.util.Observable
28866  * Represents an individual item (tab plus body) in a TabPanel.
28867  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
28868  * @param {String} id The id of this TabPanelItem
28869  * @param {String} text The text for the tab of this TabPanelItem
28870  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
28871  */
28872 Roo.TabPanelItem = function(tabPanel, id, text, closable){
28873     /**
28874      * The {@link Roo.TabPanel} this TabPanelItem belongs to
28875      * @type Roo.TabPanel
28876      */
28877     this.tabPanel = tabPanel;
28878     /**
28879      * The id for this TabPanelItem
28880      * @type String
28881      */
28882     this.id = id;
28883     /** @private */
28884     this.disabled = false;
28885     /** @private */
28886     this.text = text;
28887     /** @private */
28888     this.loaded = false;
28889     this.closable = closable;
28890
28891     /**
28892      * The body element for this TabPanelItem.
28893      * @type Roo.Element
28894      */
28895     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
28896     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
28897     this.bodyEl.setStyle("display", "block");
28898     this.bodyEl.setStyle("zoom", "1");
28899     this.hideAction();
28900
28901     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
28902     /** @private */
28903     this.el = Roo.get(els.el, true);
28904     this.inner = Roo.get(els.inner, true);
28905     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
28906     this.pnode = Roo.get(els.el.parentNode, true);
28907     this.el.on("mousedown", this.onTabMouseDown, this);
28908     this.el.on("click", this.onTabClick, this);
28909     /** @private */
28910     if(closable){
28911         var c = Roo.get(els.close, true);
28912         c.dom.title = this.closeText;
28913         c.addClassOnOver("close-over");
28914         c.on("click", this.closeClick, this);
28915      }
28916
28917     this.addEvents({
28918          /**
28919          * @event activate
28920          * Fires when this tab becomes the active tab.
28921          * @param {Roo.TabPanel} tabPanel The parent TabPanel
28922          * @param {Roo.TabPanelItem} this
28923          */
28924         "activate": true,
28925         /**
28926          * @event beforeclose
28927          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
28928          * @param {Roo.TabPanelItem} this
28929          * @param {Object} e Set cancel to true on this object to cancel the close.
28930          */
28931         "beforeclose": true,
28932         /**
28933          * @event close
28934          * Fires when this tab is closed.
28935          * @param {Roo.TabPanelItem} this
28936          */
28937          "close": true,
28938         /**
28939          * @event deactivate
28940          * Fires when this tab is no longer the active tab.
28941          * @param {Roo.TabPanel} tabPanel The parent TabPanel
28942          * @param {Roo.TabPanelItem} this
28943          */
28944          "deactivate" : true
28945     });
28946     this.hidden = false;
28947
28948     Roo.TabPanelItem.superclass.constructor.call(this);
28949 };
28950
28951 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
28952     purgeListeners : function(){
28953        Roo.util.Observable.prototype.purgeListeners.call(this);
28954        this.el.removeAllListeners();
28955     },
28956     /**
28957      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
28958      */
28959     show : function(){
28960         this.pnode.addClass("on");
28961         this.showAction();
28962         if(Roo.isOpera){
28963             this.tabPanel.stripWrap.repaint();
28964         }
28965         this.fireEvent("activate", this.tabPanel, this);
28966     },
28967
28968     /**
28969      * Returns true if this tab is the active tab.
28970      * @return {Boolean}
28971      */
28972     isActive : function(){
28973         return this.tabPanel.getActiveTab() == this;
28974     },
28975
28976     /**
28977      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
28978      */
28979     hide : function(){
28980         this.pnode.removeClass("on");
28981         this.hideAction();
28982         this.fireEvent("deactivate", this.tabPanel, this);
28983     },
28984
28985     hideAction : function(){
28986         this.bodyEl.hide();
28987         this.bodyEl.setStyle("position", "absolute");
28988         this.bodyEl.setLeft("-20000px");
28989         this.bodyEl.setTop("-20000px");
28990     },
28991
28992     showAction : function(){
28993         this.bodyEl.setStyle("position", "relative");
28994         this.bodyEl.setTop("");
28995         this.bodyEl.setLeft("");
28996         this.bodyEl.show();
28997     },
28998
28999     /**
29000      * Set the tooltip for the tab.
29001      * @param {String} tooltip The tab's tooltip
29002      */
29003     setTooltip : function(text){
29004         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
29005             this.textEl.dom.qtip = text;
29006             this.textEl.dom.removeAttribute('title');
29007         }else{
29008             this.textEl.dom.title = text;
29009         }
29010     },
29011
29012     onTabClick : function(e){
29013         e.preventDefault();
29014         this.tabPanel.activate(this.id);
29015     },
29016
29017     onTabMouseDown : function(e){
29018         e.preventDefault();
29019         this.tabPanel.activate(this.id);
29020     },
29021
29022     getWidth : function(){
29023         return this.inner.getWidth();
29024     },
29025
29026     setWidth : function(width){
29027         var iwidth = width - this.pnode.getPadding("lr");
29028         this.inner.setWidth(iwidth);
29029         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
29030         this.pnode.setWidth(width);
29031     },
29032
29033     /**
29034      * Show or hide the tab
29035      * @param {Boolean} hidden True to hide or false to show.
29036      */
29037     setHidden : function(hidden){
29038         this.hidden = hidden;
29039         this.pnode.setStyle("display", hidden ? "none" : "");
29040     },
29041
29042     /**
29043      * Returns true if this tab is "hidden"
29044      * @return {Boolean}
29045      */
29046     isHidden : function(){
29047         return this.hidden;
29048     },
29049
29050     /**
29051      * Returns the text for this tab
29052      * @return {String}
29053      */
29054     getText : function(){
29055         return this.text;
29056     },
29057
29058     autoSize : function(){
29059         //this.el.beginMeasure();
29060         this.textEl.setWidth(1);
29061         /*
29062          *  #2804 [new] Tabs in Roojs
29063          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
29064          */
29065         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
29066         //this.el.endMeasure();
29067     },
29068
29069     /**
29070      * Sets the text for the tab (Note: this also sets the tooltip text)
29071      * @param {String} text The tab's text and tooltip
29072      */
29073     setText : function(text){
29074         this.text = text;
29075         this.textEl.update(text);
29076         this.setTooltip(text);
29077         if(!this.tabPanel.resizeTabs){
29078             this.autoSize();
29079         }
29080     },
29081     /**
29082      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
29083      */
29084     activate : function(){
29085         this.tabPanel.activate(this.id);
29086     },
29087
29088     /**
29089      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
29090      */
29091     disable : function(){
29092         if(this.tabPanel.active != this){
29093             this.disabled = true;
29094             this.pnode.addClass("disabled");
29095         }
29096     },
29097
29098     /**
29099      * Enables this TabPanelItem if it was previously disabled.
29100      */
29101     enable : function(){
29102         this.disabled = false;
29103         this.pnode.removeClass("disabled");
29104     },
29105
29106     /**
29107      * Sets the content for this TabPanelItem.
29108      * @param {String} content The content
29109      * @param {Boolean} loadScripts true to look for and load scripts
29110      */
29111     setContent : function(content, loadScripts){
29112         this.bodyEl.update(content, loadScripts);
29113     },
29114
29115     /**
29116      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
29117      * @return {Roo.UpdateManager} The UpdateManager
29118      */
29119     getUpdateManager : function(){
29120         return this.bodyEl.getUpdateManager();
29121     },
29122
29123     /**
29124      * Set a URL to be used to load the content for this TabPanelItem.
29125      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
29126      * @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)
29127      * @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)
29128      * @return {Roo.UpdateManager} The UpdateManager
29129      */
29130     setUrl : function(url, params, loadOnce){
29131         if(this.refreshDelegate){
29132             this.un('activate', this.refreshDelegate);
29133         }
29134         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
29135         this.on("activate", this.refreshDelegate);
29136         return this.bodyEl.getUpdateManager();
29137     },
29138
29139     /** @private */
29140     _handleRefresh : function(url, params, loadOnce){
29141         if(!loadOnce || !this.loaded){
29142             var updater = this.bodyEl.getUpdateManager();
29143             updater.update(url, params, this._setLoaded.createDelegate(this));
29144         }
29145     },
29146
29147     /**
29148      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
29149      *   Will fail silently if the setUrl method has not been called.
29150      *   This does not activate the panel, just updates its content.
29151      */
29152     refresh : function(){
29153         if(this.refreshDelegate){
29154            this.loaded = false;
29155            this.refreshDelegate();
29156         }
29157     },
29158
29159     /** @private */
29160     _setLoaded : function(){
29161         this.loaded = true;
29162     },
29163
29164     /** @private */
29165     closeClick : function(e){
29166         var o = {};
29167         e.stopEvent();
29168         this.fireEvent("beforeclose", this, o);
29169         if(o.cancel !== true){
29170             this.tabPanel.removeTab(this.id);
29171         }
29172     },
29173     /**
29174      * The text displayed in the tooltip for the close icon.
29175      * @type String
29176      */
29177     closeText : "Close this tab"
29178 });
29179
29180 /** @private */
29181 Roo.TabPanel.prototype.createStrip = function(container){
29182     var strip = document.createElement("div");
29183     strip.className = "x-tabs-wrap";
29184     container.appendChild(strip);
29185     return strip;
29186 };
29187 /** @private */
29188 Roo.TabPanel.prototype.createStripList = function(strip){
29189     // div wrapper for retard IE
29190     // returns the "tr" element.
29191     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
29192         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
29193         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
29194     return strip.firstChild.firstChild.firstChild.firstChild;
29195 };
29196 /** @private */
29197 Roo.TabPanel.prototype.createBody = function(container){
29198     var body = document.createElement("div");
29199     Roo.id(body, "tab-body");
29200     Roo.fly(body).addClass("x-tabs-body");
29201     container.appendChild(body);
29202     return body;
29203 };
29204 /** @private */
29205 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
29206     var body = Roo.getDom(id);
29207     if(!body){
29208         body = document.createElement("div");
29209         body.id = id;
29210     }
29211     Roo.fly(body).addClass("x-tabs-item-body");
29212     bodyEl.insertBefore(body, bodyEl.firstChild);
29213     return body;
29214 };
29215 /** @private */
29216 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
29217     var td = document.createElement("td");
29218     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
29219     //stripEl.appendChild(td);
29220     if(closable){
29221         td.className = "x-tabs-closable";
29222         if(!this.closeTpl){
29223             this.closeTpl = new Roo.Template(
29224                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
29225                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
29226                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
29227             );
29228         }
29229         var el = this.closeTpl.overwrite(td, {"text": text});
29230         var close = el.getElementsByTagName("div")[0];
29231         var inner = el.getElementsByTagName("em")[0];
29232         return {"el": el, "close": close, "inner": inner};
29233     } else {
29234         if(!this.tabTpl){
29235             this.tabTpl = new Roo.Template(
29236                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
29237                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
29238             );
29239         }
29240         var el = this.tabTpl.overwrite(td, {"text": text});
29241         var inner = el.getElementsByTagName("em")[0];
29242         return {"el": el, "inner": inner};
29243     }
29244 };/*
29245  * Based on:
29246  * Ext JS Library 1.1.1
29247  * Copyright(c) 2006-2007, Ext JS, LLC.
29248  *
29249  * Originally Released Under LGPL - original licence link has changed is not relivant.
29250  *
29251  * Fork - LGPL
29252  * <script type="text/javascript">
29253  */
29254
29255 /**
29256  * @class Roo.Button
29257  * @extends Roo.util.Observable
29258  * Simple Button class
29259  * @cfg {String} text The button text
29260  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
29261  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
29262  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
29263  * @cfg {Object} scope The scope of the handler
29264  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
29265  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
29266  * @cfg {Boolean} hidden True to start hidden (defaults to false)
29267  * @cfg {Boolean} disabled True to start disabled (defaults to false)
29268  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
29269  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
29270    applies if enableToggle = true)
29271  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
29272  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
29273   an {@link Roo.util.ClickRepeater} config object (defaults to false).
29274  * @constructor
29275  * Create a new button
29276  * @param {Object} config The config object
29277  */
29278 Roo.Button = function(renderTo, config)
29279 {
29280     if (!config) {
29281         config = renderTo;
29282         renderTo = config.renderTo || false;
29283     }
29284     
29285     Roo.apply(this, config);
29286     this.addEvents({
29287         /**
29288              * @event click
29289              * Fires when this button is clicked
29290              * @param {Button} this
29291              * @param {EventObject} e The click event
29292              */
29293             "click" : true,
29294         /**
29295              * @event toggle
29296              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
29297              * @param {Button} this
29298              * @param {Boolean} pressed
29299              */
29300             "toggle" : true,
29301         /**
29302              * @event mouseover
29303              * Fires when the mouse hovers over the button
29304              * @param {Button} this
29305              * @param {Event} e The event object
29306              */
29307         'mouseover' : true,
29308         /**
29309              * @event mouseout
29310              * Fires when the mouse exits the button
29311              * @param {Button} this
29312              * @param {Event} e The event object
29313              */
29314         'mouseout': true,
29315          /**
29316              * @event render
29317              * Fires when the button is rendered
29318              * @param {Button} this
29319              */
29320         'render': true
29321     });
29322     if(this.menu){
29323         this.menu = Roo.menu.MenuMgr.get(this.menu);
29324     }
29325     // register listeners first!!  - so render can be captured..
29326     Roo.util.Observable.call(this);
29327     if(renderTo){
29328         this.render(renderTo);
29329     }
29330     
29331   
29332 };
29333
29334 Roo.extend(Roo.Button, Roo.util.Observable, {
29335     /**
29336      * 
29337      */
29338     
29339     /**
29340      * Read-only. True if this button is hidden
29341      * @type Boolean
29342      */
29343     hidden : false,
29344     /**
29345      * Read-only. True if this button is disabled
29346      * @type Boolean
29347      */
29348     disabled : false,
29349     /**
29350      * Read-only. True if this button is pressed (only if enableToggle = true)
29351      * @type Boolean
29352      */
29353     pressed : false,
29354
29355     /**
29356      * @cfg {Number} tabIndex 
29357      * The DOM tabIndex for this button (defaults to undefined)
29358      */
29359     tabIndex : undefined,
29360
29361     /**
29362      * @cfg {Boolean} enableToggle
29363      * True to enable pressed/not pressed toggling (defaults to false)
29364      */
29365     enableToggle: false,
29366     /**
29367      * @cfg {Mixed} menu
29368      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
29369      */
29370     menu : undefined,
29371     /**
29372      * @cfg {String} menuAlign
29373      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
29374      */
29375     menuAlign : "tl-bl?",
29376
29377     /**
29378      * @cfg {String} iconCls
29379      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
29380      */
29381     iconCls : undefined,
29382     /**
29383      * @cfg {String} type
29384      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
29385      */
29386     type : 'button',
29387
29388     // private
29389     menuClassTarget: 'tr',
29390
29391     /**
29392      * @cfg {String} clickEvent
29393      * The type of event to map to the button's event handler (defaults to 'click')
29394      */
29395     clickEvent : 'click',
29396
29397     /**
29398      * @cfg {Boolean} handleMouseEvents
29399      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
29400      */
29401     handleMouseEvents : true,
29402
29403     /**
29404      * @cfg {String} tooltipType
29405      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
29406      */
29407     tooltipType : 'qtip',
29408
29409     /**
29410      * @cfg {String} cls
29411      * A CSS class to apply to the button's main element.
29412      */
29413     
29414     /**
29415      * @cfg {Roo.Template} template (Optional)
29416      * An {@link Roo.Template} with which to create the Button's main element. This Template must
29417      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
29418      * require code modifications if required elements (e.g. a button) aren't present.
29419      */
29420
29421     // private
29422     render : function(renderTo){
29423         var btn;
29424         if(this.hideParent){
29425             this.parentEl = Roo.get(renderTo);
29426         }
29427         if(!this.dhconfig){
29428             if(!this.template){
29429                 if(!Roo.Button.buttonTemplate){
29430                     // hideous table template
29431                     Roo.Button.buttonTemplate = new Roo.Template(
29432                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
29433                         '<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>',
29434                         "</tr></tbody></table>");
29435                 }
29436                 this.template = Roo.Button.buttonTemplate;
29437             }
29438             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
29439             var btnEl = btn.child("button:first");
29440             btnEl.on('focus', this.onFocus, this);
29441             btnEl.on('blur', this.onBlur, this);
29442             if(this.cls){
29443                 btn.addClass(this.cls);
29444             }
29445             if(this.icon){
29446                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
29447             }
29448             if(this.iconCls){
29449                 btnEl.addClass(this.iconCls);
29450                 if(!this.cls){
29451                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
29452                 }
29453             }
29454             if(this.tabIndex !== undefined){
29455                 btnEl.dom.tabIndex = this.tabIndex;
29456             }
29457             if(this.tooltip){
29458                 if(typeof this.tooltip == 'object'){
29459                     Roo.QuickTips.tips(Roo.apply({
29460                           target: btnEl.id
29461                     }, this.tooltip));
29462                 } else {
29463                     btnEl.dom[this.tooltipType] = this.tooltip;
29464                 }
29465             }
29466         }else{
29467             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
29468         }
29469         this.el = btn;
29470         if(this.id){
29471             this.el.dom.id = this.el.id = this.id;
29472         }
29473         if(this.menu){
29474             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
29475             this.menu.on("show", this.onMenuShow, this);
29476             this.menu.on("hide", this.onMenuHide, this);
29477         }
29478         btn.addClass("x-btn");
29479         if(Roo.isIE && !Roo.isIE7){
29480             this.autoWidth.defer(1, this);
29481         }else{
29482             this.autoWidth();
29483         }
29484         if(this.handleMouseEvents){
29485             btn.on("mouseover", this.onMouseOver, this);
29486             btn.on("mouseout", this.onMouseOut, this);
29487             btn.on("mousedown", this.onMouseDown, this);
29488         }
29489         btn.on(this.clickEvent, this.onClick, this);
29490         //btn.on("mouseup", this.onMouseUp, this);
29491         if(this.hidden){
29492             this.hide();
29493         }
29494         if(this.disabled){
29495             this.disable();
29496         }
29497         Roo.ButtonToggleMgr.register(this);
29498         if(this.pressed){
29499             this.el.addClass("x-btn-pressed");
29500         }
29501         if(this.repeat){
29502             var repeater = new Roo.util.ClickRepeater(btn,
29503                 typeof this.repeat == "object" ? this.repeat : {}
29504             );
29505             repeater.on("click", this.onClick,  this);
29506         }
29507         
29508         this.fireEvent('render', this);
29509         
29510     },
29511     /**
29512      * Returns the button's underlying element
29513      * @return {Roo.Element} The element
29514      */
29515     getEl : function(){
29516         return this.el;  
29517     },
29518     
29519     /**
29520      * Destroys this Button and removes any listeners.
29521      */
29522     destroy : function(){
29523         Roo.ButtonToggleMgr.unregister(this);
29524         this.el.removeAllListeners();
29525         this.purgeListeners();
29526         this.el.remove();
29527     },
29528
29529     // private
29530     autoWidth : function(){
29531         if(this.el){
29532             this.el.setWidth("auto");
29533             if(Roo.isIE7 && Roo.isStrict){
29534                 var ib = this.el.child('button');
29535                 if(ib && ib.getWidth() > 20){
29536                     ib.clip();
29537                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
29538                 }
29539             }
29540             if(this.minWidth){
29541                 if(this.hidden){
29542                     this.el.beginMeasure();
29543                 }
29544                 if(this.el.getWidth() < this.minWidth){
29545                     this.el.setWidth(this.minWidth);
29546                 }
29547                 if(this.hidden){
29548                     this.el.endMeasure();
29549                 }
29550             }
29551         }
29552     },
29553
29554     /**
29555      * Assigns this button's click handler
29556      * @param {Function} handler The function to call when the button is clicked
29557      * @param {Object} scope (optional) Scope for the function passed in
29558      */
29559     setHandler : function(handler, scope){
29560         this.handler = handler;
29561         this.scope = scope;  
29562     },
29563     
29564     /**
29565      * Sets this button's text
29566      * @param {String} text The button text
29567      */
29568     setText : function(text){
29569         this.text = text;
29570         if(this.el){
29571             this.el.child("td.x-btn-center button.x-btn-text").update(text);
29572         }
29573         this.autoWidth();
29574     },
29575     
29576     /**
29577      * Gets the text for this button
29578      * @return {String} The button text
29579      */
29580     getText : function(){
29581         return this.text;  
29582     },
29583     
29584     /**
29585      * Show this button
29586      */
29587     show: function(){
29588         this.hidden = false;
29589         if(this.el){
29590             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
29591         }
29592     },
29593     
29594     /**
29595      * Hide this button
29596      */
29597     hide: function(){
29598         this.hidden = true;
29599         if(this.el){
29600             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
29601         }
29602     },
29603     
29604     /**
29605      * Convenience function for boolean show/hide
29606      * @param {Boolean} visible True to show, false to hide
29607      */
29608     setVisible: function(visible){
29609         if(visible) {
29610             this.show();
29611         }else{
29612             this.hide();
29613         }
29614     },
29615     
29616     /**
29617      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
29618      * @param {Boolean} state (optional) Force a particular state
29619      */
29620     toggle : function(state){
29621         state = state === undefined ? !this.pressed : state;
29622         if(state != this.pressed){
29623             if(state){
29624                 this.el.addClass("x-btn-pressed");
29625                 this.pressed = true;
29626                 this.fireEvent("toggle", this, true);
29627             }else{
29628                 this.el.removeClass("x-btn-pressed");
29629                 this.pressed = false;
29630                 this.fireEvent("toggle", this, false);
29631             }
29632             if(this.toggleHandler){
29633                 this.toggleHandler.call(this.scope || this, this, state);
29634             }
29635         }
29636     },
29637     
29638     /**
29639      * Focus the button
29640      */
29641     focus : function(){
29642         this.el.child('button:first').focus();
29643     },
29644     
29645     /**
29646      * Disable this button
29647      */
29648     disable : function(){
29649         if(this.el){
29650             this.el.addClass("x-btn-disabled");
29651         }
29652         this.disabled = true;
29653     },
29654     
29655     /**
29656      * Enable this button
29657      */
29658     enable : function(){
29659         if(this.el){
29660             this.el.removeClass("x-btn-disabled");
29661         }
29662         this.disabled = false;
29663     },
29664
29665     /**
29666      * Convenience function for boolean enable/disable
29667      * @param {Boolean} enabled True to enable, false to disable
29668      */
29669     setDisabled : function(v){
29670         this[v !== true ? "enable" : "disable"]();
29671     },
29672
29673     // private
29674     onClick : function(e)
29675     {
29676         if(e){
29677             e.preventDefault();
29678         }
29679         if(e.button != 0){
29680             return;
29681         }
29682         if(!this.disabled){
29683             if(this.enableToggle){
29684                 this.toggle();
29685             }
29686             if(this.menu && !this.menu.isVisible()){
29687                 this.menu.show(this.el, this.menuAlign);
29688             }
29689             this.fireEvent("click", this, e);
29690             if(this.handler){
29691                 this.el.removeClass("x-btn-over");
29692                 this.handler.call(this.scope || this, this, e);
29693             }
29694         }
29695     },
29696     // private
29697     onMouseOver : function(e){
29698         if(!this.disabled){
29699             this.el.addClass("x-btn-over");
29700             this.fireEvent('mouseover', this, e);
29701         }
29702     },
29703     // private
29704     onMouseOut : function(e){
29705         if(!e.within(this.el,  true)){
29706             this.el.removeClass("x-btn-over");
29707             this.fireEvent('mouseout', this, e);
29708         }
29709     },
29710     // private
29711     onFocus : function(e){
29712         if(!this.disabled){
29713             this.el.addClass("x-btn-focus");
29714         }
29715     },
29716     // private
29717     onBlur : function(e){
29718         this.el.removeClass("x-btn-focus");
29719     },
29720     // private
29721     onMouseDown : function(e){
29722         if(!this.disabled && e.button == 0){
29723             this.el.addClass("x-btn-click");
29724             Roo.get(document).on('mouseup', this.onMouseUp, this);
29725         }
29726     },
29727     // private
29728     onMouseUp : function(e){
29729         if(e.button == 0){
29730             this.el.removeClass("x-btn-click");
29731             Roo.get(document).un('mouseup', this.onMouseUp, this);
29732         }
29733     },
29734     // private
29735     onMenuShow : function(e){
29736         this.el.addClass("x-btn-menu-active");
29737     },
29738     // private
29739     onMenuHide : function(e){
29740         this.el.removeClass("x-btn-menu-active");
29741     }   
29742 });
29743
29744 // Private utility class used by Button
29745 Roo.ButtonToggleMgr = function(){
29746    var groups = {};
29747    
29748    function toggleGroup(btn, state){
29749        if(state){
29750            var g = groups[btn.toggleGroup];
29751            for(var i = 0, l = g.length; i < l; i++){
29752                if(g[i] != btn){
29753                    g[i].toggle(false);
29754                }
29755            }
29756        }
29757    }
29758    
29759    return {
29760        register : function(btn){
29761            if(!btn.toggleGroup){
29762                return;
29763            }
29764            var g = groups[btn.toggleGroup];
29765            if(!g){
29766                g = groups[btn.toggleGroup] = [];
29767            }
29768            g.push(btn);
29769            btn.on("toggle", toggleGroup);
29770        },
29771        
29772        unregister : function(btn){
29773            if(!btn.toggleGroup){
29774                return;
29775            }
29776            var g = groups[btn.toggleGroup];
29777            if(g){
29778                g.remove(btn);
29779                btn.un("toggle", toggleGroup);
29780            }
29781        }
29782    };
29783 }();/*
29784  * Based on:
29785  * Ext JS Library 1.1.1
29786  * Copyright(c) 2006-2007, Ext JS, LLC.
29787  *
29788  * Originally Released Under LGPL - original licence link has changed is not relivant.
29789  *
29790  * Fork - LGPL
29791  * <script type="text/javascript">
29792  */
29793  
29794 /**
29795  * @class Roo.SplitButton
29796  * @extends Roo.Button
29797  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
29798  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
29799  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
29800  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
29801  * @cfg {String} arrowTooltip The title attribute of the arrow
29802  * @constructor
29803  * Create a new menu button
29804  * @param {String/HTMLElement/Element} renderTo The element to append the button to
29805  * @param {Object} config The config object
29806  */
29807 Roo.SplitButton = function(renderTo, config){
29808     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
29809     /**
29810      * @event arrowclick
29811      * Fires when this button's arrow is clicked
29812      * @param {SplitButton} this
29813      * @param {EventObject} e The click event
29814      */
29815     this.addEvents({"arrowclick":true});
29816 };
29817
29818 Roo.extend(Roo.SplitButton, Roo.Button, {
29819     render : function(renderTo){
29820         // this is one sweet looking template!
29821         var tpl = new Roo.Template(
29822             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
29823             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
29824             '<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>',
29825             "</tbody></table></td><td>",
29826             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
29827             '<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>',
29828             "</tbody></table></td></tr></table>"
29829         );
29830         var btn = tpl.append(renderTo, [this.text, this.type], true);
29831         var btnEl = btn.child("button");
29832         if(this.cls){
29833             btn.addClass(this.cls);
29834         }
29835         if(this.icon){
29836             btnEl.setStyle('background-image', 'url(' +this.icon +')');
29837         }
29838         if(this.iconCls){
29839             btnEl.addClass(this.iconCls);
29840             if(!this.cls){
29841                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
29842             }
29843         }
29844         this.el = btn;
29845         if(this.handleMouseEvents){
29846             btn.on("mouseover", this.onMouseOver, this);
29847             btn.on("mouseout", this.onMouseOut, this);
29848             btn.on("mousedown", this.onMouseDown, this);
29849             btn.on("mouseup", this.onMouseUp, this);
29850         }
29851         btn.on(this.clickEvent, this.onClick, this);
29852         if(this.tooltip){
29853             if(typeof this.tooltip == 'object'){
29854                 Roo.QuickTips.tips(Roo.apply({
29855                       target: btnEl.id
29856                 }, this.tooltip));
29857             } else {
29858                 btnEl.dom[this.tooltipType] = this.tooltip;
29859             }
29860         }
29861         if(this.arrowTooltip){
29862             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
29863         }
29864         if(this.hidden){
29865             this.hide();
29866         }
29867         if(this.disabled){
29868             this.disable();
29869         }
29870         if(this.pressed){
29871             this.el.addClass("x-btn-pressed");
29872         }
29873         if(Roo.isIE && !Roo.isIE7){
29874             this.autoWidth.defer(1, this);
29875         }else{
29876             this.autoWidth();
29877         }
29878         if(this.menu){
29879             this.menu.on("show", this.onMenuShow, this);
29880             this.menu.on("hide", this.onMenuHide, this);
29881         }
29882         this.fireEvent('render', this);
29883     },
29884
29885     // private
29886     autoWidth : function(){
29887         if(this.el){
29888             var tbl = this.el.child("table:first");
29889             var tbl2 = this.el.child("table:last");
29890             this.el.setWidth("auto");
29891             tbl.setWidth("auto");
29892             if(Roo.isIE7 && Roo.isStrict){
29893                 var ib = this.el.child('button:first');
29894                 if(ib && ib.getWidth() > 20){
29895                     ib.clip();
29896                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
29897                 }
29898             }
29899             if(this.minWidth){
29900                 if(this.hidden){
29901                     this.el.beginMeasure();
29902                 }
29903                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
29904                     tbl.setWidth(this.minWidth-tbl2.getWidth());
29905                 }
29906                 if(this.hidden){
29907                     this.el.endMeasure();
29908                 }
29909             }
29910             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
29911         } 
29912     },
29913     /**
29914      * Sets this button's click handler
29915      * @param {Function} handler The function to call when the button is clicked
29916      * @param {Object} scope (optional) Scope for the function passed above
29917      */
29918     setHandler : function(handler, scope){
29919         this.handler = handler;
29920         this.scope = scope;  
29921     },
29922     
29923     /**
29924      * Sets this button's arrow click handler
29925      * @param {Function} handler The function to call when the arrow is clicked
29926      * @param {Object} scope (optional) Scope for the function passed above
29927      */
29928     setArrowHandler : function(handler, scope){
29929         this.arrowHandler = handler;
29930         this.scope = scope;  
29931     },
29932     
29933     /**
29934      * Focus the button
29935      */
29936     focus : function(){
29937         if(this.el){
29938             this.el.child("button:first").focus();
29939         }
29940     },
29941
29942     // private
29943     onClick : function(e){
29944         e.preventDefault();
29945         if(!this.disabled){
29946             if(e.getTarget(".x-btn-menu-arrow-wrap")){
29947                 if(this.menu && !this.menu.isVisible()){
29948                     this.menu.show(this.el, this.menuAlign);
29949                 }
29950                 this.fireEvent("arrowclick", this, e);
29951                 if(this.arrowHandler){
29952                     this.arrowHandler.call(this.scope || this, this, e);
29953                 }
29954             }else{
29955                 this.fireEvent("click", this, e);
29956                 if(this.handler){
29957                     this.handler.call(this.scope || this, this, e);
29958                 }
29959             }
29960         }
29961     },
29962     // private
29963     onMouseDown : function(e){
29964         if(!this.disabled){
29965             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
29966         }
29967     },
29968     // private
29969     onMouseUp : function(e){
29970         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
29971     }   
29972 });
29973
29974
29975 // backwards compat
29976 Roo.MenuButton = Roo.SplitButton;/*
29977  * Based on:
29978  * Ext JS Library 1.1.1
29979  * Copyright(c) 2006-2007, Ext JS, LLC.
29980  *
29981  * Originally Released Under LGPL - original licence link has changed is not relivant.
29982  *
29983  * Fork - LGPL
29984  * <script type="text/javascript">
29985  */
29986
29987 /**
29988  * @class Roo.Toolbar
29989  * Basic Toolbar class.
29990  * @constructor
29991  * Creates a new Toolbar
29992  * @param {Object} container The config object
29993  */ 
29994 Roo.Toolbar = function(container, buttons, config)
29995 {
29996     /// old consturctor format still supported..
29997     if(container instanceof Array){ // omit the container for later rendering
29998         buttons = container;
29999         config = buttons;
30000         container = null;
30001     }
30002     if (typeof(container) == 'object' && container.xtype) {
30003         config = container;
30004         container = config.container;
30005         buttons = config.buttons || []; // not really - use items!!
30006     }
30007     var xitems = [];
30008     if (config && config.items) {
30009         xitems = config.items;
30010         delete config.items;
30011     }
30012     Roo.apply(this, config);
30013     this.buttons = buttons;
30014     
30015     if(container){
30016         this.render(container);
30017     }
30018     this.xitems = xitems;
30019     Roo.each(xitems, function(b) {
30020         this.add(b);
30021     }, this);
30022     
30023 };
30024
30025 Roo.Toolbar.prototype = {
30026     /**
30027      * @cfg {Array} items
30028      * array of button configs or elements to add (will be converted to a MixedCollection)
30029      */
30030     
30031     /**
30032      * @cfg {String/HTMLElement/Element} container
30033      * The id or element that will contain the toolbar
30034      */
30035     // private
30036     render : function(ct){
30037         this.el = Roo.get(ct);
30038         if(this.cls){
30039             this.el.addClass(this.cls);
30040         }
30041         // using a table allows for vertical alignment
30042         // 100% width is needed by Safari...
30043         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
30044         this.tr = this.el.child("tr", true);
30045         var autoId = 0;
30046         this.items = new Roo.util.MixedCollection(false, function(o){
30047             return o.id || ("item" + (++autoId));
30048         });
30049         if(this.buttons){
30050             this.add.apply(this, this.buttons);
30051             delete this.buttons;
30052         }
30053     },
30054
30055     /**
30056      * Adds element(s) to the toolbar -- this function takes a variable number of 
30057      * arguments of mixed type and adds them to the toolbar.
30058      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
30059      * <ul>
30060      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
30061      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
30062      * <li>Field: Any form field (equivalent to {@link #addField})</li>
30063      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
30064      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
30065      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
30066      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
30067      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
30068      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
30069      * </ul>
30070      * @param {Mixed} arg2
30071      * @param {Mixed} etc.
30072      */
30073     add : function(){
30074         var a = arguments, l = a.length;
30075         for(var i = 0; i < l; i++){
30076             this._add(a[i]);
30077         }
30078     },
30079     // private..
30080     _add : function(el) {
30081         
30082         if (el.xtype) {
30083             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
30084         }
30085         
30086         if (el.applyTo){ // some kind of form field
30087             return this.addField(el);
30088         } 
30089         if (el.render){ // some kind of Toolbar.Item
30090             return this.addItem(el);
30091         }
30092         if (typeof el == "string"){ // string
30093             if(el == "separator" || el == "-"){
30094                 return this.addSeparator();
30095             }
30096             if (el == " "){
30097                 return this.addSpacer();
30098             }
30099             if(el == "->"){
30100                 return this.addFill();
30101             }
30102             return this.addText(el);
30103             
30104         }
30105         if(el.tagName){ // element
30106             return this.addElement(el);
30107         }
30108         if(typeof el == "object"){ // must be button config?
30109             return this.addButton(el);
30110         }
30111         // and now what?!?!
30112         return false;
30113         
30114     },
30115     
30116     /**
30117      * Add an Xtype element
30118      * @param {Object} xtype Xtype Object
30119      * @return {Object} created Object
30120      */
30121     addxtype : function(e){
30122         return this.add(e);  
30123     },
30124     
30125     /**
30126      * Returns the Element for this toolbar.
30127      * @return {Roo.Element}
30128      */
30129     getEl : function(){
30130         return this.el;  
30131     },
30132     
30133     /**
30134      * Adds a separator
30135      * @return {Roo.Toolbar.Item} The separator item
30136      */
30137     addSeparator : function(){
30138         return this.addItem(new Roo.Toolbar.Separator());
30139     },
30140
30141     /**
30142      * Adds a spacer element
30143      * @return {Roo.Toolbar.Spacer} The spacer item
30144      */
30145     addSpacer : function(){
30146         return this.addItem(new Roo.Toolbar.Spacer());
30147     },
30148
30149     /**
30150      * Adds a fill element that forces subsequent additions to the right side of the toolbar
30151      * @return {Roo.Toolbar.Fill} The fill item
30152      */
30153     addFill : function(){
30154         return this.addItem(new Roo.Toolbar.Fill());
30155     },
30156
30157     /**
30158      * Adds any standard HTML element to the toolbar
30159      * @param {String/HTMLElement/Element} el The element or id of the element to add
30160      * @return {Roo.Toolbar.Item} The element's item
30161      */
30162     addElement : function(el){
30163         return this.addItem(new Roo.Toolbar.Item(el));
30164     },
30165     /**
30166      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
30167      * @type Roo.util.MixedCollection  
30168      */
30169     items : false,
30170      
30171     /**
30172      * Adds any Toolbar.Item or subclass
30173      * @param {Roo.Toolbar.Item} item
30174      * @return {Roo.Toolbar.Item} The item
30175      */
30176     addItem : function(item){
30177         var td = this.nextBlock();
30178         item.render(td);
30179         this.items.add(item);
30180         return item;
30181     },
30182     
30183     /**
30184      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
30185      * @param {Object/Array} config A button config or array of configs
30186      * @return {Roo.Toolbar.Button/Array}
30187      */
30188     addButton : function(config){
30189         if(config instanceof Array){
30190             var buttons = [];
30191             for(var i = 0, len = config.length; i < len; i++) {
30192                 buttons.push(this.addButton(config[i]));
30193             }
30194             return buttons;
30195         }
30196         var b = config;
30197         if(!(config instanceof Roo.Toolbar.Button)){
30198             b = config.split ?
30199                 new Roo.Toolbar.SplitButton(config) :
30200                 new Roo.Toolbar.Button(config);
30201         }
30202         var td = this.nextBlock();
30203         b.render(td);
30204         this.items.add(b);
30205         return b;
30206     },
30207     
30208     /**
30209      * Adds text to the toolbar
30210      * @param {String} text The text to add
30211      * @return {Roo.Toolbar.Item} The element's item
30212      */
30213     addText : function(text){
30214         return this.addItem(new Roo.Toolbar.TextItem(text));
30215     },
30216     
30217     /**
30218      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
30219      * @param {Number} index The index where the item is to be inserted
30220      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
30221      * @return {Roo.Toolbar.Button/Item}
30222      */
30223     insertButton : function(index, item){
30224         if(item instanceof Array){
30225             var buttons = [];
30226             for(var i = 0, len = item.length; i < len; i++) {
30227                buttons.push(this.insertButton(index + i, item[i]));
30228             }
30229             return buttons;
30230         }
30231         if (!(item instanceof Roo.Toolbar.Button)){
30232            item = new Roo.Toolbar.Button(item);
30233         }
30234         var td = document.createElement("td");
30235         this.tr.insertBefore(td, this.tr.childNodes[index]);
30236         item.render(td);
30237         this.items.insert(index, item);
30238         return item;
30239     },
30240     
30241     /**
30242      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
30243      * @param {Object} config
30244      * @return {Roo.Toolbar.Item} The element's item
30245      */
30246     addDom : function(config, returnEl){
30247         var td = this.nextBlock();
30248         Roo.DomHelper.overwrite(td, config);
30249         var ti = new Roo.Toolbar.Item(td.firstChild);
30250         ti.render(td);
30251         this.items.add(ti);
30252         return ti;
30253     },
30254
30255     /**
30256      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
30257      * @type Roo.util.MixedCollection  
30258      */
30259     fields : false,
30260     
30261     /**
30262      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
30263      * Note: the field should not have been rendered yet. For a field that has already been
30264      * rendered, use {@link #addElement}.
30265      * @param {Roo.form.Field} field
30266      * @return {Roo.ToolbarItem}
30267      */
30268      
30269       
30270     addField : function(field) {
30271         if (!this.fields) {
30272             var autoId = 0;
30273             this.fields = new Roo.util.MixedCollection(false, function(o){
30274                 return o.id || ("item" + (++autoId));
30275             });
30276
30277         }
30278         
30279         var td = this.nextBlock();
30280         field.render(td);
30281         var ti = new Roo.Toolbar.Item(td.firstChild);
30282         ti.render(td);
30283         this.items.add(ti);
30284         this.fields.add(field);
30285         return ti;
30286     },
30287     /**
30288      * Hide the toolbar
30289      * @method hide
30290      */
30291      
30292       
30293     hide : function()
30294     {
30295         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
30296         this.el.child('div').hide();
30297     },
30298     /**
30299      * Show the toolbar
30300      * @method show
30301      */
30302     show : function()
30303     {
30304         this.el.child('div').show();
30305     },
30306       
30307     // private
30308     nextBlock : function(){
30309         var td = document.createElement("td");
30310         this.tr.appendChild(td);
30311         return td;
30312     },
30313
30314     // private
30315     destroy : function(){
30316         if(this.items){ // rendered?
30317             Roo.destroy.apply(Roo, this.items.items);
30318         }
30319         if(this.fields){ // rendered?
30320             Roo.destroy.apply(Roo, this.fields.items);
30321         }
30322         Roo.Element.uncache(this.el, this.tr);
30323     }
30324 };
30325
30326 /**
30327  * @class Roo.Toolbar.Item
30328  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
30329  * @constructor
30330  * Creates a new Item
30331  * @param {HTMLElement} el 
30332  */
30333 Roo.Toolbar.Item = function(el){
30334     var cfg = {};
30335     if (typeof (el.xtype) != 'undefined') {
30336         cfg = el;
30337         el = cfg.el;
30338     }
30339     
30340     this.el = Roo.getDom(el);
30341     this.id = Roo.id(this.el);
30342     this.hidden = false;
30343     
30344     this.addEvents({
30345          /**
30346              * @event render
30347              * Fires when the button is rendered
30348              * @param {Button} this
30349              */
30350         'render': true
30351     });
30352     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
30353 };
30354 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
30355 //Roo.Toolbar.Item.prototype = {
30356     
30357     /**
30358      * Get this item's HTML Element
30359      * @return {HTMLElement}
30360      */
30361     getEl : function(){
30362        return this.el;  
30363     },
30364
30365     // private
30366     render : function(td){
30367         
30368          this.td = td;
30369         td.appendChild(this.el);
30370         
30371         this.fireEvent('render', this);
30372     },
30373     
30374     /**
30375      * Removes and destroys this item.
30376      */
30377     destroy : function(){
30378         this.td.parentNode.removeChild(this.td);
30379     },
30380     
30381     /**
30382      * Shows this item.
30383      */
30384     show: function(){
30385         this.hidden = false;
30386         this.td.style.display = "";
30387     },
30388     
30389     /**
30390      * Hides this item.
30391      */
30392     hide: function(){
30393         this.hidden = true;
30394         this.td.style.display = "none";
30395     },
30396     
30397     /**
30398      * Convenience function for boolean show/hide.
30399      * @param {Boolean} visible true to show/false to hide
30400      */
30401     setVisible: function(visible){
30402         if(visible) {
30403             this.show();
30404         }else{
30405             this.hide();
30406         }
30407     },
30408     
30409     /**
30410      * Try to focus this item.
30411      */
30412     focus : function(){
30413         Roo.fly(this.el).focus();
30414     },
30415     
30416     /**
30417      * Disables this item.
30418      */
30419     disable : function(){
30420         Roo.fly(this.td).addClass("x-item-disabled");
30421         this.disabled = true;
30422         this.el.disabled = true;
30423     },
30424     
30425     /**
30426      * Enables this item.
30427      */
30428     enable : function(){
30429         Roo.fly(this.td).removeClass("x-item-disabled");
30430         this.disabled = false;
30431         this.el.disabled = false;
30432     }
30433 });
30434
30435
30436 /**
30437  * @class Roo.Toolbar.Separator
30438  * @extends Roo.Toolbar.Item
30439  * A simple toolbar separator class
30440  * @constructor
30441  * Creates a new Separator
30442  */
30443 Roo.Toolbar.Separator = function(cfg){
30444     
30445     var s = document.createElement("span");
30446     s.className = "ytb-sep";
30447     if (cfg) {
30448         cfg.el = s;
30449     }
30450     
30451     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
30452 };
30453 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
30454     enable:Roo.emptyFn,
30455     disable:Roo.emptyFn,
30456     focus:Roo.emptyFn
30457 });
30458
30459 /**
30460  * @class Roo.Toolbar.Spacer
30461  * @extends Roo.Toolbar.Item
30462  * A simple element that adds extra horizontal space to a toolbar.
30463  * @constructor
30464  * Creates a new Spacer
30465  */
30466 Roo.Toolbar.Spacer = function(cfg){
30467     var s = document.createElement("div");
30468     s.className = "ytb-spacer";
30469     if (cfg) {
30470         cfg.el = s;
30471     }
30472     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
30473 };
30474 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
30475     enable:Roo.emptyFn,
30476     disable:Roo.emptyFn,
30477     focus:Roo.emptyFn
30478 });
30479
30480 /**
30481  * @class Roo.Toolbar.Fill
30482  * @extends Roo.Toolbar.Spacer
30483  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
30484  * @constructor
30485  * Creates a new Spacer
30486  */
30487 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
30488     // private
30489     render : function(td){
30490         td.style.width = '100%';
30491         Roo.Toolbar.Fill.superclass.render.call(this, td);
30492     }
30493 });
30494
30495 /**
30496  * @class Roo.Toolbar.TextItem
30497  * @extends Roo.Toolbar.Item
30498  * A simple class that renders text directly into a toolbar.
30499  * @constructor
30500  * Creates a new TextItem
30501  * @cfg {string} text 
30502  */
30503 Roo.Toolbar.TextItem = function(cfg){
30504     var  text = cfg || "";
30505     if (typeof(cfg) == 'object') {
30506         text = cfg.text || "";
30507     }  else {
30508         cfg = null;
30509     }
30510     var s = document.createElement("span");
30511     s.className = "ytb-text";
30512     s.innerHTML = text;
30513     if (cfg) {
30514         cfg.el  = s;
30515     }
30516     
30517     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
30518 };
30519 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
30520     
30521      
30522     enable:Roo.emptyFn,
30523     disable:Roo.emptyFn,
30524     focus:Roo.emptyFn
30525 });
30526
30527 /**
30528  * @class Roo.Toolbar.Button
30529  * @extends Roo.Button
30530  * A button that renders into a toolbar.
30531  * @constructor
30532  * Creates a new Button
30533  * @param {Object} config A standard {@link Roo.Button} config object
30534  */
30535 Roo.Toolbar.Button = function(config){
30536     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
30537 };
30538 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
30539     render : function(td){
30540         this.td = td;
30541         Roo.Toolbar.Button.superclass.render.call(this, td);
30542     },
30543     
30544     /**
30545      * Removes and destroys this button
30546      */
30547     destroy : function(){
30548         Roo.Toolbar.Button.superclass.destroy.call(this);
30549         this.td.parentNode.removeChild(this.td);
30550     },
30551     
30552     /**
30553      * Shows this button
30554      */
30555     show: function(){
30556         this.hidden = false;
30557         this.td.style.display = "";
30558     },
30559     
30560     /**
30561      * Hides this button
30562      */
30563     hide: function(){
30564         this.hidden = true;
30565         this.td.style.display = "none";
30566     },
30567
30568     /**
30569      * Disables this item
30570      */
30571     disable : function(){
30572         Roo.fly(this.td).addClass("x-item-disabled");
30573         this.disabled = true;
30574     },
30575
30576     /**
30577      * Enables this item
30578      */
30579     enable : function(){
30580         Roo.fly(this.td).removeClass("x-item-disabled");
30581         this.disabled = false;
30582     }
30583 });
30584 // backwards compat
30585 Roo.ToolbarButton = Roo.Toolbar.Button;
30586
30587 /**
30588  * @class Roo.Toolbar.SplitButton
30589  * @extends Roo.SplitButton
30590  * A menu button that renders into a toolbar.
30591  * @constructor
30592  * Creates a new SplitButton
30593  * @param {Object} config A standard {@link Roo.SplitButton} config object
30594  */
30595 Roo.Toolbar.SplitButton = function(config){
30596     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
30597 };
30598 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
30599     render : function(td){
30600         this.td = td;
30601         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
30602     },
30603     
30604     /**
30605      * Removes and destroys this button
30606      */
30607     destroy : function(){
30608         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
30609         this.td.parentNode.removeChild(this.td);
30610     },
30611     
30612     /**
30613      * Shows this button
30614      */
30615     show: function(){
30616         this.hidden = false;
30617         this.td.style.display = "";
30618     },
30619     
30620     /**
30621      * Hides this button
30622      */
30623     hide: function(){
30624         this.hidden = true;
30625         this.td.style.display = "none";
30626     }
30627 });
30628
30629 // backwards compat
30630 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
30631  * Based on:
30632  * Ext JS Library 1.1.1
30633  * Copyright(c) 2006-2007, Ext JS, LLC.
30634  *
30635  * Originally Released Under LGPL - original licence link has changed is not relivant.
30636  *
30637  * Fork - LGPL
30638  * <script type="text/javascript">
30639  */
30640  
30641 /**
30642  * @class Roo.PagingToolbar
30643  * @extends Roo.Toolbar
30644  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
30645  * @constructor
30646  * Create a new PagingToolbar
30647  * @param {Object} config The config object
30648  */
30649 Roo.PagingToolbar = function(el, ds, config)
30650 {
30651     // old args format still supported... - xtype is prefered..
30652     if (typeof(el) == 'object' && el.xtype) {
30653         // created from xtype...
30654         config = el;
30655         ds = el.dataSource;
30656         el = config.container;
30657     }
30658     var items = [];
30659     if (config.items) {
30660         items = config.items;
30661         config.items = [];
30662     }
30663     
30664     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
30665     this.ds = ds;
30666     this.cursor = 0;
30667     this.renderButtons(this.el);
30668     this.bind(ds);
30669     
30670     // supprot items array.
30671    
30672     Roo.each(items, function(e) {
30673         this.add(Roo.factory(e));
30674     },this);
30675     
30676 };
30677
30678 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
30679     /**
30680      * @cfg {Roo.data.Store} dataSource
30681      * The underlying data store providing the paged data
30682      */
30683     /**
30684      * @cfg {String/HTMLElement/Element} container
30685      * container The id or element that will contain the toolbar
30686      */
30687     /**
30688      * @cfg {Boolean} displayInfo
30689      * True to display the displayMsg (defaults to false)
30690      */
30691     /**
30692      * @cfg {Number} pageSize
30693      * The number of records to display per page (defaults to 20)
30694      */
30695     pageSize: 20,
30696     /**
30697      * @cfg {String} displayMsg
30698      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
30699      */
30700     displayMsg : 'Displaying {0} - {1} of {2}',
30701     /**
30702      * @cfg {String} emptyMsg
30703      * The message to display when no records are found (defaults to "No data to display")
30704      */
30705     emptyMsg : 'No data to display',
30706     /**
30707      * Customizable piece of the default paging text (defaults to "Page")
30708      * @type String
30709      */
30710     beforePageText : "Page",
30711     /**
30712      * Customizable piece of the default paging text (defaults to "of %0")
30713      * @type String
30714      */
30715     afterPageText : "of {0}",
30716     /**
30717      * Customizable piece of the default paging text (defaults to "First Page")
30718      * @type String
30719      */
30720     firstText : "First Page",
30721     /**
30722      * Customizable piece of the default paging text (defaults to "Previous Page")
30723      * @type String
30724      */
30725     prevText : "Previous Page",
30726     /**
30727      * Customizable piece of the default paging text (defaults to "Next Page")
30728      * @type String
30729      */
30730     nextText : "Next Page",
30731     /**
30732      * Customizable piece of the default paging text (defaults to "Last Page")
30733      * @type String
30734      */
30735     lastText : "Last Page",
30736     /**
30737      * Customizable piece of the default paging text (defaults to "Refresh")
30738      * @type String
30739      */
30740     refreshText : "Refresh",
30741
30742     // private
30743     renderButtons : function(el){
30744         Roo.PagingToolbar.superclass.render.call(this, el);
30745         this.first = this.addButton({
30746             tooltip: this.firstText,
30747             cls: "x-btn-icon x-grid-page-first",
30748             disabled: true,
30749             handler: this.onClick.createDelegate(this, ["first"])
30750         });
30751         this.prev = this.addButton({
30752             tooltip: this.prevText,
30753             cls: "x-btn-icon x-grid-page-prev",
30754             disabled: true,
30755             handler: this.onClick.createDelegate(this, ["prev"])
30756         });
30757         //this.addSeparator();
30758         this.add(this.beforePageText);
30759         this.field = Roo.get(this.addDom({
30760            tag: "input",
30761            type: "text",
30762            size: "3",
30763            value: "1",
30764            cls: "x-grid-page-number"
30765         }).el);
30766         this.field.on("keydown", this.onPagingKeydown, this);
30767         this.field.on("focus", function(){this.dom.select();});
30768         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
30769         this.field.setHeight(18);
30770         //this.addSeparator();
30771         this.next = this.addButton({
30772             tooltip: this.nextText,
30773             cls: "x-btn-icon x-grid-page-next",
30774             disabled: true,
30775             handler: this.onClick.createDelegate(this, ["next"])
30776         });
30777         this.last = this.addButton({
30778             tooltip: this.lastText,
30779             cls: "x-btn-icon x-grid-page-last",
30780             disabled: true,
30781             handler: this.onClick.createDelegate(this, ["last"])
30782         });
30783         //this.addSeparator();
30784         this.loading = this.addButton({
30785             tooltip: this.refreshText,
30786             cls: "x-btn-icon x-grid-loading",
30787             handler: this.onClick.createDelegate(this, ["refresh"])
30788         });
30789
30790         if(this.displayInfo){
30791             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
30792         }
30793     },
30794
30795     // private
30796     updateInfo : function(){
30797         if(this.displayEl){
30798             var count = this.ds.getCount();
30799             var msg = count == 0 ?
30800                 this.emptyMsg :
30801                 String.format(
30802                     this.displayMsg,
30803                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
30804                 );
30805             this.displayEl.update(msg);
30806         }
30807     },
30808
30809     // private
30810     onLoad : function(ds, r, o){
30811        this.cursor = o.params ? o.params.start : 0;
30812        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
30813
30814        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
30815        this.field.dom.value = ap;
30816        this.first.setDisabled(ap == 1);
30817        this.prev.setDisabled(ap == 1);
30818        this.next.setDisabled(ap == ps);
30819        this.last.setDisabled(ap == ps);
30820        this.loading.enable();
30821        this.updateInfo();
30822     },
30823
30824     // private
30825     getPageData : function(){
30826         var total = this.ds.getTotalCount();
30827         return {
30828             total : total,
30829             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
30830             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
30831         };
30832     },
30833
30834     // private
30835     onLoadError : function(){
30836         this.loading.enable();
30837     },
30838
30839     // private
30840     onPagingKeydown : function(e){
30841         var k = e.getKey();
30842         var d = this.getPageData();
30843         if(k == e.RETURN){
30844             var v = this.field.dom.value, pageNum;
30845             if(!v || isNaN(pageNum = parseInt(v, 10))){
30846                 this.field.dom.value = d.activePage;
30847                 return;
30848             }
30849             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
30850             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
30851             e.stopEvent();
30852         }
30853         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))
30854         {
30855           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
30856           this.field.dom.value = pageNum;
30857           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
30858           e.stopEvent();
30859         }
30860         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
30861         {
30862           var v = this.field.dom.value, pageNum; 
30863           var increment = (e.shiftKey) ? 10 : 1;
30864           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
30865             increment *= -1;
30866           }
30867           if(!v || isNaN(pageNum = parseInt(v, 10))) {
30868             this.field.dom.value = d.activePage;
30869             return;
30870           }
30871           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
30872           {
30873             this.field.dom.value = parseInt(v, 10) + increment;
30874             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
30875             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
30876           }
30877           e.stopEvent();
30878         }
30879     },
30880
30881     // private
30882     beforeLoad : function(){
30883         if(this.loading){
30884             this.loading.disable();
30885         }
30886     },
30887
30888     // private
30889     onClick : function(which){
30890         var ds = this.ds;
30891         switch(which){
30892             case "first":
30893                 ds.load({params:{start: 0, limit: this.pageSize}});
30894             break;
30895             case "prev":
30896                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
30897             break;
30898             case "next":
30899                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
30900             break;
30901             case "last":
30902                 var total = ds.getTotalCount();
30903                 var extra = total % this.pageSize;
30904                 var lastStart = extra ? (total - extra) : total-this.pageSize;
30905                 ds.load({params:{start: lastStart, limit: this.pageSize}});
30906             break;
30907             case "refresh":
30908                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
30909             break;
30910         }
30911     },
30912
30913     /**
30914      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
30915      * @param {Roo.data.Store} store The data store to unbind
30916      */
30917     unbind : function(ds){
30918         ds.un("beforeload", this.beforeLoad, this);
30919         ds.un("load", this.onLoad, this);
30920         ds.un("loadexception", this.onLoadError, this);
30921         ds.un("remove", this.updateInfo, this);
30922         ds.un("add", this.updateInfo, this);
30923         this.ds = undefined;
30924     },
30925
30926     /**
30927      * Binds the paging toolbar to the specified {@link Roo.data.Store}
30928      * @param {Roo.data.Store} store The data store to bind
30929      */
30930     bind : function(ds){
30931         ds.on("beforeload", this.beforeLoad, this);
30932         ds.on("load", this.onLoad, this);
30933         ds.on("loadexception", this.onLoadError, this);
30934         ds.on("remove", this.updateInfo, this);
30935         ds.on("add", this.updateInfo, this);
30936         this.ds = ds;
30937     }
30938 });/*
30939  * Based on:
30940  * Ext JS Library 1.1.1
30941  * Copyright(c) 2006-2007, Ext JS, LLC.
30942  *
30943  * Originally Released Under LGPL - original licence link has changed is not relivant.
30944  *
30945  * Fork - LGPL
30946  * <script type="text/javascript">
30947  */
30948
30949 /**
30950  * @class Roo.Resizable
30951  * @extends Roo.util.Observable
30952  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
30953  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
30954  * 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
30955  * the element will be wrapped for you automatically.</p>
30956  * <p>Here is the list of valid resize handles:</p>
30957  * <pre>
30958 Value   Description
30959 ------  -------------------
30960  'n'     north
30961  's'     south
30962  'e'     east
30963  'w'     west
30964  'nw'    northwest
30965  'sw'    southwest
30966  'se'    southeast
30967  'ne'    northeast
30968  'hd'    horizontal drag
30969  'all'   all
30970 </pre>
30971  * <p>Here's an example showing the creation of a typical Resizable:</p>
30972  * <pre><code>
30973 var resizer = new Roo.Resizable("element-id", {
30974     handles: 'all',
30975     minWidth: 200,
30976     minHeight: 100,
30977     maxWidth: 500,
30978     maxHeight: 400,
30979     pinned: true
30980 });
30981 resizer.on("resize", myHandler);
30982 </code></pre>
30983  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
30984  * resizer.east.setDisplayed(false);</p>
30985  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
30986  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
30987  * resize operation's new size (defaults to [0, 0])
30988  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
30989  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
30990  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
30991  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
30992  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
30993  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
30994  * @cfg {Number} width The width of the element in pixels (defaults to null)
30995  * @cfg {Number} height The height of the element in pixels (defaults to null)
30996  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
30997  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
30998  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
30999  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
31000  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
31001  * in favor of the handles config option (defaults to false)
31002  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
31003  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
31004  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
31005  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
31006  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
31007  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
31008  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
31009  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
31010  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
31011  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
31012  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
31013  * @constructor
31014  * Create a new resizable component
31015  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
31016  * @param {Object} config configuration options
31017   */
31018 Roo.Resizable = function(el, config)
31019 {
31020     this.el = Roo.get(el);
31021
31022     if(config && config.wrap){
31023         config.resizeChild = this.el;
31024         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
31025         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
31026         this.el.setStyle("overflow", "hidden");
31027         this.el.setPositioning(config.resizeChild.getPositioning());
31028         config.resizeChild.clearPositioning();
31029         if(!config.width || !config.height){
31030             var csize = config.resizeChild.getSize();
31031             this.el.setSize(csize.width, csize.height);
31032         }
31033         if(config.pinned && !config.adjustments){
31034             config.adjustments = "auto";
31035         }
31036     }
31037
31038     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
31039     this.proxy.unselectable();
31040     this.proxy.enableDisplayMode('block');
31041
31042     Roo.apply(this, config);
31043
31044     if(this.pinned){
31045         this.disableTrackOver = true;
31046         this.el.addClass("x-resizable-pinned");
31047     }
31048     // if the element isn't positioned, make it relative
31049     var position = this.el.getStyle("position");
31050     if(position != "absolute" && position != "fixed"){
31051         this.el.setStyle("position", "relative");
31052     }
31053     if(!this.handles){ // no handles passed, must be legacy style
31054         this.handles = 's,e,se';
31055         if(this.multiDirectional){
31056             this.handles += ',n,w';
31057         }
31058     }
31059     if(this.handles == "all"){
31060         this.handles = "n s e w ne nw se sw";
31061     }
31062     var hs = this.handles.split(/\s*?[,;]\s*?| /);
31063     var ps = Roo.Resizable.positions;
31064     for(var i = 0, len = hs.length; i < len; i++){
31065         if(hs[i] && ps[hs[i]]){
31066             var pos = ps[hs[i]];
31067             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
31068         }
31069     }
31070     // legacy
31071     this.corner = this.southeast;
31072     
31073     // updateBox = the box can move..
31074     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
31075         this.updateBox = true;
31076     }
31077
31078     this.activeHandle = null;
31079
31080     if(this.resizeChild){
31081         if(typeof this.resizeChild == "boolean"){
31082             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
31083         }else{
31084             this.resizeChild = Roo.get(this.resizeChild, true);
31085         }
31086     }
31087     
31088     if(this.adjustments == "auto"){
31089         var rc = this.resizeChild;
31090         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
31091         if(rc && (hw || hn)){
31092             rc.position("relative");
31093             rc.setLeft(hw ? hw.el.getWidth() : 0);
31094             rc.setTop(hn ? hn.el.getHeight() : 0);
31095         }
31096         this.adjustments = [
31097             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
31098             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
31099         ];
31100     }
31101
31102     if(this.draggable){
31103         this.dd = this.dynamic ?
31104             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
31105         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
31106     }
31107
31108     // public events
31109     this.addEvents({
31110         /**
31111          * @event beforeresize
31112          * Fired before resize is allowed. Set enabled to false to cancel resize.
31113          * @param {Roo.Resizable} this
31114          * @param {Roo.EventObject} e The mousedown event
31115          */
31116         "beforeresize" : true,
31117         /**
31118          * @event resizing
31119          * Fired a resizing.
31120          * @param {Roo.Resizable} this
31121          * @param {Number} x The new x position
31122          * @param {Number} y The new y position
31123          * @param {Number} w The new w width
31124          * @param {Number} h The new h hight
31125          * @param {Roo.EventObject} e The mouseup event
31126          */
31127         "resizing" : true,
31128         /**
31129          * @event resize
31130          * Fired after a resize.
31131          * @param {Roo.Resizable} this
31132          * @param {Number} width The new width
31133          * @param {Number} height The new height
31134          * @param {Roo.EventObject} e The mouseup event
31135          */
31136         "resize" : true
31137     });
31138
31139     if(this.width !== null && this.height !== null){
31140         this.resizeTo(this.width, this.height);
31141     }else{
31142         this.updateChildSize();
31143     }
31144     if(Roo.isIE){
31145         this.el.dom.style.zoom = 1;
31146     }
31147     Roo.Resizable.superclass.constructor.call(this);
31148 };
31149
31150 Roo.extend(Roo.Resizable, Roo.util.Observable, {
31151         resizeChild : false,
31152         adjustments : [0, 0],
31153         minWidth : 5,
31154         minHeight : 5,
31155         maxWidth : 10000,
31156         maxHeight : 10000,
31157         enabled : true,
31158         animate : false,
31159         duration : .35,
31160         dynamic : false,
31161         handles : false,
31162         multiDirectional : false,
31163         disableTrackOver : false,
31164         easing : 'easeOutStrong',
31165         widthIncrement : 0,
31166         heightIncrement : 0,
31167         pinned : false,
31168         width : null,
31169         height : null,
31170         preserveRatio : false,
31171         transparent: false,
31172         minX: 0,
31173         minY: 0,
31174         draggable: false,
31175
31176         /**
31177          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
31178          */
31179         constrainTo: undefined,
31180         /**
31181          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
31182          */
31183         resizeRegion: undefined,
31184
31185
31186     /**
31187      * Perform a manual resize
31188      * @param {Number} width
31189      * @param {Number} height
31190      */
31191     resizeTo : function(width, height){
31192         this.el.setSize(width, height);
31193         this.updateChildSize();
31194         this.fireEvent("resize", this, width, height, null);
31195     },
31196
31197     // private
31198     startSizing : function(e, handle){
31199         this.fireEvent("beforeresize", this, e);
31200         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
31201
31202             if(!this.overlay){
31203                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
31204                 this.overlay.unselectable();
31205                 this.overlay.enableDisplayMode("block");
31206                 this.overlay.on("mousemove", this.onMouseMove, this);
31207                 this.overlay.on("mouseup", this.onMouseUp, this);
31208             }
31209             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
31210
31211             this.resizing = true;
31212             this.startBox = this.el.getBox();
31213             this.startPoint = e.getXY();
31214             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
31215                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
31216
31217             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
31218             this.overlay.show();
31219
31220             if(this.constrainTo) {
31221                 var ct = Roo.get(this.constrainTo);
31222                 this.resizeRegion = ct.getRegion().adjust(
31223                     ct.getFrameWidth('t'),
31224                     ct.getFrameWidth('l'),
31225                     -ct.getFrameWidth('b'),
31226                     -ct.getFrameWidth('r')
31227                 );
31228             }
31229
31230             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
31231             this.proxy.show();
31232             this.proxy.setBox(this.startBox);
31233             if(!this.dynamic){
31234                 this.proxy.setStyle('visibility', 'visible');
31235             }
31236         }
31237     },
31238
31239     // private
31240     onMouseDown : function(handle, e){
31241         if(this.enabled){
31242             e.stopEvent();
31243             this.activeHandle = handle;
31244             this.startSizing(e, handle);
31245         }
31246     },
31247
31248     // private
31249     onMouseUp : function(e){
31250         var size = this.resizeElement();
31251         this.resizing = false;
31252         this.handleOut();
31253         this.overlay.hide();
31254         this.proxy.hide();
31255         this.fireEvent("resize", this, size.width, size.height, e);
31256     },
31257
31258     // private
31259     updateChildSize : function(){
31260         
31261         if(this.resizeChild){
31262             var el = this.el;
31263             var child = this.resizeChild;
31264             var adj = this.adjustments;
31265             if(el.dom.offsetWidth){
31266                 var b = el.getSize(true);
31267                 child.setSize(b.width+adj[0], b.height+adj[1]);
31268             }
31269             // Second call here for IE
31270             // The first call enables instant resizing and
31271             // the second call corrects scroll bars if they
31272             // exist
31273             if(Roo.isIE){
31274                 setTimeout(function(){
31275                     if(el.dom.offsetWidth){
31276                         var b = el.getSize(true);
31277                         child.setSize(b.width+adj[0], b.height+adj[1]);
31278                     }
31279                 }, 10);
31280             }
31281         }
31282     },
31283
31284     // private
31285     snap : function(value, inc, min){
31286         if(!inc || !value) {
31287             return value;
31288         }
31289         var newValue = value;
31290         var m = value % inc;
31291         if(m > 0){
31292             if(m > (inc/2)){
31293                 newValue = value + (inc-m);
31294             }else{
31295                 newValue = value - m;
31296             }
31297         }
31298         return Math.max(min, newValue);
31299     },
31300
31301     // private
31302     resizeElement : function(){
31303         var box = this.proxy.getBox();
31304         if(this.updateBox){
31305             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
31306         }else{
31307             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
31308         }
31309         this.updateChildSize();
31310         if(!this.dynamic){
31311             this.proxy.hide();
31312         }
31313         return box;
31314     },
31315
31316     // private
31317     constrain : function(v, diff, m, mx){
31318         if(v - diff < m){
31319             diff = v - m;
31320         }else if(v - diff > mx){
31321             diff = mx - v;
31322         }
31323         return diff;
31324     },
31325
31326     // private
31327     onMouseMove : function(e){
31328         
31329         if(this.enabled){
31330             try{// try catch so if something goes wrong the user doesn't get hung
31331
31332             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
31333                 return;
31334             }
31335
31336             //var curXY = this.startPoint;
31337             var curSize = this.curSize || this.startBox;
31338             var x = this.startBox.x, y = this.startBox.y;
31339             var ox = x, oy = y;
31340             var w = curSize.width, h = curSize.height;
31341             var ow = w, oh = h;
31342             var mw = this.minWidth, mh = this.minHeight;
31343             var mxw = this.maxWidth, mxh = this.maxHeight;
31344             var wi = this.widthIncrement;
31345             var hi = this.heightIncrement;
31346
31347             var eventXY = e.getXY();
31348             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
31349             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
31350
31351             var pos = this.activeHandle.position;
31352
31353             switch(pos){
31354                 case "east":
31355                     w += diffX;
31356                     w = Math.min(Math.max(mw, w), mxw);
31357                     break;
31358              
31359                 case "south":
31360                     h += diffY;
31361                     h = Math.min(Math.max(mh, h), mxh);
31362                     break;
31363                 case "southeast":
31364                     w += diffX;
31365                     h += diffY;
31366                     w = Math.min(Math.max(mw, w), mxw);
31367                     h = Math.min(Math.max(mh, h), mxh);
31368                     break;
31369                 case "north":
31370                     diffY = this.constrain(h, diffY, mh, mxh);
31371                     y += diffY;
31372                     h -= diffY;
31373                     break;
31374                 case "hdrag":
31375                     
31376                     if (wi) {
31377                         var adiffX = Math.abs(diffX);
31378                         var sub = (adiffX % wi); // how much 
31379                         if (sub > (wi/2)) { // far enough to snap
31380                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
31381                         } else {
31382                             // remove difference.. 
31383                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
31384                         }
31385                     }
31386                     x += diffX;
31387                     x = Math.max(this.minX, x);
31388                     break;
31389                 case "west":
31390                     diffX = this.constrain(w, diffX, mw, mxw);
31391                     x += diffX;
31392                     w -= diffX;
31393                     break;
31394                 case "northeast":
31395                     w += diffX;
31396                     w = Math.min(Math.max(mw, w), mxw);
31397                     diffY = this.constrain(h, diffY, mh, mxh);
31398                     y += diffY;
31399                     h -= diffY;
31400                     break;
31401                 case "northwest":
31402                     diffX = this.constrain(w, diffX, mw, mxw);
31403                     diffY = this.constrain(h, diffY, mh, mxh);
31404                     y += diffY;
31405                     h -= diffY;
31406                     x += diffX;
31407                     w -= diffX;
31408                     break;
31409                case "southwest":
31410                     diffX = this.constrain(w, diffX, mw, mxw);
31411                     h += diffY;
31412                     h = Math.min(Math.max(mh, h), mxh);
31413                     x += diffX;
31414                     w -= diffX;
31415                     break;
31416             }
31417
31418             var sw = this.snap(w, wi, mw);
31419             var sh = this.snap(h, hi, mh);
31420             if(sw != w || sh != h){
31421                 switch(pos){
31422                     case "northeast":
31423                         y -= sh - h;
31424                     break;
31425                     case "north":
31426                         y -= sh - h;
31427                         break;
31428                     case "southwest":
31429                         x -= sw - w;
31430                     break;
31431                     case "west":
31432                         x -= sw - w;
31433                         break;
31434                     case "northwest":
31435                         x -= sw - w;
31436                         y -= sh - h;
31437                     break;
31438                 }
31439                 w = sw;
31440                 h = sh;
31441             }
31442
31443             if(this.preserveRatio){
31444                 switch(pos){
31445                     case "southeast":
31446                     case "east":
31447                         h = oh * (w/ow);
31448                         h = Math.min(Math.max(mh, h), mxh);
31449                         w = ow * (h/oh);
31450                        break;
31451                     case "south":
31452                         w = ow * (h/oh);
31453                         w = Math.min(Math.max(mw, w), mxw);
31454                         h = oh * (w/ow);
31455                         break;
31456                     case "northeast":
31457                         w = ow * (h/oh);
31458                         w = Math.min(Math.max(mw, w), mxw);
31459                         h = oh * (w/ow);
31460                     break;
31461                     case "north":
31462                         var tw = w;
31463                         w = ow * (h/oh);
31464                         w = Math.min(Math.max(mw, w), mxw);
31465                         h = oh * (w/ow);
31466                         x += (tw - w) / 2;
31467                         break;
31468                     case "southwest":
31469                         h = oh * (w/ow);
31470                         h = Math.min(Math.max(mh, h), mxh);
31471                         var tw = w;
31472                         w = ow * (h/oh);
31473                         x += tw - w;
31474                         break;
31475                     case "west":
31476                         var th = h;
31477                         h = oh * (w/ow);
31478                         h = Math.min(Math.max(mh, h), mxh);
31479                         y += (th - h) / 2;
31480                         var tw = w;
31481                         w = ow * (h/oh);
31482                         x += tw - w;
31483                        break;
31484                     case "northwest":
31485                         var tw = w;
31486                         var th = h;
31487                         h = oh * (w/ow);
31488                         h = Math.min(Math.max(mh, h), mxh);
31489                         w = ow * (h/oh);
31490                         y += th - h;
31491                         x += tw - w;
31492                        break;
31493
31494                 }
31495             }
31496             if (pos == 'hdrag') {
31497                 w = ow;
31498             }
31499             this.proxy.setBounds(x, y, w, h);
31500             if(this.dynamic){
31501                 this.resizeElement();
31502             }
31503             }catch(e){}
31504         }
31505         this.fireEvent("resizing", this, x, y, w, h, e);
31506     },
31507
31508     // private
31509     handleOver : function(){
31510         if(this.enabled){
31511             this.el.addClass("x-resizable-over");
31512         }
31513     },
31514
31515     // private
31516     handleOut : function(){
31517         if(!this.resizing){
31518             this.el.removeClass("x-resizable-over");
31519         }
31520     },
31521
31522     /**
31523      * Returns the element this component is bound to.
31524      * @return {Roo.Element}
31525      */
31526     getEl : function(){
31527         return this.el;
31528     },
31529
31530     /**
31531      * Returns the resizeChild element (or null).
31532      * @return {Roo.Element}
31533      */
31534     getResizeChild : function(){
31535         return this.resizeChild;
31536     },
31537     groupHandler : function()
31538     {
31539         
31540     },
31541     /**
31542      * Destroys this resizable. If the element was wrapped and
31543      * removeEl is not true then the element remains.
31544      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
31545      */
31546     destroy : function(removeEl){
31547         this.proxy.remove();
31548         if(this.overlay){
31549             this.overlay.removeAllListeners();
31550             this.overlay.remove();
31551         }
31552         var ps = Roo.Resizable.positions;
31553         for(var k in ps){
31554             if(typeof ps[k] != "function" && this[ps[k]]){
31555                 var h = this[ps[k]];
31556                 h.el.removeAllListeners();
31557                 h.el.remove();
31558             }
31559         }
31560         if(removeEl){
31561             this.el.update("");
31562             this.el.remove();
31563         }
31564     }
31565 });
31566
31567 // private
31568 // hash to map config positions to true positions
31569 Roo.Resizable.positions = {
31570     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
31571     hd: "hdrag"
31572 };
31573
31574 // private
31575 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
31576     if(!this.tpl){
31577         // only initialize the template if resizable is used
31578         var tpl = Roo.DomHelper.createTemplate(
31579             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
31580         );
31581         tpl.compile();
31582         Roo.Resizable.Handle.prototype.tpl = tpl;
31583     }
31584     this.position = pos;
31585     this.rz = rz;
31586     // show north drag fro topdra
31587     var handlepos = pos == 'hdrag' ? 'north' : pos;
31588     
31589     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
31590     if (pos == 'hdrag') {
31591         this.el.setStyle('cursor', 'pointer');
31592     }
31593     this.el.unselectable();
31594     if(transparent){
31595         this.el.setOpacity(0);
31596     }
31597     this.el.on("mousedown", this.onMouseDown, this);
31598     if(!disableTrackOver){
31599         this.el.on("mouseover", this.onMouseOver, this);
31600         this.el.on("mouseout", this.onMouseOut, this);
31601     }
31602 };
31603
31604 // private
31605 Roo.Resizable.Handle.prototype = {
31606     afterResize : function(rz){
31607         Roo.log('after?');
31608         // do nothing
31609     },
31610     // private
31611     onMouseDown : function(e){
31612         this.rz.onMouseDown(this, e);
31613     },
31614     // private
31615     onMouseOver : function(e){
31616         this.rz.handleOver(this, e);
31617     },
31618     // private
31619     onMouseOut : function(e){
31620         this.rz.handleOut(this, e);
31621     }
31622 };/*
31623  * Based on:
31624  * Ext JS Library 1.1.1
31625  * Copyright(c) 2006-2007, Ext JS, LLC.
31626  *
31627  * Originally Released Under LGPL - original licence link has changed is not relivant.
31628  *
31629  * Fork - LGPL
31630  * <script type="text/javascript">
31631  */
31632
31633 /**
31634  * @class Roo.Editor
31635  * @extends Roo.Component
31636  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
31637  * @constructor
31638  * Create a new Editor
31639  * @param {Roo.form.Field} field The Field object (or descendant)
31640  * @param {Object} config The config object
31641  */
31642 Roo.Editor = function(field, config){
31643     Roo.Editor.superclass.constructor.call(this, config);
31644     this.field = field;
31645     this.addEvents({
31646         /**
31647              * @event beforestartedit
31648              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
31649              * false from the handler of this event.
31650              * @param {Editor} this
31651              * @param {Roo.Element} boundEl The underlying element bound to this editor
31652              * @param {Mixed} value The field value being set
31653              */
31654         "beforestartedit" : true,
31655         /**
31656              * @event startedit
31657              * Fires when this editor is displayed
31658              * @param {Roo.Element} boundEl The underlying element bound to this editor
31659              * @param {Mixed} value The starting field value
31660              */
31661         "startedit" : true,
31662         /**
31663              * @event beforecomplete
31664              * Fires after a change has been made to the field, but before the change is reflected in the underlying
31665              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
31666              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
31667              * event will not fire since no edit actually occurred.
31668              * @param {Editor} this
31669              * @param {Mixed} value The current field value
31670              * @param {Mixed} startValue The original field value
31671              */
31672         "beforecomplete" : true,
31673         /**
31674              * @event complete
31675              * Fires after editing is complete and any changed value has been written to the underlying field.
31676              * @param {Editor} this
31677              * @param {Mixed} value The current field value
31678              * @param {Mixed} startValue The original field value
31679              */
31680         "complete" : true,
31681         /**
31682          * @event specialkey
31683          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
31684          * {@link Roo.EventObject#getKey} to determine which key was pressed.
31685          * @param {Roo.form.Field} this
31686          * @param {Roo.EventObject} e The event object
31687          */
31688         "specialkey" : true
31689     });
31690 };
31691
31692 Roo.extend(Roo.Editor, Roo.Component, {
31693     /**
31694      * @cfg {Boolean/String} autosize
31695      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
31696      * or "height" to adopt the height only (defaults to false)
31697      */
31698     /**
31699      * @cfg {Boolean} revertInvalid
31700      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
31701      * validation fails (defaults to true)
31702      */
31703     /**
31704      * @cfg {Boolean} ignoreNoChange
31705      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
31706      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
31707      * will never be ignored.
31708      */
31709     /**
31710      * @cfg {Boolean} hideEl
31711      * False to keep the bound element visible while the editor is displayed (defaults to true)
31712      */
31713     /**
31714      * @cfg {Mixed} value
31715      * The data value of the underlying field (defaults to "")
31716      */
31717     value : "",
31718     /**
31719      * @cfg {String} alignment
31720      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
31721      */
31722     alignment: "c-c?",
31723     /**
31724      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
31725      * for bottom-right shadow (defaults to "frame")
31726      */
31727     shadow : "frame",
31728     /**
31729      * @cfg {Boolean} constrain True to constrain the editor to the viewport
31730      */
31731     constrain : false,
31732     /**
31733      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
31734      */
31735     completeOnEnter : false,
31736     /**
31737      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
31738      */
31739     cancelOnEsc : false,
31740     /**
31741      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
31742      */
31743     updateEl : false,
31744
31745     // private
31746     onRender : function(ct, position){
31747         this.el = new Roo.Layer({
31748             shadow: this.shadow,
31749             cls: "x-editor",
31750             parentEl : ct,
31751             shim : this.shim,
31752             shadowOffset:4,
31753             id: this.id,
31754             constrain: this.constrain
31755         });
31756         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
31757         if(this.field.msgTarget != 'title'){
31758             this.field.msgTarget = 'qtip';
31759         }
31760         this.field.render(this.el);
31761         if(Roo.isGecko){
31762             this.field.el.dom.setAttribute('autocomplete', 'off');
31763         }
31764         this.field.on("specialkey", this.onSpecialKey, this);
31765         if(this.swallowKeys){
31766             this.field.el.swallowEvent(['keydown','keypress']);
31767         }
31768         this.field.show();
31769         this.field.on("blur", this.onBlur, this);
31770         if(this.field.grow){
31771             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
31772         }
31773     },
31774
31775     onSpecialKey : function(field, e)
31776     {
31777         //Roo.log('editor onSpecialKey');
31778         if(this.completeOnEnter && e.getKey() == e.ENTER){
31779             e.stopEvent();
31780             this.completeEdit();
31781             return;
31782         }
31783         // do not fire special key otherwise it might hide close the editor...
31784         if(e.getKey() == e.ENTER){    
31785             return;
31786         }
31787         if(this.cancelOnEsc && e.getKey() == e.ESC){
31788             this.cancelEdit();
31789             return;
31790         } 
31791         this.fireEvent('specialkey', field, e);
31792     
31793     },
31794
31795     /**
31796      * Starts the editing process and shows the editor.
31797      * @param {String/HTMLElement/Element} el The element to edit
31798      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
31799       * to the innerHTML of el.
31800      */
31801     startEdit : function(el, value){
31802         if(this.editing){
31803             this.completeEdit();
31804         }
31805         this.boundEl = Roo.get(el);
31806         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
31807         if(!this.rendered){
31808             this.render(this.parentEl || document.body);
31809         }
31810         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
31811             return;
31812         }
31813         this.startValue = v;
31814         this.field.setValue(v);
31815         if(this.autoSize){
31816             var sz = this.boundEl.getSize();
31817             switch(this.autoSize){
31818                 case "width":
31819                 this.setSize(sz.width,  "");
31820                 break;
31821                 case "height":
31822                 this.setSize("",  sz.height);
31823                 break;
31824                 default:
31825                 this.setSize(sz.width,  sz.height);
31826             }
31827         }
31828         this.el.alignTo(this.boundEl, this.alignment);
31829         this.editing = true;
31830         if(Roo.QuickTips){
31831             Roo.QuickTips.disable();
31832         }
31833         this.show();
31834     },
31835
31836     /**
31837      * Sets the height and width of this editor.
31838      * @param {Number} width The new width
31839      * @param {Number} height The new height
31840      */
31841     setSize : function(w, h){
31842         this.field.setSize(w, h);
31843         if(this.el){
31844             this.el.sync();
31845         }
31846     },
31847
31848     /**
31849      * Realigns the editor to the bound field based on the current alignment config value.
31850      */
31851     realign : function(){
31852         this.el.alignTo(this.boundEl, this.alignment);
31853     },
31854
31855     /**
31856      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
31857      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
31858      */
31859     completeEdit : function(remainVisible){
31860         if(!this.editing){
31861             return;
31862         }
31863         var v = this.getValue();
31864         if(this.revertInvalid !== false && !this.field.isValid()){
31865             v = this.startValue;
31866             this.cancelEdit(true);
31867         }
31868         if(String(v) === String(this.startValue) && this.ignoreNoChange){
31869             this.editing = false;
31870             this.hide();
31871             return;
31872         }
31873         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
31874             this.editing = false;
31875             if(this.updateEl && this.boundEl){
31876                 this.boundEl.update(v);
31877             }
31878             if(remainVisible !== true){
31879                 this.hide();
31880             }
31881             this.fireEvent("complete", this, v, this.startValue);
31882         }
31883     },
31884
31885     // private
31886     onShow : function(){
31887         this.el.show();
31888         if(this.hideEl !== false){
31889             this.boundEl.hide();
31890         }
31891         this.field.show();
31892         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
31893             this.fixIEFocus = true;
31894             this.deferredFocus.defer(50, this);
31895         }else{
31896             this.field.focus();
31897         }
31898         this.fireEvent("startedit", this.boundEl, this.startValue);
31899     },
31900
31901     deferredFocus : function(){
31902         if(this.editing){
31903             this.field.focus();
31904         }
31905     },
31906
31907     /**
31908      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
31909      * reverted to the original starting value.
31910      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
31911      * cancel (defaults to false)
31912      */
31913     cancelEdit : function(remainVisible){
31914         if(this.editing){
31915             this.setValue(this.startValue);
31916             if(remainVisible !== true){
31917                 this.hide();
31918             }
31919         }
31920     },
31921
31922     // private
31923     onBlur : function(){
31924         if(this.allowBlur !== true && this.editing){
31925             this.completeEdit();
31926         }
31927     },
31928
31929     // private
31930     onHide : function(){
31931         if(this.editing){
31932             this.completeEdit();
31933             return;
31934         }
31935         this.field.blur();
31936         if(this.field.collapse){
31937             this.field.collapse();
31938         }
31939         this.el.hide();
31940         if(this.hideEl !== false){
31941             this.boundEl.show();
31942         }
31943         if(Roo.QuickTips){
31944             Roo.QuickTips.enable();
31945         }
31946     },
31947
31948     /**
31949      * Sets the data value of the editor
31950      * @param {Mixed} value Any valid value supported by the underlying field
31951      */
31952     setValue : function(v){
31953         this.field.setValue(v);
31954     },
31955
31956     /**
31957      * Gets the data value of the editor
31958      * @return {Mixed} The data value
31959      */
31960     getValue : function(){
31961         return this.field.getValue();
31962     }
31963 });/*
31964  * Based on:
31965  * Ext JS Library 1.1.1
31966  * Copyright(c) 2006-2007, Ext JS, LLC.
31967  *
31968  * Originally Released Under LGPL - original licence link has changed is not relivant.
31969  *
31970  * Fork - LGPL
31971  * <script type="text/javascript">
31972  */
31973  
31974 /**
31975  * @class Roo.BasicDialog
31976  * @extends Roo.util.Observable
31977  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
31978  * <pre><code>
31979 var dlg = new Roo.BasicDialog("my-dlg", {
31980     height: 200,
31981     width: 300,
31982     minHeight: 100,
31983     minWidth: 150,
31984     modal: true,
31985     proxyDrag: true,
31986     shadow: true
31987 });
31988 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
31989 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
31990 dlg.addButton('Cancel', dlg.hide, dlg);
31991 dlg.show();
31992 </code></pre>
31993   <b>A Dialog should always be a direct child of the body element.</b>
31994  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
31995  * @cfg {String} title Default text to display in the title bar (defaults to null)
31996  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
31997  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
31998  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
31999  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
32000  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
32001  * (defaults to null with no animation)
32002  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
32003  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
32004  * property for valid values (defaults to 'all')
32005  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
32006  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
32007  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
32008  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
32009  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
32010  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
32011  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
32012  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
32013  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
32014  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
32015  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
32016  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
32017  * draggable = true (defaults to false)
32018  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
32019  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
32020  * shadow (defaults to false)
32021  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
32022  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
32023  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
32024  * @cfg {Array} buttons Array of buttons
32025  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
32026  * @constructor
32027  * Create a new BasicDialog.
32028  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
32029  * @param {Object} config Configuration options
32030  */
32031 Roo.BasicDialog = function(el, config){
32032     this.el = Roo.get(el);
32033     var dh = Roo.DomHelper;
32034     if(!this.el && config && config.autoCreate){
32035         if(typeof config.autoCreate == "object"){
32036             if(!config.autoCreate.id){
32037                 config.autoCreate.id = el;
32038             }
32039             this.el = dh.append(document.body,
32040                         config.autoCreate, true);
32041         }else{
32042             this.el = dh.append(document.body,
32043                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
32044         }
32045     }
32046     el = this.el;
32047     el.setDisplayed(true);
32048     el.hide = this.hideAction;
32049     this.id = el.id;
32050     el.addClass("x-dlg");
32051
32052     Roo.apply(this, config);
32053
32054     this.proxy = el.createProxy("x-dlg-proxy");
32055     this.proxy.hide = this.hideAction;
32056     this.proxy.setOpacity(.5);
32057     this.proxy.hide();
32058
32059     if(config.width){
32060         el.setWidth(config.width);
32061     }
32062     if(config.height){
32063         el.setHeight(config.height);
32064     }
32065     this.size = el.getSize();
32066     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
32067         this.xy = [config.x,config.y];
32068     }else{
32069         this.xy = el.getCenterXY(true);
32070     }
32071     /** The header element @type Roo.Element */
32072     this.header = el.child("> .x-dlg-hd");
32073     /** The body element @type Roo.Element */
32074     this.body = el.child("> .x-dlg-bd");
32075     /** The footer element @type Roo.Element */
32076     this.footer = el.child("> .x-dlg-ft");
32077
32078     if(!this.header){
32079         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
32080     }
32081     if(!this.body){
32082         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
32083     }
32084
32085     this.header.unselectable();
32086     if(this.title){
32087         this.header.update(this.title);
32088     }
32089     // this element allows the dialog to be focused for keyboard event
32090     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
32091     this.focusEl.swallowEvent("click", true);
32092
32093     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
32094
32095     // wrap the body and footer for special rendering
32096     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
32097     if(this.footer){
32098         this.bwrap.dom.appendChild(this.footer.dom);
32099     }
32100
32101     this.bg = this.el.createChild({
32102         tag: "div", cls:"x-dlg-bg",
32103         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
32104     });
32105     this.centerBg = this.bg.child("div.x-dlg-bg-center");
32106
32107
32108     if(this.autoScroll !== false && !this.autoTabs){
32109         this.body.setStyle("overflow", "auto");
32110     }
32111
32112     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
32113
32114     if(this.closable !== false){
32115         this.el.addClass("x-dlg-closable");
32116         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
32117         this.close.on("click", this.closeClick, this);
32118         this.close.addClassOnOver("x-dlg-close-over");
32119     }
32120     if(this.collapsible !== false){
32121         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
32122         this.collapseBtn.on("click", this.collapseClick, this);
32123         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
32124         this.header.on("dblclick", this.collapseClick, this);
32125     }
32126     if(this.resizable !== false){
32127         this.el.addClass("x-dlg-resizable");
32128         this.resizer = new Roo.Resizable(el, {
32129             minWidth: this.minWidth || 80,
32130             minHeight:this.minHeight || 80,
32131             handles: this.resizeHandles || "all",
32132             pinned: true
32133         });
32134         this.resizer.on("beforeresize", this.beforeResize, this);
32135         this.resizer.on("resize", this.onResize, this);
32136     }
32137     if(this.draggable !== false){
32138         el.addClass("x-dlg-draggable");
32139         if (!this.proxyDrag) {
32140             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
32141         }
32142         else {
32143             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
32144         }
32145         dd.setHandleElId(this.header.id);
32146         dd.endDrag = this.endMove.createDelegate(this);
32147         dd.startDrag = this.startMove.createDelegate(this);
32148         dd.onDrag = this.onDrag.createDelegate(this);
32149         dd.scroll = false;
32150         this.dd = dd;
32151     }
32152     if(this.modal){
32153         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
32154         this.mask.enableDisplayMode("block");
32155         this.mask.hide();
32156         this.el.addClass("x-dlg-modal");
32157     }
32158     if(this.shadow){
32159         this.shadow = new Roo.Shadow({
32160             mode : typeof this.shadow == "string" ? this.shadow : "sides",
32161             offset : this.shadowOffset
32162         });
32163     }else{
32164         this.shadowOffset = 0;
32165     }
32166     if(Roo.useShims && this.shim !== false){
32167         this.shim = this.el.createShim();
32168         this.shim.hide = this.hideAction;
32169         this.shim.hide();
32170     }else{
32171         this.shim = false;
32172     }
32173     if(this.autoTabs){
32174         this.initTabs();
32175     }
32176     if (this.buttons) { 
32177         var bts= this.buttons;
32178         this.buttons = [];
32179         Roo.each(bts, function(b) {
32180             this.addButton(b);
32181         }, this);
32182     }
32183     
32184     
32185     this.addEvents({
32186         /**
32187          * @event keydown
32188          * Fires when a key is pressed
32189          * @param {Roo.BasicDialog} this
32190          * @param {Roo.EventObject} e
32191          */
32192         "keydown" : true,
32193         /**
32194          * @event move
32195          * Fires when this dialog is moved by the user.
32196          * @param {Roo.BasicDialog} this
32197          * @param {Number} x The new page X
32198          * @param {Number} y The new page Y
32199          */
32200         "move" : true,
32201         /**
32202          * @event resize
32203          * Fires when this dialog is resized by the user.
32204          * @param {Roo.BasicDialog} this
32205          * @param {Number} width The new width
32206          * @param {Number} height The new height
32207          */
32208         "resize" : true,
32209         /**
32210          * @event beforehide
32211          * Fires before this dialog is hidden.
32212          * @param {Roo.BasicDialog} this
32213          */
32214         "beforehide" : true,
32215         /**
32216          * @event hide
32217          * Fires when this dialog is hidden.
32218          * @param {Roo.BasicDialog} this
32219          */
32220         "hide" : true,
32221         /**
32222          * @event beforeshow
32223          * Fires before this dialog is shown.
32224          * @param {Roo.BasicDialog} this
32225          */
32226         "beforeshow" : true,
32227         /**
32228          * @event show
32229          * Fires when this dialog is shown.
32230          * @param {Roo.BasicDialog} this
32231          */
32232         "show" : true
32233     });
32234     el.on("keydown", this.onKeyDown, this);
32235     el.on("mousedown", this.toFront, this);
32236     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
32237     this.el.hide();
32238     Roo.DialogManager.register(this);
32239     Roo.BasicDialog.superclass.constructor.call(this);
32240 };
32241
32242 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
32243     shadowOffset: Roo.isIE ? 6 : 5,
32244     minHeight: 80,
32245     minWidth: 200,
32246     minButtonWidth: 75,
32247     defaultButton: null,
32248     buttonAlign: "right",
32249     tabTag: 'div',
32250     firstShow: true,
32251
32252     /**
32253      * Sets the dialog title text
32254      * @param {String} text The title text to display
32255      * @return {Roo.BasicDialog} this
32256      */
32257     setTitle : function(text){
32258         this.header.update(text);
32259         return this;
32260     },
32261
32262     // private
32263     closeClick : function(){
32264         this.hide();
32265     },
32266
32267     // private
32268     collapseClick : function(){
32269         this[this.collapsed ? "expand" : "collapse"]();
32270     },
32271
32272     /**
32273      * Collapses the dialog to its minimized state (only the title bar is visible).
32274      * Equivalent to the user clicking the collapse dialog button.
32275      */
32276     collapse : function(){
32277         if(!this.collapsed){
32278             this.collapsed = true;
32279             this.el.addClass("x-dlg-collapsed");
32280             this.restoreHeight = this.el.getHeight();
32281             this.resizeTo(this.el.getWidth(), this.header.getHeight());
32282         }
32283     },
32284
32285     /**
32286      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
32287      * clicking the expand dialog button.
32288      */
32289     expand : function(){
32290         if(this.collapsed){
32291             this.collapsed = false;
32292             this.el.removeClass("x-dlg-collapsed");
32293             this.resizeTo(this.el.getWidth(), this.restoreHeight);
32294         }
32295     },
32296
32297     /**
32298      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
32299      * @return {Roo.TabPanel} The tabs component
32300      */
32301     initTabs : function(){
32302         var tabs = this.getTabs();
32303         while(tabs.getTab(0)){
32304             tabs.removeTab(0);
32305         }
32306         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
32307             var dom = el.dom;
32308             tabs.addTab(Roo.id(dom), dom.title);
32309             dom.title = "";
32310         });
32311         tabs.activate(0);
32312         return tabs;
32313     },
32314
32315     // private
32316     beforeResize : function(){
32317         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
32318     },
32319
32320     // private
32321     onResize : function(){
32322         this.refreshSize();
32323         this.syncBodyHeight();
32324         this.adjustAssets();
32325         this.focus();
32326         this.fireEvent("resize", this, this.size.width, this.size.height);
32327     },
32328
32329     // private
32330     onKeyDown : function(e){
32331         if(this.isVisible()){
32332             this.fireEvent("keydown", this, e);
32333         }
32334     },
32335
32336     /**
32337      * Resizes the dialog.
32338      * @param {Number} width
32339      * @param {Number} height
32340      * @return {Roo.BasicDialog} this
32341      */
32342     resizeTo : function(width, height){
32343         this.el.setSize(width, height);
32344         this.size = {width: width, height: height};
32345         this.syncBodyHeight();
32346         if(this.fixedcenter){
32347             this.center();
32348         }
32349         if(this.isVisible()){
32350             this.constrainXY();
32351             this.adjustAssets();
32352         }
32353         this.fireEvent("resize", this, width, height);
32354         return this;
32355     },
32356
32357
32358     /**
32359      * Resizes the dialog to fit the specified content size.
32360      * @param {Number} width
32361      * @param {Number} height
32362      * @return {Roo.BasicDialog} this
32363      */
32364     setContentSize : function(w, h){
32365         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
32366         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
32367         //if(!this.el.isBorderBox()){
32368             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
32369             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
32370         //}
32371         if(this.tabs){
32372             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
32373             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
32374         }
32375         this.resizeTo(w, h);
32376         return this;
32377     },
32378
32379     /**
32380      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
32381      * executed in response to a particular key being pressed while the dialog is active.
32382      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
32383      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
32384      * @param {Function} fn The function to call
32385      * @param {Object} scope (optional) The scope of the function
32386      * @return {Roo.BasicDialog} this
32387      */
32388     addKeyListener : function(key, fn, scope){
32389         var keyCode, shift, ctrl, alt;
32390         if(typeof key == "object" && !(key instanceof Array)){
32391             keyCode = key["key"];
32392             shift = key["shift"];
32393             ctrl = key["ctrl"];
32394             alt = key["alt"];
32395         }else{
32396             keyCode = key;
32397         }
32398         var handler = function(dlg, e){
32399             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
32400                 var k = e.getKey();
32401                 if(keyCode instanceof Array){
32402                     for(var i = 0, len = keyCode.length; i < len; i++){
32403                         if(keyCode[i] == k){
32404                           fn.call(scope || window, dlg, k, e);
32405                           return;
32406                         }
32407                     }
32408                 }else{
32409                     if(k == keyCode){
32410                         fn.call(scope || window, dlg, k, e);
32411                     }
32412                 }
32413             }
32414         };
32415         this.on("keydown", handler);
32416         return this;
32417     },
32418
32419     /**
32420      * Returns the TabPanel component (creates it if it doesn't exist).
32421      * Note: If you wish to simply check for the existence of tabs without creating them,
32422      * check for a null 'tabs' property.
32423      * @return {Roo.TabPanel} The tabs component
32424      */
32425     getTabs : function(){
32426         if(!this.tabs){
32427             this.el.addClass("x-dlg-auto-tabs");
32428             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
32429             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
32430         }
32431         return this.tabs;
32432     },
32433
32434     /**
32435      * Adds a button to the footer section of the dialog.
32436      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
32437      * object or a valid Roo.DomHelper element config
32438      * @param {Function} handler The function called when the button is clicked
32439      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
32440      * @return {Roo.Button} The new button
32441      */
32442     addButton : function(config, handler, scope){
32443         var dh = Roo.DomHelper;
32444         if(!this.footer){
32445             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
32446         }
32447         if(!this.btnContainer){
32448             var tb = this.footer.createChild({
32449
32450                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
32451                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
32452             }, null, true);
32453             this.btnContainer = tb.firstChild.firstChild.firstChild;
32454         }
32455         var bconfig = {
32456             handler: handler,
32457             scope: scope,
32458             minWidth: this.minButtonWidth,
32459             hideParent:true
32460         };
32461         if(typeof config == "string"){
32462             bconfig.text = config;
32463         }else{
32464             if(config.tag){
32465                 bconfig.dhconfig = config;
32466             }else{
32467                 Roo.apply(bconfig, config);
32468             }
32469         }
32470         var fc = false;
32471         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
32472             bconfig.position = Math.max(0, bconfig.position);
32473             fc = this.btnContainer.childNodes[bconfig.position];
32474         }
32475          
32476         var btn = new Roo.Button(
32477             fc ? 
32478                 this.btnContainer.insertBefore(document.createElement("td"),fc)
32479                 : this.btnContainer.appendChild(document.createElement("td")),
32480             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
32481             bconfig
32482         );
32483         this.syncBodyHeight();
32484         if(!this.buttons){
32485             /**
32486              * Array of all the buttons that have been added to this dialog via addButton
32487              * @type Array
32488              */
32489             this.buttons = [];
32490         }
32491         this.buttons.push(btn);
32492         return btn;
32493     },
32494
32495     /**
32496      * Sets the default button to be focused when the dialog is displayed.
32497      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
32498      * @return {Roo.BasicDialog} this
32499      */
32500     setDefaultButton : function(btn){
32501         this.defaultButton = btn;
32502         return this;
32503     },
32504
32505     // private
32506     getHeaderFooterHeight : function(safe){
32507         var height = 0;
32508         if(this.header){
32509            height += this.header.getHeight();
32510         }
32511         if(this.footer){
32512            var fm = this.footer.getMargins();
32513             height += (this.footer.getHeight()+fm.top+fm.bottom);
32514         }
32515         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
32516         height += this.centerBg.getPadding("tb");
32517         return height;
32518     },
32519
32520     // private
32521     syncBodyHeight : function()
32522     {
32523         var bd = this.body, // the text
32524             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
32525             bw = this.bwrap;
32526         var height = this.size.height - this.getHeaderFooterHeight(false);
32527         bd.setHeight(height-bd.getMargins("tb"));
32528         var hh = this.header.getHeight();
32529         var h = this.size.height-hh;
32530         cb.setHeight(h);
32531         
32532         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
32533         bw.setHeight(h-cb.getPadding("tb"));
32534         
32535         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
32536         bd.setWidth(bw.getWidth(true));
32537         if(this.tabs){
32538             this.tabs.syncHeight();
32539             if(Roo.isIE){
32540                 this.tabs.el.repaint();
32541             }
32542         }
32543     },
32544
32545     /**
32546      * Restores the previous state of the dialog if Roo.state is configured.
32547      * @return {Roo.BasicDialog} this
32548      */
32549     restoreState : function(){
32550         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
32551         if(box && box.width){
32552             this.xy = [box.x, box.y];
32553             this.resizeTo(box.width, box.height);
32554         }
32555         return this;
32556     },
32557
32558     // private
32559     beforeShow : function(){
32560         this.expand();
32561         if(this.fixedcenter){
32562             this.xy = this.el.getCenterXY(true);
32563         }
32564         if(this.modal){
32565             Roo.get(document.body).addClass("x-body-masked");
32566             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32567             this.mask.show();
32568         }
32569         this.constrainXY();
32570     },
32571
32572     // private
32573     animShow : function(){
32574         var b = Roo.get(this.animateTarget).getBox();
32575         this.proxy.setSize(b.width, b.height);
32576         this.proxy.setLocation(b.x, b.y);
32577         this.proxy.show();
32578         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
32579                     true, .35, this.showEl.createDelegate(this));
32580     },
32581
32582     /**
32583      * Shows the dialog.
32584      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
32585      * @return {Roo.BasicDialog} this
32586      */
32587     show : function(animateTarget){
32588         if (this.fireEvent("beforeshow", this) === false){
32589             return;
32590         }
32591         if(this.syncHeightBeforeShow){
32592             this.syncBodyHeight();
32593         }else if(this.firstShow){
32594             this.firstShow = false;
32595             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
32596         }
32597         this.animateTarget = animateTarget || this.animateTarget;
32598         if(!this.el.isVisible()){
32599             this.beforeShow();
32600             if(this.animateTarget && Roo.get(this.animateTarget)){
32601                 this.animShow();
32602             }else{
32603                 this.showEl();
32604             }
32605         }
32606         return this;
32607     },
32608
32609     // private
32610     showEl : function(){
32611         this.proxy.hide();
32612         this.el.setXY(this.xy);
32613         this.el.show();
32614         this.adjustAssets(true);
32615         this.toFront();
32616         this.focus();
32617         // IE peekaboo bug - fix found by Dave Fenwick
32618         if(Roo.isIE){
32619             this.el.repaint();
32620         }
32621         this.fireEvent("show", this);
32622     },
32623
32624     /**
32625      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
32626      * dialog itself will receive focus.
32627      */
32628     focus : function(){
32629         if(this.defaultButton){
32630             this.defaultButton.focus();
32631         }else{
32632             this.focusEl.focus();
32633         }
32634     },
32635
32636     // private
32637     constrainXY : function(){
32638         if(this.constraintoviewport !== false){
32639             if(!this.viewSize){
32640                 if(this.container){
32641                     var s = this.container.getSize();
32642                     this.viewSize = [s.width, s.height];
32643                 }else{
32644                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
32645                 }
32646             }
32647             var s = Roo.get(this.container||document).getScroll();
32648
32649             var x = this.xy[0], y = this.xy[1];
32650             var w = this.size.width, h = this.size.height;
32651             var vw = this.viewSize[0], vh = this.viewSize[1];
32652             // only move it if it needs it
32653             var moved = false;
32654             // first validate right/bottom
32655             if(x + w > vw+s.left){
32656                 x = vw - w;
32657                 moved = true;
32658             }
32659             if(y + h > vh+s.top){
32660                 y = vh - h;
32661                 moved = true;
32662             }
32663             // then make sure top/left isn't negative
32664             if(x < s.left){
32665                 x = s.left;
32666                 moved = true;
32667             }
32668             if(y < s.top){
32669                 y = s.top;
32670                 moved = true;
32671             }
32672             if(moved){
32673                 // cache xy
32674                 this.xy = [x, y];
32675                 if(this.isVisible()){
32676                     this.el.setLocation(x, y);
32677                     this.adjustAssets();
32678                 }
32679             }
32680         }
32681     },
32682
32683     // private
32684     onDrag : function(){
32685         if(!this.proxyDrag){
32686             this.xy = this.el.getXY();
32687             this.adjustAssets();
32688         }
32689     },
32690
32691     // private
32692     adjustAssets : function(doShow){
32693         var x = this.xy[0], y = this.xy[1];
32694         var w = this.size.width, h = this.size.height;
32695         if(doShow === true){
32696             if(this.shadow){
32697                 this.shadow.show(this.el);
32698             }
32699             if(this.shim){
32700                 this.shim.show();
32701             }
32702         }
32703         if(this.shadow && this.shadow.isVisible()){
32704             this.shadow.show(this.el);
32705         }
32706         if(this.shim && this.shim.isVisible()){
32707             this.shim.setBounds(x, y, w, h);
32708         }
32709     },
32710
32711     // private
32712     adjustViewport : function(w, h){
32713         if(!w || !h){
32714             w = Roo.lib.Dom.getViewWidth();
32715             h = Roo.lib.Dom.getViewHeight();
32716         }
32717         // cache the size
32718         this.viewSize = [w, h];
32719         if(this.modal && this.mask.isVisible()){
32720             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
32721             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32722         }
32723         if(this.isVisible()){
32724             this.constrainXY();
32725         }
32726     },
32727
32728     /**
32729      * Destroys this dialog and all its supporting elements (including any tabs, shim,
32730      * shadow, proxy, mask, etc.)  Also removes all event listeners.
32731      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
32732      */
32733     destroy : function(removeEl){
32734         if(this.isVisible()){
32735             this.animateTarget = null;
32736             this.hide();
32737         }
32738         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
32739         if(this.tabs){
32740             this.tabs.destroy(removeEl);
32741         }
32742         Roo.destroy(
32743              this.shim,
32744              this.proxy,
32745              this.resizer,
32746              this.close,
32747              this.mask
32748         );
32749         if(this.dd){
32750             this.dd.unreg();
32751         }
32752         if(this.buttons){
32753            for(var i = 0, len = this.buttons.length; i < len; i++){
32754                this.buttons[i].destroy();
32755            }
32756         }
32757         this.el.removeAllListeners();
32758         if(removeEl === true){
32759             this.el.update("");
32760             this.el.remove();
32761         }
32762         Roo.DialogManager.unregister(this);
32763     },
32764
32765     // private
32766     startMove : function(){
32767         if(this.proxyDrag){
32768             this.proxy.show();
32769         }
32770         if(this.constraintoviewport !== false){
32771             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
32772         }
32773     },
32774
32775     // private
32776     endMove : function(){
32777         if(!this.proxyDrag){
32778             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
32779         }else{
32780             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
32781             this.proxy.hide();
32782         }
32783         this.refreshSize();
32784         this.adjustAssets();
32785         this.focus();
32786         this.fireEvent("move", this, this.xy[0], this.xy[1]);
32787     },
32788
32789     /**
32790      * Brings this dialog to the front of any other visible dialogs
32791      * @return {Roo.BasicDialog} this
32792      */
32793     toFront : function(){
32794         Roo.DialogManager.bringToFront(this);
32795         return this;
32796     },
32797
32798     /**
32799      * Sends this dialog to the back (under) of any other visible dialogs
32800      * @return {Roo.BasicDialog} this
32801      */
32802     toBack : function(){
32803         Roo.DialogManager.sendToBack(this);
32804         return this;
32805     },
32806
32807     /**
32808      * Centers this dialog in the viewport
32809      * @return {Roo.BasicDialog} this
32810      */
32811     center : function(){
32812         var xy = this.el.getCenterXY(true);
32813         this.moveTo(xy[0], xy[1]);
32814         return this;
32815     },
32816
32817     /**
32818      * Moves the dialog's top-left corner to the specified point
32819      * @param {Number} x
32820      * @param {Number} y
32821      * @return {Roo.BasicDialog} this
32822      */
32823     moveTo : function(x, y){
32824         this.xy = [x,y];
32825         if(this.isVisible()){
32826             this.el.setXY(this.xy);
32827             this.adjustAssets();
32828         }
32829         return this;
32830     },
32831
32832     /**
32833      * Aligns the dialog to the specified element
32834      * @param {String/HTMLElement/Roo.Element} element The element to align to.
32835      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
32836      * @param {Array} offsets (optional) Offset the positioning by [x, y]
32837      * @return {Roo.BasicDialog} this
32838      */
32839     alignTo : function(element, position, offsets){
32840         this.xy = this.el.getAlignToXY(element, position, offsets);
32841         if(this.isVisible()){
32842             this.el.setXY(this.xy);
32843             this.adjustAssets();
32844         }
32845         return this;
32846     },
32847
32848     /**
32849      * Anchors an element to another element and realigns it when the window is resized.
32850      * @param {String/HTMLElement/Roo.Element} element The element to align to.
32851      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
32852      * @param {Array} offsets (optional) Offset the positioning by [x, y]
32853      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
32854      * is a number, it is used as the buffer delay (defaults to 50ms).
32855      * @return {Roo.BasicDialog} this
32856      */
32857     anchorTo : function(el, alignment, offsets, monitorScroll){
32858         var action = function(){
32859             this.alignTo(el, alignment, offsets);
32860         };
32861         Roo.EventManager.onWindowResize(action, this);
32862         var tm = typeof monitorScroll;
32863         if(tm != 'undefined'){
32864             Roo.EventManager.on(window, 'scroll', action, this,
32865                 {buffer: tm == 'number' ? monitorScroll : 50});
32866         }
32867         action.call(this);
32868         return this;
32869     },
32870
32871     /**
32872      * Returns true if the dialog is visible
32873      * @return {Boolean}
32874      */
32875     isVisible : function(){
32876         return this.el.isVisible();
32877     },
32878
32879     // private
32880     animHide : function(callback){
32881         var b = Roo.get(this.animateTarget).getBox();
32882         this.proxy.show();
32883         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
32884         this.el.hide();
32885         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
32886                     this.hideEl.createDelegate(this, [callback]));
32887     },
32888
32889     /**
32890      * Hides the dialog.
32891      * @param {Function} callback (optional) Function to call when the dialog is hidden
32892      * @return {Roo.BasicDialog} this
32893      */
32894     hide : function(callback){
32895         if (this.fireEvent("beforehide", this) === false){
32896             return;
32897         }
32898         if(this.shadow){
32899             this.shadow.hide();
32900         }
32901         if(this.shim) {
32902           this.shim.hide();
32903         }
32904         // sometimes animateTarget seems to get set.. causing problems...
32905         // this just double checks..
32906         if(this.animateTarget && Roo.get(this.animateTarget)) {
32907            this.animHide(callback);
32908         }else{
32909             this.el.hide();
32910             this.hideEl(callback);
32911         }
32912         return this;
32913     },
32914
32915     // private
32916     hideEl : function(callback){
32917         this.proxy.hide();
32918         if(this.modal){
32919             this.mask.hide();
32920             Roo.get(document.body).removeClass("x-body-masked");
32921         }
32922         this.fireEvent("hide", this);
32923         if(typeof callback == "function"){
32924             callback();
32925         }
32926     },
32927
32928     // private
32929     hideAction : function(){
32930         this.setLeft("-10000px");
32931         this.setTop("-10000px");
32932         this.setStyle("visibility", "hidden");
32933     },
32934
32935     // private
32936     refreshSize : function(){
32937         this.size = this.el.getSize();
32938         this.xy = this.el.getXY();
32939         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
32940     },
32941
32942     // private
32943     // z-index is managed by the DialogManager and may be overwritten at any time
32944     setZIndex : function(index){
32945         if(this.modal){
32946             this.mask.setStyle("z-index", index);
32947         }
32948         if(this.shim){
32949             this.shim.setStyle("z-index", ++index);
32950         }
32951         if(this.shadow){
32952             this.shadow.setZIndex(++index);
32953         }
32954         this.el.setStyle("z-index", ++index);
32955         if(this.proxy){
32956             this.proxy.setStyle("z-index", ++index);
32957         }
32958         if(this.resizer){
32959             this.resizer.proxy.setStyle("z-index", ++index);
32960         }
32961
32962         this.lastZIndex = index;
32963     },
32964
32965     /**
32966      * Returns the element for this dialog
32967      * @return {Roo.Element} The underlying dialog Element
32968      */
32969     getEl : function(){
32970         return this.el;
32971     }
32972 });
32973
32974 /**
32975  * @class Roo.DialogManager
32976  * Provides global access to BasicDialogs that have been created and
32977  * support for z-indexing (layering) multiple open dialogs.
32978  */
32979 Roo.DialogManager = function(){
32980     var list = {};
32981     var accessList = [];
32982     var front = null;
32983
32984     // private
32985     var sortDialogs = function(d1, d2){
32986         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
32987     };
32988
32989     // private
32990     var orderDialogs = function(){
32991         accessList.sort(sortDialogs);
32992         var seed = Roo.DialogManager.zseed;
32993         for(var i = 0, len = accessList.length; i < len; i++){
32994             var dlg = accessList[i];
32995             if(dlg){
32996                 dlg.setZIndex(seed + (i*10));
32997             }
32998         }
32999     };
33000
33001     return {
33002         /**
33003          * The starting z-index for BasicDialogs (defaults to 9000)
33004          * @type Number The z-index value
33005          */
33006         zseed : 9000,
33007
33008         // private
33009         register : function(dlg){
33010             list[dlg.id] = dlg;
33011             accessList.push(dlg);
33012         },
33013
33014         // private
33015         unregister : function(dlg){
33016             delete list[dlg.id];
33017             var i=0;
33018             var len=0;
33019             if(!accessList.indexOf){
33020                 for(  i = 0, len = accessList.length; i < len; i++){
33021                     if(accessList[i] == dlg){
33022                         accessList.splice(i, 1);
33023                         return;
33024                     }
33025                 }
33026             }else{
33027                  i = accessList.indexOf(dlg);
33028                 if(i != -1){
33029                     accessList.splice(i, 1);
33030                 }
33031             }
33032         },
33033
33034         /**
33035          * Gets a registered dialog by id
33036          * @param {String/Object} id The id of the dialog or a dialog
33037          * @return {Roo.BasicDialog} this
33038          */
33039         get : function(id){
33040             return typeof id == "object" ? id : list[id];
33041         },
33042
33043         /**
33044          * Brings the specified dialog to the front
33045          * @param {String/Object} dlg The id of the dialog or a dialog
33046          * @return {Roo.BasicDialog} this
33047          */
33048         bringToFront : function(dlg){
33049             dlg = this.get(dlg);
33050             if(dlg != front){
33051                 front = dlg;
33052                 dlg._lastAccess = new Date().getTime();
33053                 orderDialogs();
33054             }
33055             return dlg;
33056         },
33057
33058         /**
33059          * Sends the specified dialog to the back
33060          * @param {String/Object} dlg The id of the dialog or a dialog
33061          * @return {Roo.BasicDialog} this
33062          */
33063         sendToBack : function(dlg){
33064             dlg = this.get(dlg);
33065             dlg._lastAccess = -(new Date().getTime());
33066             orderDialogs();
33067             return dlg;
33068         },
33069
33070         /**
33071          * Hides all dialogs
33072          */
33073         hideAll : function(){
33074             for(var id in list){
33075                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
33076                     list[id].hide();
33077                 }
33078             }
33079         }
33080     };
33081 }();
33082
33083 /**
33084  * @class Roo.LayoutDialog
33085  * @extends Roo.BasicDialog
33086  * Dialog which provides adjustments for working with a layout in a Dialog.
33087  * Add your necessary layout config options to the dialog's config.<br>
33088  * Example usage (including a nested layout):
33089  * <pre><code>
33090 if(!dialog){
33091     dialog = new Roo.LayoutDialog("download-dlg", {
33092         modal: true,
33093         width:600,
33094         height:450,
33095         shadow:true,
33096         minWidth:500,
33097         minHeight:350,
33098         autoTabs:true,
33099         proxyDrag:true,
33100         // layout config merges with the dialog config
33101         center:{
33102             tabPosition: "top",
33103             alwaysShowTabs: true
33104         }
33105     });
33106     dialog.addKeyListener(27, dialog.hide, dialog);
33107     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
33108     dialog.addButton("Build It!", this.getDownload, this);
33109
33110     // we can even add nested layouts
33111     var innerLayout = new Roo.BorderLayout("dl-inner", {
33112         east: {
33113             initialSize: 200,
33114             autoScroll:true,
33115             split:true
33116         },
33117         center: {
33118             autoScroll:true
33119         }
33120     });
33121     innerLayout.beginUpdate();
33122     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
33123     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
33124     innerLayout.endUpdate(true);
33125
33126     var layout = dialog.getLayout();
33127     layout.beginUpdate();
33128     layout.add("center", new Roo.ContentPanel("standard-panel",
33129                         {title: "Download the Source", fitToFrame:true}));
33130     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
33131                {title: "Build your own roo.js"}));
33132     layout.getRegion("center").showPanel(sp);
33133     layout.endUpdate();
33134 }
33135 </code></pre>
33136     * @constructor
33137     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
33138     * @param {Object} config configuration options
33139   */
33140 Roo.LayoutDialog = function(el, cfg){
33141     
33142     var config=  cfg;
33143     if (typeof(cfg) == 'undefined') {
33144         config = Roo.apply({}, el);
33145         // not sure why we use documentElement here.. - it should always be body.
33146         // IE7 borks horribly if we use documentElement.
33147         // webkit also does not like documentElement - it creates a body element...
33148         el = Roo.get( document.body || document.documentElement ).createChild();
33149         //config.autoCreate = true;
33150     }
33151     
33152     
33153     config.autoTabs = false;
33154     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
33155     this.body.setStyle({overflow:"hidden", position:"relative"});
33156     this.layout = new Roo.BorderLayout(this.body.dom, config);
33157     this.layout.monitorWindowResize = false;
33158     this.el.addClass("x-dlg-auto-layout");
33159     // fix case when center region overwrites center function
33160     this.center = Roo.BasicDialog.prototype.center;
33161     this.on("show", this.layout.layout, this.layout, true);
33162     if (config.items) {
33163         var xitems = config.items;
33164         delete config.items;
33165         Roo.each(xitems, this.addxtype, this);
33166     }
33167     
33168     
33169 };
33170 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
33171     /**
33172      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
33173      * @deprecated
33174      */
33175     endUpdate : function(){
33176         this.layout.endUpdate();
33177     },
33178
33179     /**
33180      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
33181      *  @deprecated
33182      */
33183     beginUpdate : function(){
33184         this.layout.beginUpdate();
33185     },
33186
33187     /**
33188      * Get the BorderLayout for this dialog
33189      * @return {Roo.BorderLayout}
33190      */
33191     getLayout : function(){
33192         return this.layout;
33193     },
33194
33195     showEl : function(){
33196         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
33197         if(Roo.isIE7){
33198             this.layout.layout();
33199         }
33200     },
33201
33202     // private
33203     // Use the syncHeightBeforeShow config option to control this automatically
33204     syncBodyHeight : function(){
33205         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
33206         if(this.layout){this.layout.layout();}
33207     },
33208     
33209       /**
33210      * Add an xtype element (actually adds to the layout.)
33211      * @return {Object} xdata xtype object data.
33212      */
33213     
33214     addxtype : function(c) {
33215         return this.layout.addxtype(c);
33216     }
33217 });/*
33218  * Based on:
33219  * Ext JS Library 1.1.1
33220  * Copyright(c) 2006-2007, Ext JS, LLC.
33221  *
33222  * Originally Released Under LGPL - original licence link has changed is not relivant.
33223  *
33224  * Fork - LGPL
33225  * <script type="text/javascript">
33226  */
33227  
33228 /**
33229  * @class Roo.MessageBox
33230  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
33231  * Example usage:
33232  *<pre><code>
33233 // Basic alert:
33234 Roo.Msg.alert('Status', 'Changes saved successfully.');
33235
33236 // Prompt for user data:
33237 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
33238     if (btn == 'ok'){
33239         // process text value...
33240     }
33241 });
33242
33243 // Show a dialog using config options:
33244 Roo.Msg.show({
33245    title:'Save Changes?',
33246    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
33247    buttons: Roo.Msg.YESNOCANCEL,
33248    fn: processResult,
33249    animEl: 'elId'
33250 });
33251 </code></pre>
33252  * @singleton
33253  */
33254 Roo.MessageBox = function(){
33255     var dlg, opt, mask, waitTimer;
33256     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
33257     var buttons, activeTextEl, bwidth;
33258
33259     // private
33260     var handleButton = function(button){
33261         dlg.hide();
33262         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
33263     };
33264
33265     // private
33266     var handleHide = function(){
33267         if(opt && opt.cls){
33268             dlg.el.removeClass(opt.cls);
33269         }
33270         if(waitTimer){
33271             Roo.TaskMgr.stop(waitTimer);
33272             waitTimer = null;
33273         }
33274     };
33275
33276     // private
33277     var updateButtons = function(b){
33278         var width = 0;
33279         if(!b){
33280             buttons["ok"].hide();
33281             buttons["cancel"].hide();
33282             buttons["yes"].hide();
33283             buttons["no"].hide();
33284             dlg.footer.dom.style.display = 'none';
33285             return width;
33286         }
33287         dlg.footer.dom.style.display = '';
33288         for(var k in buttons){
33289             if(typeof buttons[k] != "function"){
33290                 if(b[k]){
33291                     buttons[k].show();
33292                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
33293                     width += buttons[k].el.getWidth()+15;
33294                 }else{
33295                     buttons[k].hide();
33296                 }
33297             }
33298         }
33299         return width;
33300     };
33301
33302     // private
33303     var handleEsc = function(d, k, e){
33304         if(opt && opt.closable !== false){
33305             dlg.hide();
33306         }
33307         if(e){
33308             e.stopEvent();
33309         }
33310     };
33311
33312     return {
33313         /**
33314          * Returns a reference to the underlying {@link Roo.BasicDialog} element
33315          * @return {Roo.BasicDialog} The BasicDialog element
33316          */
33317         getDialog : function(){
33318            if(!dlg){
33319                 dlg = new Roo.BasicDialog("x-msg-box", {
33320                     autoCreate : true,
33321                     shadow: true,
33322                     draggable: true,
33323                     resizable:false,
33324                     constraintoviewport:false,
33325                     fixedcenter:true,
33326                     collapsible : false,
33327                     shim:true,
33328                     modal: true,
33329                     width:400, height:100,
33330                     buttonAlign:"center",
33331                     closeClick : function(){
33332                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
33333                             handleButton("no");
33334                         }else{
33335                             handleButton("cancel");
33336                         }
33337                     }
33338                 });
33339                 dlg.on("hide", handleHide);
33340                 mask = dlg.mask;
33341                 dlg.addKeyListener(27, handleEsc);
33342                 buttons = {};
33343                 var bt = this.buttonText;
33344                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
33345                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
33346                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
33347                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
33348                 bodyEl = dlg.body.createChild({
33349
33350                     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>'
33351                 });
33352                 msgEl = bodyEl.dom.firstChild;
33353                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
33354                 textboxEl.enableDisplayMode();
33355                 textboxEl.addKeyListener([10,13], function(){
33356                     if(dlg.isVisible() && opt && opt.buttons){
33357                         if(opt.buttons.ok){
33358                             handleButton("ok");
33359                         }else if(opt.buttons.yes){
33360                             handleButton("yes");
33361                         }
33362                     }
33363                 });
33364                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
33365                 textareaEl.enableDisplayMode();
33366                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
33367                 progressEl.enableDisplayMode();
33368                 var pf = progressEl.dom.firstChild;
33369                 if (pf) {
33370                     pp = Roo.get(pf.firstChild);
33371                     pp.setHeight(pf.offsetHeight);
33372                 }
33373                 
33374             }
33375             return dlg;
33376         },
33377
33378         /**
33379          * Updates the message box body text
33380          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
33381          * the XHTML-compliant non-breaking space character '&amp;#160;')
33382          * @return {Roo.MessageBox} This message box
33383          */
33384         updateText : function(text){
33385             if(!dlg.isVisible() && !opt.width){
33386                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
33387             }
33388             msgEl.innerHTML = text || '&#160;';
33389       
33390             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
33391             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
33392             var w = Math.max(
33393                     Math.min(opt.width || cw , this.maxWidth), 
33394                     Math.max(opt.minWidth || this.minWidth, bwidth)
33395             );
33396             if(opt.prompt){
33397                 activeTextEl.setWidth(w);
33398             }
33399             if(dlg.isVisible()){
33400                 dlg.fixedcenter = false;
33401             }
33402             // to big, make it scroll. = But as usual stupid IE does not support
33403             // !important..
33404             
33405             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
33406                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
33407                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
33408             } else {
33409                 bodyEl.dom.style.height = '';
33410                 bodyEl.dom.style.overflowY = '';
33411             }
33412             if (cw > w) {
33413                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
33414             } else {
33415                 bodyEl.dom.style.overflowX = '';
33416             }
33417             
33418             dlg.setContentSize(w, bodyEl.getHeight());
33419             if(dlg.isVisible()){
33420                 dlg.fixedcenter = true;
33421             }
33422             return this;
33423         },
33424
33425         /**
33426          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
33427          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
33428          * @param {Number} value Any number between 0 and 1 (e.g., .5)
33429          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
33430          * @return {Roo.MessageBox} This message box
33431          */
33432         updateProgress : function(value, text){
33433             if(text){
33434                 this.updateText(text);
33435             }
33436             if (pp) { // weird bug on my firefox - for some reason this is not defined
33437                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
33438             }
33439             return this;
33440         },        
33441
33442         /**
33443          * Returns true if the message box is currently displayed
33444          * @return {Boolean} True if the message box is visible, else false
33445          */
33446         isVisible : function(){
33447             return dlg && dlg.isVisible();  
33448         },
33449
33450         /**
33451          * Hides the message box if it is displayed
33452          */
33453         hide : function(){
33454             if(this.isVisible()){
33455                 dlg.hide();
33456             }  
33457         },
33458
33459         /**
33460          * Displays a new message box, or reinitializes an existing message box, based on the config options
33461          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
33462          * The following config object properties are supported:
33463          * <pre>
33464 Property    Type             Description
33465 ----------  ---------------  ------------------------------------------------------------------------------------
33466 animEl            String/Element   An id or Element from which the message box should animate as it opens and
33467                                    closes (defaults to undefined)
33468 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
33469                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
33470 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
33471                                    progress and wait dialogs will ignore this property and always hide the
33472                                    close button as they can only be closed programmatically.
33473 cls               String           A custom CSS class to apply to the message box element
33474 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
33475                                    displayed (defaults to 75)
33476 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
33477                                    function will be btn (the name of the button that was clicked, if applicable,
33478                                    e.g. "ok"), and text (the value of the active text field, if applicable).
33479                                    Progress and wait dialogs will ignore this option since they do not respond to
33480                                    user actions and can only be closed programmatically, so any required function
33481                                    should be called by the same code after it closes the dialog.
33482 icon              String           A CSS class that provides a background image to be used as an icon for
33483                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
33484 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
33485 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
33486 modal             Boolean          False to allow user interaction with the page while the message box is
33487                                    displayed (defaults to true)
33488 msg               String           A string that will replace the existing message box body text (defaults
33489                                    to the XHTML-compliant non-breaking space character '&#160;')
33490 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
33491 progress          Boolean          True to display a progress bar (defaults to false)
33492 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
33493 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
33494 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
33495 title             String           The title text
33496 value             String           The string value to set into the active textbox element if displayed
33497 wait              Boolean          True to display a progress bar (defaults to false)
33498 width             Number           The width of the dialog in pixels
33499 </pre>
33500          *
33501          * Example usage:
33502          * <pre><code>
33503 Roo.Msg.show({
33504    title: 'Address',
33505    msg: 'Please enter your address:',
33506    width: 300,
33507    buttons: Roo.MessageBox.OKCANCEL,
33508    multiline: true,
33509    fn: saveAddress,
33510    animEl: 'addAddressBtn'
33511 });
33512 </code></pre>
33513          * @param {Object} config Configuration options
33514          * @return {Roo.MessageBox} This message box
33515          */
33516         show : function(options)
33517         {
33518             
33519             // this causes nightmares if you show one dialog after another
33520             // especially on callbacks..
33521              
33522             if(this.isVisible()){
33523                 
33524                 this.hide();
33525                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
33526                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
33527                 Roo.log("New Dialog Message:" +  options.msg )
33528                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
33529                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
33530                 
33531             }
33532             var d = this.getDialog();
33533             opt = options;
33534             d.setTitle(opt.title || "&#160;");
33535             d.close.setDisplayed(opt.closable !== false);
33536             activeTextEl = textboxEl;
33537             opt.prompt = opt.prompt || (opt.multiline ? true : false);
33538             if(opt.prompt){
33539                 if(opt.multiline){
33540                     textboxEl.hide();
33541                     textareaEl.show();
33542                     textareaEl.setHeight(typeof opt.multiline == "number" ?
33543                         opt.multiline : this.defaultTextHeight);
33544                     activeTextEl = textareaEl;
33545                 }else{
33546                     textboxEl.show();
33547                     textareaEl.hide();
33548                 }
33549             }else{
33550                 textboxEl.hide();
33551                 textareaEl.hide();
33552             }
33553             progressEl.setDisplayed(opt.progress === true);
33554             this.updateProgress(0);
33555             activeTextEl.dom.value = opt.value || "";
33556             if(opt.prompt){
33557                 dlg.setDefaultButton(activeTextEl);
33558             }else{
33559                 var bs = opt.buttons;
33560                 var db = null;
33561                 if(bs && bs.ok){
33562                     db = buttons["ok"];
33563                 }else if(bs && bs.yes){
33564                     db = buttons["yes"];
33565                 }
33566                 dlg.setDefaultButton(db);
33567             }
33568             bwidth = updateButtons(opt.buttons);
33569             this.updateText(opt.msg);
33570             if(opt.cls){
33571                 d.el.addClass(opt.cls);
33572             }
33573             d.proxyDrag = opt.proxyDrag === true;
33574             d.modal = opt.modal !== false;
33575             d.mask = opt.modal !== false ? mask : false;
33576             if(!d.isVisible()){
33577                 // force it to the end of the z-index stack so it gets a cursor in FF
33578                 document.body.appendChild(dlg.el.dom);
33579                 d.animateTarget = null;
33580                 d.show(options.animEl);
33581             }
33582             return this;
33583         },
33584
33585         /**
33586          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
33587          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
33588          * and closing the message box when the process is complete.
33589          * @param {String} title The title bar text
33590          * @param {String} msg The message box body text
33591          * @return {Roo.MessageBox} This message box
33592          */
33593         progress : function(title, msg){
33594             this.show({
33595                 title : title,
33596                 msg : msg,
33597                 buttons: false,
33598                 progress:true,
33599                 closable:false,
33600                 minWidth: this.minProgressWidth,
33601                 modal : true
33602             });
33603             return this;
33604         },
33605
33606         /**
33607          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
33608          * If a callback function is passed it will be called after the user clicks the button, and the
33609          * id of the button that was clicked will be passed as the only parameter to the callback
33610          * (could also be the top-right close button).
33611          * @param {String} title The title bar text
33612          * @param {String} msg The message box body text
33613          * @param {Function} fn (optional) The callback function invoked after the message box is closed
33614          * @param {Object} scope (optional) The scope of the callback function
33615          * @return {Roo.MessageBox} This message box
33616          */
33617         alert : function(title, msg, fn, scope){
33618             this.show({
33619                 title : title,
33620                 msg : msg,
33621                 buttons: this.OK,
33622                 fn: fn,
33623                 scope : scope,
33624                 modal : true
33625             });
33626             return this;
33627         },
33628
33629         /**
33630          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
33631          * interaction while waiting for a long-running process to complete that does not have defined intervals.
33632          * You are responsible for closing the message box when the process is complete.
33633          * @param {String} msg The message box body text
33634          * @param {String} title (optional) The title bar text
33635          * @return {Roo.MessageBox} This message box
33636          */
33637         wait : function(msg, title){
33638             this.show({
33639                 title : title,
33640                 msg : msg,
33641                 buttons: false,
33642                 closable:false,
33643                 progress:true,
33644                 modal:true,
33645                 width:300,
33646                 wait:true
33647             });
33648             waitTimer = Roo.TaskMgr.start({
33649                 run: function(i){
33650                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
33651                 },
33652                 interval: 1000
33653             });
33654             return this;
33655         },
33656
33657         /**
33658          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
33659          * If a callback function is passed it will be called after the user clicks either button, and the id of the
33660          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
33661          * @param {String} title The title bar text
33662          * @param {String} msg The message box body text
33663          * @param {Function} fn (optional) The callback function invoked after the message box is closed
33664          * @param {Object} scope (optional) The scope of the callback function
33665          * @return {Roo.MessageBox} This message box
33666          */
33667         confirm : function(title, msg, fn, scope){
33668             this.show({
33669                 title : title,
33670                 msg : msg,
33671                 buttons: this.YESNO,
33672                 fn: fn,
33673                 scope : scope,
33674                 modal : true
33675             });
33676             return this;
33677         },
33678
33679         /**
33680          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
33681          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
33682          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
33683          * (could also be the top-right close button) and the text that was entered will be passed as the two
33684          * parameters to the callback.
33685          * @param {String} title The title bar text
33686          * @param {String} msg The message box body text
33687          * @param {Function} fn (optional) The callback function invoked after the message box is closed
33688          * @param {Object} scope (optional) The scope of the callback function
33689          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
33690          * property, or the height in pixels to create the textbox (defaults to false / single-line)
33691          * @return {Roo.MessageBox} This message box
33692          */
33693         prompt : function(title, msg, fn, scope, multiline){
33694             this.show({
33695                 title : title,
33696                 msg : msg,
33697                 buttons: this.OKCANCEL,
33698                 fn: fn,
33699                 minWidth:250,
33700                 scope : scope,
33701                 prompt:true,
33702                 multiline: multiline,
33703                 modal : true
33704             });
33705             return this;
33706         },
33707
33708         /**
33709          * Button config that displays a single OK button
33710          * @type Object
33711          */
33712         OK : {ok:true},
33713         /**
33714          * Button config that displays Yes and No buttons
33715          * @type Object
33716          */
33717         YESNO : {yes:true, no:true},
33718         /**
33719          * Button config that displays OK and Cancel buttons
33720          * @type Object
33721          */
33722         OKCANCEL : {ok:true, cancel:true},
33723         /**
33724          * Button config that displays Yes, No and Cancel buttons
33725          * @type Object
33726          */
33727         YESNOCANCEL : {yes:true, no:true, cancel:true},
33728
33729         /**
33730          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
33731          * @type Number
33732          */
33733         defaultTextHeight : 75,
33734         /**
33735          * The maximum width in pixels of the message box (defaults to 600)
33736          * @type Number
33737          */
33738         maxWidth : 600,
33739         /**
33740          * The minimum width in pixels of the message box (defaults to 100)
33741          * @type Number
33742          */
33743         minWidth : 100,
33744         /**
33745          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
33746          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
33747          * @type Number
33748          */
33749         minProgressWidth : 250,
33750         /**
33751          * An object containing the default button text strings that can be overriden for localized language support.
33752          * Supported properties are: ok, cancel, yes and no.
33753          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
33754          * @type Object
33755          */
33756         buttonText : {
33757             ok : "OK",
33758             cancel : "Cancel",
33759             yes : "Yes",
33760             no : "No"
33761         }
33762     };
33763 }();
33764
33765 /**
33766  * Shorthand for {@link Roo.MessageBox}
33767  */
33768 Roo.Msg = Roo.MessageBox;/*
33769  * Based on:
33770  * Ext JS Library 1.1.1
33771  * Copyright(c) 2006-2007, Ext JS, LLC.
33772  *
33773  * Originally Released Under LGPL - original licence link has changed is not relivant.
33774  *
33775  * Fork - LGPL
33776  * <script type="text/javascript">
33777  */
33778 /**
33779  * @class Roo.QuickTips
33780  * Provides attractive and customizable tooltips for any element.
33781  * @singleton
33782  */
33783 Roo.QuickTips = function(){
33784     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
33785     var ce, bd, xy, dd;
33786     var visible = false, disabled = true, inited = false;
33787     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
33788     
33789     var onOver = function(e){
33790         if(disabled){
33791             return;
33792         }
33793         var t = e.getTarget();
33794         if(!t || t.nodeType !== 1 || t == document || t == document.body){
33795             return;
33796         }
33797         if(ce && t == ce.el){
33798             clearTimeout(hideProc);
33799             return;
33800         }
33801         if(t && tagEls[t.id]){
33802             tagEls[t.id].el = t;
33803             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
33804             return;
33805         }
33806         var ttp, et = Roo.fly(t);
33807         var ns = cfg.namespace;
33808         if(tm.interceptTitles && t.title){
33809             ttp = t.title;
33810             t.qtip = ttp;
33811             t.removeAttribute("title");
33812             e.preventDefault();
33813         }else{
33814             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
33815         }
33816         if(ttp){
33817             showProc = show.defer(tm.showDelay, tm, [{
33818                 el: t, 
33819                 text: ttp.replace(/\\n/g,'<br/>'),
33820                 width: et.getAttributeNS(ns, cfg.width),
33821                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
33822                 title: et.getAttributeNS(ns, cfg.title),
33823                     cls: et.getAttributeNS(ns, cfg.cls)
33824             }]);
33825         }
33826     };
33827     
33828     var onOut = function(e){
33829         clearTimeout(showProc);
33830         var t = e.getTarget();
33831         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
33832             hideProc = setTimeout(hide, tm.hideDelay);
33833         }
33834     };
33835     
33836     var onMove = function(e){
33837         if(disabled){
33838             return;
33839         }
33840         xy = e.getXY();
33841         xy[1] += 18;
33842         if(tm.trackMouse && ce){
33843             el.setXY(xy);
33844         }
33845     };
33846     
33847     var onDown = function(e){
33848         clearTimeout(showProc);
33849         clearTimeout(hideProc);
33850         if(!e.within(el)){
33851             if(tm.hideOnClick){
33852                 hide();
33853                 tm.disable();
33854                 tm.enable.defer(100, tm);
33855             }
33856         }
33857     };
33858     
33859     var getPad = function(){
33860         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
33861     };
33862
33863     var show = function(o){
33864         if(disabled){
33865             return;
33866         }
33867         clearTimeout(dismissProc);
33868         ce = o;
33869         if(removeCls){ // in case manually hidden
33870             el.removeClass(removeCls);
33871             removeCls = null;
33872         }
33873         if(ce.cls){
33874             el.addClass(ce.cls);
33875             removeCls = ce.cls;
33876         }
33877         if(ce.title){
33878             tipTitle.update(ce.title);
33879             tipTitle.show();
33880         }else{
33881             tipTitle.update('');
33882             tipTitle.hide();
33883         }
33884         el.dom.style.width  = tm.maxWidth+'px';
33885         //tipBody.dom.style.width = '';
33886         tipBodyText.update(o.text);
33887         var p = getPad(), w = ce.width;
33888         if(!w){
33889             var td = tipBodyText.dom;
33890             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
33891             if(aw > tm.maxWidth){
33892                 w = tm.maxWidth;
33893             }else if(aw < tm.minWidth){
33894                 w = tm.minWidth;
33895             }else{
33896                 w = aw;
33897             }
33898         }
33899         //tipBody.setWidth(w);
33900         el.setWidth(parseInt(w, 10) + p);
33901         if(ce.autoHide === false){
33902             close.setDisplayed(true);
33903             if(dd){
33904                 dd.unlock();
33905             }
33906         }else{
33907             close.setDisplayed(false);
33908             if(dd){
33909                 dd.lock();
33910             }
33911         }
33912         if(xy){
33913             el.avoidY = xy[1]-18;
33914             el.setXY(xy);
33915         }
33916         if(tm.animate){
33917             el.setOpacity(.1);
33918             el.setStyle("visibility", "visible");
33919             el.fadeIn({callback: afterShow});
33920         }else{
33921             afterShow();
33922         }
33923     };
33924     
33925     var afterShow = function(){
33926         if(ce){
33927             el.show();
33928             esc.enable();
33929             if(tm.autoDismiss && ce.autoHide !== false){
33930                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
33931             }
33932         }
33933     };
33934     
33935     var hide = function(noanim){
33936         clearTimeout(dismissProc);
33937         clearTimeout(hideProc);
33938         ce = null;
33939         if(el.isVisible()){
33940             esc.disable();
33941             if(noanim !== true && tm.animate){
33942                 el.fadeOut({callback: afterHide});
33943             }else{
33944                 afterHide();
33945             } 
33946         }
33947     };
33948     
33949     var afterHide = function(){
33950         el.hide();
33951         if(removeCls){
33952             el.removeClass(removeCls);
33953             removeCls = null;
33954         }
33955     };
33956     
33957     return {
33958         /**
33959         * @cfg {Number} minWidth
33960         * The minimum width of the quick tip (defaults to 40)
33961         */
33962        minWidth : 40,
33963         /**
33964         * @cfg {Number} maxWidth
33965         * The maximum width of the quick tip (defaults to 300)
33966         */
33967        maxWidth : 300,
33968         /**
33969         * @cfg {Boolean} interceptTitles
33970         * True to automatically use the element's DOM title value if available (defaults to false)
33971         */
33972        interceptTitles : false,
33973         /**
33974         * @cfg {Boolean} trackMouse
33975         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
33976         */
33977        trackMouse : false,
33978         /**
33979         * @cfg {Boolean} hideOnClick
33980         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
33981         */
33982        hideOnClick : true,
33983         /**
33984         * @cfg {Number} showDelay
33985         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
33986         */
33987        showDelay : 500,
33988         /**
33989         * @cfg {Number} hideDelay
33990         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
33991         */
33992        hideDelay : 200,
33993         /**
33994         * @cfg {Boolean} autoHide
33995         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
33996         * Used in conjunction with hideDelay.
33997         */
33998        autoHide : true,
33999         /**
34000         * @cfg {Boolean}
34001         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
34002         * (defaults to true).  Used in conjunction with autoDismissDelay.
34003         */
34004        autoDismiss : true,
34005         /**
34006         * @cfg {Number}
34007         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
34008         */
34009        autoDismissDelay : 5000,
34010        /**
34011         * @cfg {Boolean} animate
34012         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
34013         */
34014        animate : false,
34015
34016        /**
34017         * @cfg {String} title
34018         * Title text to display (defaults to '').  This can be any valid HTML markup.
34019         */
34020         title: '',
34021        /**
34022         * @cfg {String} text
34023         * Body text to display (defaults to '').  This can be any valid HTML markup.
34024         */
34025         text : '',
34026        /**
34027         * @cfg {String} cls
34028         * A CSS class to apply to the base quick tip element (defaults to '').
34029         */
34030         cls : '',
34031        /**
34032         * @cfg {Number} width
34033         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
34034         * minWidth or maxWidth.
34035         */
34036         width : null,
34037
34038     /**
34039      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
34040      * or display QuickTips in a page.
34041      */
34042        init : function(){
34043           tm = Roo.QuickTips;
34044           cfg = tm.tagConfig;
34045           if(!inited){
34046               if(!Roo.isReady){ // allow calling of init() before onReady
34047                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
34048                   return;
34049               }
34050               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
34051               el.fxDefaults = {stopFx: true};
34052               // maximum custom styling
34053               //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>');
34054               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>');              
34055               tipTitle = el.child('h3');
34056               tipTitle.enableDisplayMode("block");
34057               tipBody = el.child('div.x-tip-bd');
34058               tipBodyText = el.child('div.x-tip-bd-inner');
34059               //bdLeft = el.child('div.x-tip-bd-left');
34060               //bdRight = el.child('div.x-tip-bd-right');
34061               close = el.child('div.x-tip-close');
34062               close.enableDisplayMode("block");
34063               close.on("click", hide);
34064               var d = Roo.get(document);
34065               d.on("mousedown", onDown);
34066               d.on("mouseover", onOver);
34067               d.on("mouseout", onOut);
34068               d.on("mousemove", onMove);
34069               esc = d.addKeyListener(27, hide);
34070               esc.disable();
34071               if(Roo.dd.DD){
34072                   dd = el.initDD("default", null, {
34073                       onDrag : function(){
34074                           el.sync();  
34075                       }
34076                   });
34077                   dd.setHandleElId(tipTitle.id);
34078                   dd.lock();
34079               }
34080               inited = true;
34081           }
34082           this.enable(); 
34083        },
34084
34085     /**
34086      * Configures a new quick tip instance and assigns it to a target element.  The following config options
34087      * are supported:
34088      * <pre>
34089 Property    Type                   Description
34090 ----------  ---------------------  ------------------------------------------------------------------------
34091 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
34092      * </ul>
34093      * @param {Object} config The config object
34094      */
34095        register : function(config){
34096            var cs = config instanceof Array ? config : arguments;
34097            for(var i = 0, len = cs.length; i < len; i++) {
34098                var c = cs[i];
34099                var target = c.target;
34100                if(target){
34101                    if(target instanceof Array){
34102                        for(var j = 0, jlen = target.length; j < jlen; j++){
34103                            tagEls[target[j]] = c;
34104                        }
34105                    }else{
34106                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
34107                    }
34108                }
34109            }
34110        },
34111
34112     /**
34113      * Removes this quick tip from its element and destroys it.
34114      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
34115      */
34116        unregister : function(el){
34117            delete tagEls[Roo.id(el)];
34118        },
34119
34120     /**
34121      * Enable this quick tip.
34122      */
34123        enable : function(){
34124            if(inited && disabled){
34125                locks.pop();
34126                if(locks.length < 1){
34127                    disabled = false;
34128                }
34129            }
34130        },
34131
34132     /**
34133      * Disable this quick tip.
34134      */
34135        disable : function(){
34136           disabled = true;
34137           clearTimeout(showProc);
34138           clearTimeout(hideProc);
34139           clearTimeout(dismissProc);
34140           if(ce){
34141               hide(true);
34142           }
34143           locks.push(1);
34144        },
34145
34146     /**
34147      * Returns true if the quick tip is enabled, else false.
34148      */
34149        isEnabled : function(){
34150             return !disabled;
34151        },
34152
34153         // private
34154        tagConfig : {
34155            namespace : "roo", // was ext?? this may break..
34156            alt_namespace : "ext",
34157            attribute : "qtip",
34158            width : "width",
34159            target : "target",
34160            title : "qtitle",
34161            hide : "hide",
34162            cls : "qclass"
34163        }
34164    };
34165 }();
34166
34167 // backwards compat
34168 Roo.QuickTips.tips = Roo.QuickTips.register;/*
34169  * Based on:
34170  * Ext JS Library 1.1.1
34171  * Copyright(c) 2006-2007, Ext JS, LLC.
34172  *
34173  * Originally Released Under LGPL - original licence link has changed is not relivant.
34174  *
34175  * Fork - LGPL
34176  * <script type="text/javascript">
34177  */
34178  
34179
34180 /**
34181  * @class Roo.tree.TreePanel
34182  * @extends Roo.data.Tree
34183
34184  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
34185  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
34186  * @cfg {Boolean} enableDD true to enable drag and drop
34187  * @cfg {Boolean} enableDrag true to enable just drag
34188  * @cfg {Boolean} enableDrop true to enable just drop
34189  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
34190  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
34191  * @cfg {String} ddGroup The DD group this TreePanel belongs to
34192  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
34193  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
34194  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
34195  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
34196  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
34197  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
34198  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
34199  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
34200  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
34201  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
34202  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
34203  * @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>
34204  * @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>
34205  * 
34206  * @constructor
34207  * @param {String/HTMLElement/Element} el The container element
34208  * @param {Object} config
34209  */
34210 Roo.tree.TreePanel = function(el, config){
34211     var root = false;
34212     var loader = false;
34213     if (config.root) {
34214         root = config.root;
34215         delete config.root;
34216     }
34217     if (config.loader) {
34218         loader = config.loader;
34219         delete config.loader;
34220     }
34221     
34222     Roo.apply(this, config);
34223     Roo.tree.TreePanel.superclass.constructor.call(this);
34224     this.el = Roo.get(el);
34225     this.el.addClass('x-tree');
34226     //console.log(root);
34227     if (root) {
34228         this.setRootNode( Roo.factory(root, Roo.tree));
34229     }
34230     if (loader) {
34231         this.loader = Roo.factory(loader, Roo.tree);
34232     }
34233    /**
34234     * Read-only. The id of the container element becomes this TreePanel's id.
34235     */
34236     this.id = this.el.id;
34237     this.addEvents({
34238         /**
34239         * @event beforeload
34240         * Fires before a node is loaded, return false to cancel
34241         * @param {Node} node The node being loaded
34242         */
34243         "beforeload" : true,
34244         /**
34245         * @event load
34246         * Fires when a node is loaded
34247         * @param {Node} node The node that was loaded
34248         */
34249         "load" : true,
34250         /**
34251         * @event textchange
34252         * Fires when the text for a node is changed
34253         * @param {Node} node The node
34254         * @param {String} text The new text
34255         * @param {String} oldText The old text
34256         */
34257         "textchange" : true,
34258         /**
34259         * @event beforeexpand
34260         * Fires before a node is expanded, return false to cancel.
34261         * @param {Node} node The node
34262         * @param {Boolean} deep
34263         * @param {Boolean} anim
34264         */
34265         "beforeexpand" : true,
34266         /**
34267         * @event beforecollapse
34268         * Fires before a node is collapsed, return false to cancel.
34269         * @param {Node} node The node
34270         * @param {Boolean} deep
34271         * @param {Boolean} anim
34272         */
34273         "beforecollapse" : true,
34274         /**
34275         * @event expand
34276         * Fires when a node is expanded
34277         * @param {Node} node The node
34278         */
34279         "expand" : true,
34280         /**
34281         * @event disabledchange
34282         * Fires when the disabled status of a node changes
34283         * @param {Node} node The node
34284         * @param {Boolean} disabled
34285         */
34286         "disabledchange" : true,
34287         /**
34288         * @event collapse
34289         * Fires when a node is collapsed
34290         * @param {Node} node The node
34291         */
34292         "collapse" : true,
34293         /**
34294         * @event beforeclick
34295         * Fires before click processing on a node. Return false to cancel the default action.
34296         * @param {Node} node The node
34297         * @param {Roo.EventObject} e The event object
34298         */
34299         "beforeclick":true,
34300         /**
34301         * @event checkchange
34302         * Fires when a node with a checkbox's checked property changes
34303         * @param {Node} this This node
34304         * @param {Boolean} checked
34305         */
34306         "checkchange":true,
34307         /**
34308         * @event click
34309         * Fires when a node is clicked
34310         * @param {Node} node The node
34311         * @param {Roo.EventObject} e The event object
34312         */
34313         "click":true,
34314         /**
34315         * @event dblclick
34316         * Fires when a node is double clicked
34317         * @param {Node} node The node
34318         * @param {Roo.EventObject} e The event object
34319         */
34320         "dblclick":true,
34321         /**
34322         * @event contextmenu
34323         * Fires when a node is right clicked
34324         * @param {Node} node The node
34325         * @param {Roo.EventObject} e The event object
34326         */
34327         "contextmenu":true,
34328         /**
34329         * @event beforechildrenrendered
34330         * Fires right before the child nodes for a node are rendered
34331         * @param {Node} node The node
34332         */
34333         "beforechildrenrendered":true,
34334         /**
34335         * @event startdrag
34336         * Fires when a node starts being dragged
34337         * @param {Roo.tree.TreePanel} this
34338         * @param {Roo.tree.TreeNode} node
34339         * @param {event} e The raw browser event
34340         */ 
34341        "startdrag" : true,
34342        /**
34343         * @event enddrag
34344         * Fires when a drag operation is complete
34345         * @param {Roo.tree.TreePanel} this
34346         * @param {Roo.tree.TreeNode} node
34347         * @param {event} e The raw browser event
34348         */
34349        "enddrag" : true,
34350        /**
34351         * @event dragdrop
34352         * Fires when a dragged node is dropped on a valid DD target
34353         * @param {Roo.tree.TreePanel} this
34354         * @param {Roo.tree.TreeNode} node
34355         * @param {DD} dd The dd it was dropped on
34356         * @param {event} e The raw browser event
34357         */
34358        "dragdrop" : true,
34359        /**
34360         * @event beforenodedrop
34361         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
34362         * passed to handlers has the following properties:<br />
34363         * <ul style="padding:5px;padding-left:16px;">
34364         * <li>tree - The TreePanel</li>
34365         * <li>target - The node being targeted for the drop</li>
34366         * <li>data - The drag data from the drag source</li>
34367         * <li>point - The point of the drop - append, above or below</li>
34368         * <li>source - The drag source</li>
34369         * <li>rawEvent - Raw mouse event</li>
34370         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
34371         * to be inserted by setting them on this object.</li>
34372         * <li>cancel - Set this to true to cancel the drop.</li>
34373         * </ul>
34374         * @param {Object} dropEvent
34375         */
34376        "beforenodedrop" : true,
34377        /**
34378         * @event nodedrop
34379         * Fires after a DD object is dropped on a node in this tree. The dropEvent
34380         * passed to handlers has the following properties:<br />
34381         * <ul style="padding:5px;padding-left:16px;">
34382         * <li>tree - The TreePanel</li>
34383         * <li>target - The node being targeted for the drop</li>
34384         * <li>data - The drag data from the drag source</li>
34385         * <li>point - The point of the drop - append, above or below</li>
34386         * <li>source - The drag source</li>
34387         * <li>rawEvent - Raw mouse event</li>
34388         * <li>dropNode - Dropped node(s).</li>
34389         * </ul>
34390         * @param {Object} dropEvent
34391         */
34392        "nodedrop" : true,
34393         /**
34394         * @event nodedragover
34395         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
34396         * passed to handlers has the following properties:<br />
34397         * <ul style="padding:5px;padding-left:16px;">
34398         * <li>tree - The TreePanel</li>
34399         * <li>target - The node being targeted for the drop</li>
34400         * <li>data - The drag data from the drag source</li>
34401         * <li>point - The point of the drop - append, above or below</li>
34402         * <li>source - The drag source</li>
34403         * <li>rawEvent - Raw mouse event</li>
34404         * <li>dropNode - Drop node(s) provided by the source.</li>
34405         * <li>cancel - Set this to true to signal drop not allowed.</li>
34406         * </ul>
34407         * @param {Object} dragOverEvent
34408         */
34409        "nodedragover" : true,
34410        /**
34411         * @event appendnode
34412         * Fires when append node to the tree
34413         * @param {Roo.tree.TreePanel} this
34414         * @param {Roo.tree.TreeNode} node
34415         * @param {Number} index The index of the newly appended node
34416         */
34417        "appendnode" : true
34418         
34419     });
34420     if(this.singleExpand){
34421        this.on("beforeexpand", this.restrictExpand, this);
34422     }
34423     if (this.editor) {
34424         this.editor.tree = this;
34425         this.editor = Roo.factory(this.editor, Roo.tree);
34426     }
34427     
34428     if (this.selModel) {
34429         this.selModel = Roo.factory(this.selModel, Roo.tree);
34430     }
34431    
34432 };
34433 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
34434     rootVisible : true,
34435     animate: Roo.enableFx,
34436     lines : true,
34437     enableDD : false,
34438     hlDrop : Roo.enableFx,
34439   
34440     renderer: false,
34441     
34442     rendererTip: false,
34443     // private
34444     restrictExpand : function(node){
34445         var p = node.parentNode;
34446         if(p){
34447             if(p.expandedChild && p.expandedChild.parentNode == p){
34448                 p.expandedChild.collapse();
34449             }
34450             p.expandedChild = node;
34451         }
34452     },
34453
34454     // private override
34455     setRootNode : function(node){
34456         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
34457         if(!this.rootVisible){
34458             node.ui = new Roo.tree.RootTreeNodeUI(node);
34459         }
34460         return node;
34461     },
34462
34463     /**
34464      * Returns the container element for this TreePanel
34465      */
34466     getEl : function(){
34467         return this.el;
34468     },
34469
34470     /**
34471      * Returns the default TreeLoader for this TreePanel
34472      */
34473     getLoader : function(){
34474         return this.loader;
34475     },
34476
34477     /**
34478      * Expand all nodes
34479      */
34480     expandAll : function(){
34481         this.root.expand(true);
34482     },
34483
34484     /**
34485      * Collapse all nodes
34486      */
34487     collapseAll : function(){
34488         this.root.collapse(true);
34489     },
34490
34491     /**
34492      * Returns the selection model used by this TreePanel
34493      */
34494     getSelectionModel : function(){
34495         if(!this.selModel){
34496             this.selModel = new Roo.tree.DefaultSelectionModel();
34497         }
34498         return this.selModel;
34499     },
34500
34501     /**
34502      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
34503      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
34504      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
34505      * @return {Array}
34506      */
34507     getChecked : function(a, startNode){
34508         startNode = startNode || this.root;
34509         var r = [];
34510         var f = function(){
34511             if(this.attributes.checked){
34512                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
34513             }
34514         }
34515         startNode.cascade(f);
34516         return r;
34517     },
34518
34519     /**
34520      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
34521      * @param {String} path
34522      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
34523      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
34524      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
34525      */
34526     expandPath : function(path, attr, callback){
34527         attr = attr || "id";
34528         var keys = path.split(this.pathSeparator);
34529         var curNode = this.root;
34530         if(curNode.attributes[attr] != keys[1]){ // invalid root
34531             if(callback){
34532                 callback(false, null);
34533             }
34534             return;
34535         }
34536         var index = 1;
34537         var f = function(){
34538             if(++index == keys.length){
34539                 if(callback){
34540                     callback(true, curNode);
34541                 }
34542                 return;
34543             }
34544             var c = curNode.findChild(attr, keys[index]);
34545             if(!c){
34546                 if(callback){
34547                     callback(false, curNode);
34548                 }
34549                 return;
34550             }
34551             curNode = c;
34552             c.expand(false, false, f);
34553         };
34554         curNode.expand(false, false, f);
34555     },
34556
34557     /**
34558      * Selects the node in this tree at the specified path. 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 selection is complete. The callback will be called with
34562      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
34563      */
34564     selectPath : function(path, attr, callback){
34565         attr = attr || "id";
34566         var keys = path.split(this.pathSeparator);
34567         var v = keys.pop();
34568         if(keys.length > 0){
34569             var f = function(success, node){
34570                 if(success && node){
34571                     var n = node.findChild(attr, v);
34572                     if(n){
34573                         n.select();
34574                         if(callback){
34575                             callback(true, n);
34576                         }
34577                     }else if(callback){
34578                         callback(false, n);
34579                     }
34580                 }else{
34581                     if(callback){
34582                         callback(false, n);
34583                     }
34584                 }
34585             };
34586             this.expandPath(keys.join(this.pathSeparator), attr, f);
34587         }else{
34588             this.root.select();
34589             if(callback){
34590                 callback(true, this.root);
34591             }
34592         }
34593     },
34594
34595     getTreeEl : function(){
34596         return this.el;
34597     },
34598
34599     /**
34600      * Trigger rendering of this TreePanel
34601      */
34602     render : function(){
34603         if (this.innerCt) {
34604             return this; // stop it rendering more than once!!
34605         }
34606         
34607         this.innerCt = this.el.createChild({tag:"ul",
34608                cls:"x-tree-root-ct " +
34609                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
34610
34611         if(this.containerScroll){
34612             Roo.dd.ScrollManager.register(this.el);
34613         }
34614         if((this.enableDD || this.enableDrop) && !this.dropZone){
34615            /**
34616             * The dropZone used by this tree if drop is enabled
34617             * @type Roo.tree.TreeDropZone
34618             */
34619              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
34620                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
34621            });
34622         }
34623         if((this.enableDD || this.enableDrag) && !this.dragZone){
34624            /**
34625             * The dragZone used by this tree if drag is enabled
34626             * @type Roo.tree.TreeDragZone
34627             */
34628             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
34629                ddGroup: this.ddGroup || "TreeDD",
34630                scroll: this.ddScroll
34631            });
34632         }
34633         this.getSelectionModel().init(this);
34634         if (!this.root) {
34635             Roo.log("ROOT not set in tree");
34636             return this;
34637         }
34638         this.root.render();
34639         if(!this.rootVisible){
34640             this.root.renderChildren();
34641         }
34642         return this;
34643     }
34644 });/*
34645  * Based on:
34646  * Ext JS Library 1.1.1
34647  * Copyright(c) 2006-2007, Ext JS, LLC.
34648  *
34649  * Originally Released Under LGPL - original licence link has changed is not relivant.
34650  *
34651  * Fork - LGPL
34652  * <script type="text/javascript">
34653  */
34654  
34655
34656 /**
34657  * @class Roo.tree.DefaultSelectionModel
34658  * @extends Roo.util.Observable
34659  * The default single selection for a TreePanel.
34660  * @param {Object} cfg Configuration
34661  */
34662 Roo.tree.DefaultSelectionModel = function(cfg){
34663    this.selNode = null;
34664    
34665    
34666    
34667    this.addEvents({
34668        /**
34669         * @event selectionchange
34670         * Fires when the selected node changes
34671         * @param {DefaultSelectionModel} this
34672         * @param {TreeNode} node the new selection
34673         */
34674        "selectionchange" : true,
34675
34676        /**
34677         * @event beforeselect
34678         * Fires before the selected node changes, return false to cancel the change
34679         * @param {DefaultSelectionModel} this
34680         * @param {TreeNode} node the new selection
34681         * @param {TreeNode} node the old selection
34682         */
34683        "beforeselect" : true
34684    });
34685    
34686     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
34687 };
34688
34689 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
34690     init : function(tree){
34691         this.tree = tree;
34692         tree.getTreeEl().on("keydown", this.onKeyDown, this);
34693         tree.on("click", this.onNodeClick, this);
34694     },
34695     
34696     onNodeClick : function(node, e){
34697         if (e.ctrlKey && this.selNode == node)  {
34698             this.unselect(node);
34699             return;
34700         }
34701         this.select(node);
34702     },
34703     
34704     /**
34705      * Select a node.
34706      * @param {TreeNode} node The node to select
34707      * @return {TreeNode} The selected node
34708      */
34709     select : function(node){
34710         var last = this.selNode;
34711         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
34712             if(last){
34713                 last.ui.onSelectedChange(false);
34714             }
34715             this.selNode = node;
34716             node.ui.onSelectedChange(true);
34717             this.fireEvent("selectionchange", this, node, last);
34718         }
34719         return node;
34720     },
34721     
34722     /**
34723      * Deselect a node.
34724      * @param {TreeNode} node The node to unselect
34725      */
34726     unselect : function(node){
34727         if(this.selNode == node){
34728             this.clearSelections();
34729         }    
34730     },
34731     
34732     /**
34733      * Clear all selections
34734      */
34735     clearSelections : function(){
34736         var n = this.selNode;
34737         if(n){
34738             n.ui.onSelectedChange(false);
34739             this.selNode = null;
34740             this.fireEvent("selectionchange", this, null);
34741         }
34742         return n;
34743     },
34744     
34745     /**
34746      * Get the selected node
34747      * @return {TreeNode} The selected node
34748      */
34749     getSelectedNode : function(){
34750         return this.selNode;    
34751     },
34752     
34753     /**
34754      * Returns true if the node is selected
34755      * @param {TreeNode} node The node to check
34756      * @return {Boolean}
34757      */
34758     isSelected : function(node){
34759         return this.selNode == node;  
34760     },
34761
34762     /**
34763      * Selects the node above the selected node in the tree, intelligently walking the nodes
34764      * @return TreeNode The new selection
34765      */
34766     selectPrevious : function(){
34767         var s = this.selNode || this.lastSelNode;
34768         if(!s){
34769             return null;
34770         }
34771         var ps = s.previousSibling;
34772         if(ps){
34773             if(!ps.isExpanded() || ps.childNodes.length < 1){
34774                 return this.select(ps);
34775             } else{
34776                 var lc = ps.lastChild;
34777                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
34778                     lc = lc.lastChild;
34779                 }
34780                 return this.select(lc);
34781             }
34782         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
34783             return this.select(s.parentNode);
34784         }
34785         return null;
34786     },
34787
34788     /**
34789      * Selects the node above the selected node in the tree, intelligently walking the nodes
34790      * @return TreeNode The new selection
34791      */
34792     selectNext : function(){
34793         var s = this.selNode || this.lastSelNode;
34794         if(!s){
34795             return null;
34796         }
34797         if(s.firstChild && s.isExpanded()){
34798              return this.select(s.firstChild);
34799          }else if(s.nextSibling){
34800              return this.select(s.nextSibling);
34801          }else if(s.parentNode){
34802             var newS = null;
34803             s.parentNode.bubble(function(){
34804                 if(this.nextSibling){
34805                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
34806                     return false;
34807                 }
34808             });
34809             return newS;
34810          }
34811         return null;
34812     },
34813
34814     onKeyDown : function(e){
34815         var s = this.selNode || this.lastSelNode;
34816         // undesirable, but required
34817         var sm = this;
34818         if(!s){
34819             return;
34820         }
34821         var k = e.getKey();
34822         switch(k){
34823              case e.DOWN:
34824                  e.stopEvent();
34825                  this.selectNext();
34826              break;
34827              case e.UP:
34828                  e.stopEvent();
34829                  this.selectPrevious();
34830              break;
34831              case e.RIGHT:
34832                  e.preventDefault();
34833                  if(s.hasChildNodes()){
34834                      if(!s.isExpanded()){
34835                          s.expand();
34836                      }else if(s.firstChild){
34837                          this.select(s.firstChild, e);
34838                      }
34839                  }
34840              break;
34841              case e.LEFT:
34842                  e.preventDefault();
34843                  if(s.hasChildNodes() && s.isExpanded()){
34844                      s.collapse();
34845                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
34846                      this.select(s.parentNode, e);
34847                  }
34848              break;
34849         };
34850     }
34851 });
34852
34853 /**
34854  * @class Roo.tree.MultiSelectionModel
34855  * @extends Roo.util.Observable
34856  * Multi selection for a TreePanel.
34857  * @param {Object} cfg Configuration
34858  */
34859 Roo.tree.MultiSelectionModel = function(){
34860    this.selNodes = [];
34861    this.selMap = {};
34862    this.addEvents({
34863        /**
34864         * @event selectionchange
34865         * Fires when the selected nodes change
34866         * @param {MultiSelectionModel} this
34867         * @param {Array} nodes Array of the selected nodes
34868         */
34869        "selectionchange" : true
34870    });
34871    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
34872    
34873 };
34874
34875 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
34876     init : function(tree){
34877         this.tree = tree;
34878         tree.getTreeEl().on("keydown", this.onKeyDown, this);
34879         tree.on("click", this.onNodeClick, this);
34880     },
34881     
34882     onNodeClick : function(node, e){
34883         this.select(node, e, e.ctrlKey);
34884     },
34885     
34886     /**
34887      * Select a node.
34888      * @param {TreeNode} node The node to select
34889      * @param {EventObject} e (optional) An event associated with the selection
34890      * @param {Boolean} keepExisting True to retain existing selections
34891      * @return {TreeNode} The selected node
34892      */
34893     select : function(node, e, keepExisting){
34894         if(keepExisting !== true){
34895             this.clearSelections(true);
34896         }
34897         if(this.isSelected(node)){
34898             this.lastSelNode = node;
34899             return node;
34900         }
34901         this.selNodes.push(node);
34902         this.selMap[node.id] = node;
34903         this.lastSelNode = node;
34904         node.ui.onSelectedChange(true);
34905         this.fireEvent("selectionchange", this, this.selNodes);
34906         return node;
34907     },
34908     
34909     /**
34910      * Deselect a node.
34911      * @param {TreeNode} node The node to unselect
34912      */
34913     unselect : function(node){
34914         if(this.selMap[node.id]){
34915             node.ui.onSelectedChange(false);
34916             var sn = this.selNodes;
34917             var index = -1;
34918             if(sn.indexOf){
34919                 index = sn.indexOf(node);
34920             }else{
34921                 for(var i = 0, len = sn.length; i < len; i++){
34922                     if(sn[i] == node){
34923                         index = i;
34924                         break;
34925                     }
34926                 }
34927             }
34928             if(index != -1){
34929                 this.selNodes.splice(index, 1);
34930             }
34931             delete this.selMap[node.id];
34932             this.fireEvent("selectionchange", this, this.selNodes);
34933         }
34934     },
34935     
34936     /**
34937      * Clear all selections
34938      */
34939     clearSelections : function(suppressEvent){
34940         var sn = this.selNodes;
34941         if(sn.length > 0){
34942             for(var i = 0, len = sn.length; i < len; i++){
34943                 sn[i].ui.onSelectedChange(false);
34944             }
34945             this.selNodes = [];
34946             this.selMap = {};
34947             if(suppressEvent !== true){
34948                 this.fireEvent("selectionchange", this, this.selNodes);
34949             }
34950         }
34951     },
34952     
34953     /**
34954      * Returns true if the node is selected
34955      * @param {TreeNode} node The node to check
34956      * @return {Boolean}
34957      */
34958     isSelected : function(node){
34959         return this.selMap[node.id] ? true : false;  
34960     },
34961     
34962     /**
34963      * Returns an array of the selected nodes
34964      * @return {Array}
34965      */
34966     getSelectedNodes : function(){
34967         return this.selNodes;    
34968     },
34969
34970     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
34971
34972     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
34973
34974     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
34975 });/*
34976  * Based on:
34977  * Ext JS Library 1.1.1
34978  * Copyright(c) 2006-2007, Ext JS, LLC.
34979  *
34980  * Originally Released Under LGPL - original licence link has changed is not relivant.
34981  *
34982  * Fork - LGPL
34983  * <script type="text/javascript">
34984  */
34985  
34986 /**
34987  * @class Roo.tree.TreeNode
34988  * @extends Roo.data.Node
34989  * @cfg {String} text The text for this node
34990  * @cfg {Boolean} expanded true to start the node expanded
34991  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
34992  * @cfg {Boolean} allowDrop false if this node cannot be drop on
34993  * @cfg {Boolean} disabled true to start the node disabled
34994  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
34995  *    is to use the cls or iconCls attributes and add the icon via a CSS background image.
34996  * @cfg {String} cls A css class to be added to the node
34997  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
34998  * @cfg {String} href URL of the link used for the node (defaults to #)
34999  * @cfg {String} hrefTarget target frame for the link
35000  * @cfg {String} qtip An Ext QuickTip for the node
35001  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
35002  * @cfg {Boolean} singleClickExpand True for single click expand on this node
35003  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
35004  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
35005  * (defaults to undefined with no checkbox rendered)
35006  * @constructor
35007  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
35008  */
35009 Roo.tree.TreeNode = function(attributes){
35010     attributes = attributes || {};
35011     if(typeof attributes == "string"){
35012         attributes = {text: attributes};
35013     }
35014     this.childrenRendered = false;
35015     this.rendered = false;
35016     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
35017     this.expanded = attributes.expanded === true;
35018     this.isTarget = attributes.isTarget !== false;
35019     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
35020     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
35021
35022     /**
35023      * Read-only. The text for this node. To change it use setText().
35024      * @type String
35025      */
35026     this.text = attributes.text;
35027     /**
35028      * True if this node is disabled.
35029      * @type Boolean
35030      */
35031     this.disabled = attributes.disabled === true;
35032
35033     this.addEvents({
35034         /**
35035         * @event textchange
35036         * Fires when the text for this node is changed
35037         * @param {Node} this This node
35038         * @param {String} text The new text
35039         * @param {String} oldText The old text
35040         */
35041         "textchange" : true,
35042         /**
35043         * @event beforeexpand
35044         * Fires before this node is expanded, return false to cancel.
35045         * @param {Node} this This node
35046         * @param {Boolean} deep
35047         * @param {Boolean} anim
35048         */
35049         "beforeexpand" : true,
35050         /**
35051         * @event beforecollapse
35052         * Fires before this node is collapsed, return false to cancel.
35053         * @param {Node} this This node
35054         * @param {Boolean} deep
35055         * @param {Boolean} anim
35056         */
35057         "beforecollapse" : true,
35058         /**
35059         * @event expand
35060         * Fires when this node is expanded
35061         * @param {Node} this This node
35062         */
35063         "expand" : true,
35064         /**
35065         * @event disabledchange
35066         * Fires when the disabled status of this node changes
35067         * @param {Node} this This node
35068         * @param {Boolean} disabled
35069         */
35070         "disabledchange" : true,
35071         /**
35072         * @event collapse
35073         * Fires when this node is collapsed
35074         * @param {Node} this This node
35075         */
35076         "collapse" : true,
35077         /**
35078         * @event beforeclick
35079         * Fires before click processing. Return false to cancel the default action.
35080         * @param {Node} this This node
35081         * @param {Roo.EventObject} e The event object
35082         */
35083         "beforeclick":true,
35084         /**
35085         * @event checkchange
35086         * Fires when a node with a checkbox's checked property changes
35087         * @param {Node} this This node
35088         * @param {Boolean} checked
35089         */
35090         "checkchange":true,
35091         /**
35092         * @event click
35093         * Fires when this node is clicked
35094         * @param {Node} this This node
35095         * @param {Roo.EventObject} e The event object
35096         */
35097         "click":true,
35098         /**
35099         * @event dblclick
35100         * Fires when this node is double clicked
35101         * @param {Node} this This node
35102         * @param {Roo.EventObject} e The event object
35103         */
35104         "dblclick":true,
35105         /**
35106         * @event contextmenu
35107         * Fires when this node is right clicked
35108         * @param {Node} this This node
35109         * @param {Roo.EventObject} e The event object
35110         */
35111         "contextmenu":true,
35112         /**
35113         * @event beforechildrenrendered
35114         * Fires right before the child nodes for this node are rendered
35115         * @param {Node} this This node
35116         */
35117         "beforechildrenrendered":true
35118     });
35119
35120     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
35121
35122     /**
35123      * Read-only. The UI for this node
35124      * @type TreeNodeUI
35125      */
35126     this.ui = new uiClass(this);
35127     
35128     // finally support items[]
35129     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
35130         return;
35131     }
35132     
35133     
35134     Roo.each(this.attributes.items, function(c) {
35135         this.appendChild(Roo.factory(c,Roo.Tree));
35136     }, this);
35137     delete this.attributes.items;
35138     
35139     
35140     
35141 };
35142 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
35143     preventHScroll: true,
35144     /**
35145      * Returns true if this node is expanded
35146      * @return {Boolean}
35147      */
35148     isExpanded : function(){
35149         return this.expanded;
35150     },
35151
35152     /**
35153      * Returns the UI object for this node
35154      * @return {TreeNodeUI}
35155      */
35156     getUI : function(){
35157         return this.ui;
35158     },
35159
35160     // private override
35161     setFirstChild : function(node){
35162         var of = this.firstChild;
35163         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
35164         if(this.childrenRendered && of && node != of){
35165             of.renderIndent(true, true);
35166         }
35167         if(this.rendered){
35168             this.renderIndent(true, true);
35169         }
35170     },
35171
35172     // private override
35173     setLastChild : function(node){
35174         var ol = this.lastChild;
35175         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
35176         if(this.childrenRendered && ol && node != ol){
35177             ol.renderIndent(true, true);
35178         }
35179         if(this.rendered){
35180             this.renderIndent(true, true);
35181         }
35182     },
35183
35184     // these methods are overridden to provide lazy rendering support
35185     // private override
35186     appendChild : function()
35187     {
35188         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
35189         if(node && this.childrenRendered){
35190             node.render();
35191         }
35192         this.ui.updateExpandIcon();
35193         return node;
35194     },
35195
35196     // private override
35197     removeChild : function(node){
35198         this.ownerTree.getSelectionModel().unselect(node);
35199         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
35200         // if it's been rendered remove dom node
35201         if(this.childrenRendered){
35202             node.ui.remove();
35203         }
35204         if(this.childNodes.length < 1){
35205             this.collapse(false, false);
35206         }else{
35207             this.ui.updateExpandIcon();
35208         }
35209         if(!this.firstChild) {
35210             this.childrenRendered = false;
35211         }
35212         return node;
35213     },
35214
35215     // private override
35216     insertBefore : function(node, refNode){
35217         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
35218         if(newNode && refNode && this.childrenRendered){
35219             node.render();
35220         }
35221         this.ui.updateExpandIcon();
35222         return newNode;
35223     },
35224
35225     /**
35226      * Sets the text for this node
35227      * @param {String} text
35228      */
35229     setText : function(text){
35230         var oldText = this.text;
35231         this.text = text;
35232         this.attributes.text = text;
35233         if(this.rendered){ // event without subscribing
35234             this.ui.onTextChange(this, text, oldText);
35235         }
35236         this.fireEvent("textchange", this, text, oldText);
35237     },
35238
35239     /**
35240      * Triggers selection of this node
35241      */
35242     select : function(){
35243         this.getOwnerTree().getSelectionModel().select(this);
35244     },
35245
35246     /**
35247      * Triggers deselection of this node
35248      */
35249     unselect : function(){
35250         this.getOwnerTree().getSelectionModel().unselect(this);
35251     },
35252
35253     /**
35254      * Returns true if this node is selected
35255      * @return {Boolean}
35256      */
35257     isSelected : function(){
35258         return this.getOwnerTree().getSelectionModel().isSelected(this);
35259     },
35260
35261     /**
35262      * Expand this node.
35263      * @param {Boolean} deep (optional) True to expand all children as well
35264      * @param {Boolean} anim (optional) false to cancel the default animation
35265      * @param {Function} callback (optional) A callback to be called when
35266      * expanding this node completes (does not wait for deep expand to complete).
35267      * Called with 1 parameter, this node.
35268      */
35269     expand : function(deep, anim, callback){
35270         if(!this.expanded){
35271             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
35272                 return;
35273             }
35274             if(!this.childrenRendered){
35275                 this.renderChildren();
35276             }
35277             this.expanded = true;
35278             
35279             if(!this.isHiddenRoot() && (this.getOwnerTree() && this.getOwnerTree().animate && anim !== false) || anim){
35280                 this.ui.animExpand(function(){
35281                     this.fireEvent("expand", this);
35282                     if(typeof callback == "function"){
35283                         callback(this);
35284                     }
35285                     if(deep === true){
35286                         this.expandChildNodes(true);
35287                     }
35288                 }.createDelegate(this));
35289                 return;
35290             }else{
35291                 this.ui.expand();
35292                 this.fireEvent("expand", this);
35293                 if(typeof callback == "function"){
35294                     callback(this);
35295                 }
35296             }
35297         }else{
35298            if(typeof callback == "function"){
35299                callback(this);
35300            }
35301         }
35302         if(deep === true){
35303             this.expandChildNodes(true);
35304         }
35305     },
35306
35307     isHiddenRoot : function(){
35308         return this.isRoot && !this.getOwnerTree().rootVisible;
35309     },
35310
35311     /**
35312      * Collapse this node.
35313      * @param {Boolean} deep (optional) True to collapse all children as well
35314      * @param {Boolean} anim (optional) false to cancel the default animation
35315      */
35316     collapse : function(deep, anim){
35317         if(this.expanded && !this.isHiddenRoot()){
35318             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
35319                 return;
35320             }
35321             this.expanded = false;
35322             if((this.getOwnerTree().animate && anim !== false) || anim){
35323                 this.ui.animCollapse(function(){
35324                     this.fireEvent("collapse", this);
35325                     if(deep === true){
35326                         this.collapseChildNodes(true);
35327                     }
35328                 }.createDelegate(this));
35329                 return;
35330             }else{
35331                 this.ui.collapse();
35332                 this.fireEvent("collapse", this);
35333             }
35334         }
35335         if(deep === true){
35336             var cs = this.childNodes;
35337             for(var i = 0, len = cs.length; i < len; i++) {
35338                 cs[i].collapse(true, false);
35339             }
35340         }
35341     },
35342
35343     // private
35344     delayedExpand : function(delay){
35345         if(!this.expandProcId){
35346             this.expandProcId = this.expand.defer(delay, this);
35347         }
35348     },
35349
35350     // private
35351     cancelExpand : function(){
35352         if(this.expandProcId){
35353             clearTimeout(this.expandProcId);
35354         }
35355         this.expandProcId = false;
35356     },
35357
35358     /**
35359      * Toggles expanded/collapsed state of the node
35360      */
35361     toggle : function(){
35362         if(this.expanded){
35363             this.collapse();
35364         }else{
35365             this.expand();
35366         }
35367     },
35368
35369     /**
35370      * Ensures all parent nodes are expanded
35371      */
35372     ensureVisible : function(callback){
35373         var tree = this.getOwnerTree();
35374         tree.expandPath(this.parentNode.getPath(), false, function(){
35375             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
35376             Roo.callback(callback);
35377         }.createDelegate(this));
35378     },
35379
35380     /**
35381      * Expand all child nodes
35382      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
35383      */
35384     expandChildNodes : function(deep){
35385         var cs = this.childNodes;
35386         for(var i = 0, len = cs.length; i < len; i++) {
35387                 cs[i].expand(deep);
35388         }
35389     },
35390
35391     /**
35392      * Collapse all child nodes
35393      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
35394      */
35395     collapseChildNodes : function(deep){
35396         var cs = this.childNodes;
35397         for(var i = 0, len = cs.length; i < len; i++) {
35398                 cs[i].collapse(deep);
35399         }
35400     },
35401
35402     /**
35403      * Disables this node
35404      */
35405     disable : function(){
35406         this.disabled = true;
35407         this.unselect();
35408         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
35409             this.ui.onDisableChange(this, true);
35410         }
35411         this.fireEvent("disabledchange", this, true);
35412     },
35413
35414     /**
35415      * Enables this node
35416      */
35417     enable : function(){
35418         this.disabled = false;
35419         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
35420             this.ui.onDisableChange(this, false);
35421         }
35422         this.fireEvent("disabledchange", this, false);
35423     },
35424
35425     // private
35426     renderChildren : function(suppressEvent){
35427         if(suppressEvent !== false){
35428             this.fireEvent("beforechildrenrendered", this);
35429         }
35430         var cs = this.childNodes;
35431         for(var i = 0, len = cs.length; i < len; i++){
35432             cs[i].render(true);
35433         }
35434         this.childrenRendered = true;
35435     },
35436
35437     // private
35438     sort : function(fn, scope){
35439         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
35440         if(this.childrenRendered){
35441             var cs = this.childNodes;
35442             for(var i = 0, len = cs.length; i < len; i++){
35443                 cs[i].render(true);
35444             }
35445         }
35446     },
35447
35448     // private
35449     render : function(bulkRender){
35450         this.ui.render(bulkRender);
35451         if(!this.rendered){
35452             this.rendered = true;
35453             if(this.expanded){
35454                 this.expanded = false;
35455                 this.expand(false, false);
35456             }
35457         }
35458     },
35459
35460     // private
35461     renderIndent : function(deep, refresh){
35462         if(refresh){
35463             this.ui.childIndent = null;
35464         }
35465         this.ui.renderIndent();
35466         if(deep === true && this.childrenRendered){
35467             var cs = this.childNodes;
35468             for(var i = 0, len = cs.length; i < len; i++){
35469                 cs[i].renderIndent(true, refresh);
35470             }
35471         }
35472     }
35473 });/*
35474  * Based on:
35475  * Ext JS Library 1.1.1
35476  * Copyright(c) 2006-2007, Ext JS, LLC.
35477  *
35478  * Originally Released Under LGPL - original licence link has changed is not relivant.
35479  *
35480  * Fork - LGPL
35481  * <script type="text/javascript">
35482  */
35483  
35484 /**
35485  * @class Roo.tree.AsyncTreeNode
35486  * @extends Roo.tree.TreeNode
35487  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
35488  * @constructor
35489  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
35490  */
35491  Roo.tree.AsyncTreeNode = function(config){
35492     this.loaded = false;
35493     this.loading = false;
35494     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
35495     /**
35496     * @event beforeload
35497     * Fires before this node is loaded, return false to cancel
35498     * @param {Node} this This node
35499     */
35500     this.addEvents({'beforeload':true, 'load': true});
35501     /**
35502     * @event load
35503     * Fires when this node is loaded
35504     * @param {Node} this This node
35505     */
35506     /**
35507      * The loader used by this node (defaults to using the tree's defined loader)
35508      * @type TreeLoader
35509      * @property loader
35510      */
35511 };
35512 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
35513     expand : function(deep, anim, callback){
35514         if(this.loading){ // if an async load is already running, waiting til it's done
35515             var timer;
35516             var f = function(){
35517                 if(!this.loading){ // done loading
35518                     clearInterval(timer);
35519                     this.expand(deep, anim, callback);
35520                 }
35521             }.createDelegate(this);
35522             timer = setInterval(f, 200);
35523             return;
35524         }
35525         if(!this.loaded){
35526             if(this.fireEvent("beforeload", this) === false){
35527                 return;
35528             }
35529             this.loading = true;
35530             this.ui.beforeLoad(this);
35531             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
35532             if(loader){
35533                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
35534                 return;
35535             }
35536         }
35537         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
35538     },
35539     
35540     /**
35541      * Returns true if this node is currently loading
35542      * @return {Boolean}
35543      */
35544     isLoading : function(){
35545         return this.loading;  
35546     },
35547     
35548     loadComplete : function(deep, anim, callback){
35549         this.loading = false;
35550         this.loaded = true;
35551         this.ui.afterLoad(this);
35552         this.fireEvent("load", this);
35553         this.expand(deep, anim, callback);
35554     },
35555     
35556     /**
35557      * Returns true if this node has been loaded
35558      * @return {Boolean}
35559      */
35560     isLoaded : function(){
35561         return this.loaded;
35562     },
35563     
35564     hasChildNodes : function(){
35565         if(!this.isLeaf() && !this.loaded){
35566             return true;
35567         }else{
35568             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
35569         }
35570     },
35571
35572     /**
35573      * Trigger a reload for this node
35574      * @param {Function} callback
35575      */
35576     reload : function(callback){
35577         this.collapse(false, false);
35578         while(this.firstChild){
35579             this.removeChild(this.firstChild);
35580         }
35581         this.childrenRendered = false;
35582         this.loaded = false;
35583         if(this.isHiddenRoot()){
35584             this.expanded = false;
35585         }
35586         this.expand(false, false, callback);
35587     }
35588 });/*
35589  * Based on:
35590  * Ext JS Library 1.1.1
35591  * Copyright(c) 2006-2007, Ext JS, LLC.
35592  *
35593  * Originally Released Under LGPL - original licence link has changed is not relivant.
35594  *
35595  * Fork - LGPL
35596  * <script type="text/javascript">
35597  */
35598  
35599 /**
35600  * @class Roo.tree.TreeNodeUI
35601  * @constructor
35602  * @param {Object} node The node to render
35603  * The TreeNode UI implementation is separate from the
35604  * tree implementation. Unless you are customizing the tree UI,
35605  * you should never have to use this directly.
35606  */
35607 Roo.tree.TreeNodeUI = function(node){
35608     this.node = node;
35609     this.rendered = false;
35610     this.animating = false;
35611     this.emptyIcon = Roo.BLANK_IMAGE_URL;
35612 };
35613
35614 Roo.tree.TreeNodeUI.prototype = {
35615     removeChild : function(node){
35616         if(this.rendered){
35617             this.ctNode.removeChild(node.ui.getEl());
35618         }
35619     },
35620
35621     beforeLoad : function(){
35622          this.addClass("x-tree-node-loading");
35623     },
35624
35625     afterLoad : function(){
35626          this.removeClass("x-tree-node-loading");
35627     },
35628
35629     onTextChange : function(node, text, oldText){
35630         if(this.rendered){
35631             this.textNode.innerHTML = text;
35632         }
35633     },
35634
35635     onDisableChange : function(node, state){
35636         this.disabled = state;
35637         if(state){
35638             this.addClass("x-tree-node-disabled");
35639         }else{
35640             this.removeClass("x-tree-node-disabled");
35641         }
35642     },
35643
35644     onSelectedChange : function(state){
35645         if(state){
35646             this.focus();
35647             this.addClass("x-tree-selected");
35648         }else{
35649             //this.blur();
35650             this.removeClass("x-tree-selected");
35651         }
35652     },
35653
35654     onMove : function(tree, node, oldParent, newParent, index, refNode){
35655         this.childIndent = null;
35656         if(this.rendered){
35657             var targetNode = newParent.ui.getContainer();
35658             if(!targetNode){//target not rendered
35659                 this.holder = document.createElement("div");
35660                 this.holder.appendChild(this.wrap);
35661                 return;
35662             }
35663             var insertBefore = refNode ? refNode.ui.getEl() : null;
35664             if(insertBefore){
35665                 targetNode.insertBefore(this.wrap, insertBefore);
35666             }else{
35667                 targetNode.appendChild(this.wrap);
35668             }
35669             this.node.renderIndent(true);
35670         }
35671     },
35672
35673     addClass : function(cls){
35674         if(this.elNode){
35675             Roo.fly(this.elNode).addClass(cls);
35676         }
35677     },
35678
35679     removeClass : function(cls){
35680         if(this.elNode){
35681             Roo.fly(this.elNode).removeClass(cls);
35682         }
35683     },
35684
35685     remove : function(){
35686         if(this.rendered){
35687             this.holder = document.createElement("div");
35688             this.holder.appendChild(this.wrap);
35689         }
35690     },
35691
35692     fireEvent : function(){
35693         return this.node.fireEvent.apply(this.node, arguments);
35694     },
35695
35696     initEvents : function(){
35697         this.node.on("move", this.onMove, this);
35698         var E = Roo.EventManager;
35699         var a = this.anchor;
35700
35701         var el = Roo.fly(a, '_treeui');
35702
35703         if(Roo.isOpera){ // opera render bug ignores the CSS
35704             el.setStyle("text-decoration", "none");
35705         }
35706
35707         el.on("click", this.onClick, this);
35708         el.on("dblclick", this.onDblClick, this);
35709
35710         if(this.checkbox){
35711             Roo.EventManager.on(this.checkbox,
35712                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
35713         }
35714
35715         el.on("contextmenu", this.onContextMenu, this);
35716
35717         var icon = Roo.fly(this.iconNode);
35718         icon.on("click", this.onClick, this);
35719         icon.on("dblclick", this.onDblClick, this);
35720         icon.on("contextmenu", this.onContextMenu, this);
35721         E.on(this.ecNode, "click", this.ecClick, this, true);
35722
35723         if(this.node.disabled){
35724             this.addClass("x-tree-node-disabled");
35725         }
35726         if(this.node.hidden){
35727             this.addClass("x-tree-node-disabled");
35728         }
35729         var ot = this.node.getOwnerTree();
35730         var dd = ot ? (ot.enableDD || ot.enableDrag || ot.enableDrop) : false;
35731         if(dd && (!this.node.isRoot || ot.rootVisible)){
35732             Roo.dd.Registry.register(this.elNode, {
35733                 node: this.node,
35734                 handles: this.getDDHandles(),
35735                 isHandle: false
35736             });
35737         }
35738     },
35739
35740     getDDHandles : function(){
35741         return [this.iconNode, this.textNode];
35742     },
35743
35744     hide : function(){
35745         if(this.rendered){
35746             this.wrap.style.display = "none";
35747         }
35748     },
35749
35750     show : function(){
35751         if(this.rendered){
35752             this.wrap.style.display = "";
35753         }
35754     },
35755
35756     onContextMenu : function(e){
35757         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
35758             e.preventDefault();
35759             this.focus();
35760             this.fireEvent("contextmenu", this.node, e);
35761         }
35762     },
35763
35764     onClick : function(e){
35765         if(this.dropping){
35766             e.stopEvent();
35767             return;
35768         }
35769         if(this.fireEvent("beforeclick", this.node, e) !== false){
35770             if(!this.disabled && this.node.attributes.href){
35771                 this.fireEvent("click", this.node, e);
35772                 return;
35773             }
35774             e.preventDefault();
35775             if(this.disabled){
35776                 return;
35777             }
35778
35779             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
35780                 this.node.toggle();
35781             }
35782
35783             this.fireEvent("click", this.node, e);
35784         }else{
35785             e.stopEvent();
35786         }
35787     },
35788
35789     onDblClick : function(e){
35790         e.preventDefault();
35791         if(this.disabled){
35792             return;
35793         }
35794         if(this.checkbox){
35795             this.toggleCheck();
35796         }
35797         if(!this.animating && this.node.hasChildNodes()){
35798             this.node.toggle();
35799         }
35800         this.fireEvent("dblclick", this.node, e);
35801     },
35802
35803     onCheckChange : function(){
35804         var checked = this.checkbox.checked;
35805         this.node.attributes.checked = checked;
35806         this.fireEvent('checkchange', this.node, checked);
35807     },
35808
35809     ecClick : function(e){
35810         if(!this.animating && this.node.hasChildNodes()){
35811             this.node.toggle();
35812         }
35813     },
35814
35815     startDrop : function(){
35816         this.dropping = true;
35817     },
35818
35819     // delayed drop so the click event doesn't get fired on a drop
35820     endDrop : function(){
35821        setTimeout(function(){
35822            this.dropping = false;
35823        }.createDelegate(this), 50);
35824     },
35825
35826     expand : function(){
35827         this.updateExpandIcon();
35828         this.ctNode.style.display = "";
35829     },
35830
35831     focus : function(){
35832         if(!this.node.preventHScroll){
35833             try{this.anchor.focus();
35834             }catch(e){}
35835         }else if(!Roo.isIE){
35836             try{
35837                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
35838                 var l = noscroll.scrollLeft;
35839                 this.anchor.focus();
35840                 noscroll.scrollLeft = l;
35841             }catch(e){}
35842         }
35843     },
35844
35845     toggleCheck : function(value){
35846         var cb = this.checkbox;
35847         if(cb){
35848             cb.checked = (value === undefined ? !cb.checked : value);
35849         }
35850     },
35851
35852     blur : function(){
35853         try{
35854             this.anchor.blur();
35855         }catch(e){}
35856     },
35857
35858     animExpand : function(callback){
35859         var ct = Roo.get(this.ctNode);
35860         ct.stopFx();
35861         if(!this.node.hasChildNodes()){
35862             this.updateExpandIcon();
35863             this.ctNode.style.display = "";
35864             Roo.callback(callback);
35865             return;
35866         }
35867         this.animating = true;
35868         this.updateExpandIcon();
35869
35870         ct.slideIn('t', {
35871            callback : function(){
35872                this.animating = false;
35873                Roo.callback(callback);
35874             },
35875             scope: this,
35876             duration: this.node.ownerTree.duration || .25
35877         });
35878     },
35879
35880     highlight : function(){
35881         var tree = this.node.getOwnerTree();
35882         Roo.fly(this.wrap).highlight(
35883             tree.hlColor || "C3DAF9",
35884             {endColor: tree.hlBaseColor}
35885         );
35886     },
35887
35888     collapse : function(){
35889         this.updateExpandIcon();
35890         this.ctNode.style.display = "none";
35891     },
35892
35893     animCollapse : function(callback){
35894         var ct = Roo.get(this.ctNode);
35895         ct.enableDisplayMode('block');
35896         ct.stopFx();
35897
35898         this.animating = true;
35899         this.updateExpandIcon();
35900
35901         ct.slideOut('t', {
35902             callback : function(){
35903                this.animating = false;
35904                Roo.callback(callback);
35905             },
35906             scope: this,
35907             duration: this.node.ownerTree.duration || .25
35908         });
35909     },
35910
35911     getContainer : function(){
35912         return this.ctNode;
35913     },
35914
35915     getEl : function(){
35916         return this.wrap;
35917     },
35918
35919     appendDDGhost : function(ghostNode){
35920         ghostNode.appendChild(this.elNode.cloneNode(true));
35921     },
35922
35923     getDDRepairXY : function(){
35924         return Roo.lib.Dom.getXY(this.iconNode);
35925     },
35926
35927     onRender : function(){
35928         this.render();
35929     },
35930
35931     render : function(bulkRender){
35932         var n = this.node, a = n.attributes;
35933         var targetNode = n.parentNode ?
35934               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
35935
35936         if(!this.rendered){
35937             this.rendered = true;
35938
35939             this.renderElements(n, a, targetNode, bulkRender);
35940
35941             if(a.qtip){
35942                if(this.textNode.setAttributeNS){
35943                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
35944                    if(a.qtipTitle){
35945                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
35946                    }
35947                }else{
35948                    this.textNode.setAttribute("ext:qtip", a.qtip);
35949                    if(a.qtipTitle){
35950                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
35951                    }
35952                }
35953             }else if(a.qtipCfg){
35954                 a.qtipCfg.target = Roo.id(this.textNode);
35955                 Roo.QuickTips.register(a.qtipCfg);
35956             }
35957             this.initEvents();
35958             if(!this.node.expanded){
35959                 this.updateExpandIcon();
35960             }
35961         }else{
35962             if(bulkRender === true) {
35963                 targetNode.appendChild(this.wrap);
35964             }
35965         }
35966     },
35967
35968     renderElements : function(n, a, targetNode, bulkRender)
35969     {
35970         // add some indent caching, this helps performance when rendering a large tree
35971         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
35972         var t = n.getOwnerTree();
35973         var txt = t && t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
35974         if (typeof(n.attributes.html) != 'undefined') {
35975             txt = n.attributes.html;
35976         }
35977         var tip = t && t.rendererTip ? t.rendererTip(n.attributes) : txt;
35978         var cb = typeof a.checked == 'boolean';
35979         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
35980         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
35981             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
35982             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
35983             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
35984             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
35985             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
35986              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
35987                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
35988             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
35989             "</li>"];
35990
35991         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
35992             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
35993                                 n.nextSibling.ui.getEl(), buf.join(""));
35994         }else{
35995             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
35996         }
35997
35998         this.elNode = this.wrap.childNodes[0];
35999         this.ctNode = this.wrap.childNodes[1];
36000         var cs = this.elNode.childNodes;
36001         this.indentNode = cs[0];
36002         this.ecNode = cs[1];
36003         this.iconNode = cs[2];
36004         var index = 3;
36005         if(cb){
36006             this.checkbox = cs[3];
36007             index++;
36008         }
36009         this.anchor = cs[index];
36010         this.textNode = cs[index].firstChild;
36011     },
36012
36013     getAnchor : function(){
36014         return this.anchor;
36015     },
36016
36017     getTextEl : function(){
36018         return this.textNode;
36019     },
36020
36021     getIconEl : function(){
36022         return this.iconNode;
36023     },
36024
36025     isChecked : function(){
36026         return this.checkbox ? this.checkbox.checked : false;
36027     },
36028
36029     updateExpandIcon : function(){
36030         if(this.rendered){
36031             var n = this.node, c1, c2;
36032             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
36033             var hasChild = n.hasChildNodes();
36034             if(hasChild){
36035                 if(n.expanded){
36036                     cls += "-minus";
36037                     c1 = "x-tree-node-collapsed";
36038                     c2 = "x-tree-node-expanded";
36039                 }else{
36040                     cls += "-plus";
36041                     c1 = "x-tree-node-expanded";
36042                     c2 = "x-tree-node-collapsed";
36043                 }
36044                 if(this.wasLeaf){
36045                     this.removeClass("x-tree-node-leaf");
36046                     this.wasLeaf = false;
36047                 }
36048                 if(this.c1 != c1 || this.c2 != c2){
36049                     Roo.fly(this.elNode).replaceClass(c1, c2);
36050                     this.c1 = c1; this.c2 = c2;
36051                 }
36052             }else{
36053                 // this changes non-leafs into leafs if they have no children.
36054                 // it's not very rational behaviour..
36055                 
36056                 if(!this.wasLeaf && this.node.leaf){
36057                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
36058                     delete this.c1;
36059                     delete this.c2;
36060                     this.wasLeaf = true;
36061                 }
36062             }
36063             var ecc = "x-tree-ec-icon "+cls;
36064             if(this.ecc != ecc){
36065                 this.ecNode.className = ecc;
36066                 this.ecc = ecc;
36067             }
36068         }
36069     },
36070
36071     getChildIndent : function(){
36072         if(!this.childIndent){
36073             var buf = [];
36074             var p = this.node;
36075             while(p){
36076                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
36077                     if(!p.isLast()) {
36078                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
36079                     } else {
36080                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
36081                     }
36082                 }
36083                 p = p.parentNode;
36084             }
36085             this.childIndent = buf.join("");
36086         }
36087         return this.childIndent;
36088     },
36089
36090     renderIndent : function(){
36091         if(this.rendered){
36092             var indent = "";
36093             var p = this.node.parentNode;
36094             if(p){
36095                 indent = p.ui.getChildIndent();
36096             }
36097             if(this.indentMarkup != indent){ // don't rerender if not required
36098                 this.indentNode.innerHTML = indent;
36099                 this.indentMarkup = indent;
36100             }
36101             this.updateExpandIcon();
36102         }
36103     }
36104 };
36105
36106 Roo.tree.RootTreeNodeUI = function(){
36107     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
36108 };
36109 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
36110     render : function(){
36111         if(!this.rendered){
36112             var targetNode = this.node.ownerTree.innerCt.dom;
36113             this.node.expanded = true;
36114             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
36115             this.wrap = this.ctNode = targetNode.firstChild;
36116         }
36117     },
36118     collapse : function(){
36119     },
36120     expand : function(){
36121     }
36122 });/*
36123  * Based on:
36124  * Ext JS Library 1.1.1
36125  * Copyright(c) 2006-2007, Ext JS, LLC.
36126  *
36127  * Originally Released Under LGPL - original licence link has changed is not relivant.
36128  *
36129  * Fork - LGPL
36130  * <script type="text/javascript">
36131  */
36132 /**
36133  * @class Roo.tree.TreeLoader
36134  * @extends Roo.util.Observable
36135  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
36136  * nodes from a specified URL. The response must be a javascript Array definition
36137  * who's elements are node definition objects. eg:
36138  * <pre><code>
36139 {  success : true,
36140    data :      [
36141    
36142     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
36143     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
36144     ]
36145 }
36146
36147
36148 </code></pre>
36149  * <br><br>
36150  * The old style respose with just an array is still supported, but not recommended.
36151  * <br><br>
36152  *
36153  * A server request is sent, and child nodes are loaded only when a node is expanded.
36154  * The loading node's id is passed to the server under the parameter name "node" to
36155  * enable the server to produce the correct child nodes.
36156  * <br><br>
36157  * To pass extra parameters, an event handler may be attached to the "beforeload"
36158  * event, and the parameters specified in the TreeLoader's baseParams property:
36159  * <pre><code>
36160     myTreeLoader.on("beforeload", function(treeLoader, node) {
36161         this.baseParams.category = node.attributes.category;
36162     }, this);
36163     
36164 </code></pre>
36165  *
36166  * This would pass an HTTP parameter called "category" to the server containing
36167  * the value of the Node's "category" attribute.
36168  * @constructor
36169  * Creates a new Treeloader.
36170  * @param {Object} config A config object containing config properties.
36171  */
36172 Roo.tree.TreeLoader = function(config){
36173     this.baseParams = {};
36174     this.requestMethod = "POST";
36175     Roo.apply(this, config);
36176
36177     this.addEvents({
36178     
36179         /**
36180          * @event beforeload
36181          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
36182          * @param {Object} This TreeLoader object.
36183          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
36184          * @param {Object} callback The callback function specified in the {@link #load} call.
36185          */
36186         beforeload : true,
36187         /**
36188          * @event load
36189          * Fires when the node has been successfuly loaded.
36190          * @param {Object} This TreeLoader object.
36191          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
36192          * @param {Object} response The response object containing the data from the server.
36193          */
36194         load : true,
36195         /**
36196          * @event loadexception
36197          * Fires if the network request failed.
36198          * @param {Object} This TreeLoader object.
36199          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
36200          * @param {Object} response The response object containing the data from the server.
36201          */
36202         loadexception : true,
36203         /**
36204          * @event create
36205          * Fires before a node is created, enabling you to return custom Node types 
36206          * @param {Object} This TreeLoader object.
36207          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
36208          */
36209         create : true
36210     });
36211
36212     Roo.tree.TreeLoader.superclass.constructor.call(this);
36213 };
36214
36215 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
36216     /**
36217     * @cfg {String} dataUrl The URL from which to request a Json string which
36218     * specifies an array of node definition object representing the child nodes
36219     * to be loaded.
36220     */
36221     /**
36222     * @cfg {String} requestMethod either GET or POST
36223     * defaults to POST (due to BC)
36224     * to be loaded.
36225     */
36226     /**
36227     * @cfg {Object} baseParams (optional) An object containing properties which
36228     * specify HTTP parameters to be passed to each request for child nodes.
36229     */
36230     /**
36231     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
36232     * created by this loader. If the attributes sent by the server have an attribute in this object,
36233     * they take priority.
36234     */
36235     /**
36236     * @cfg {Object} uiProviders (optional) An object containing properties which
36237     * 
36238     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
36239     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
36240     * <i>uiProvider</i> attribute of a returned child node is a string rather
36241     * than a reference to a TreeNodeUI implementation, this that string value
36242     * is used as a property name in the uiProviders object. You can define the provider named
36243     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
36244     */
36245     uiProviders : {},
36246
36247     /**
36248     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
36249     * child nodes before loading.
36250     */
36251     clearOnLoad : true,
36252
36253     /**
36254     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
36255     * property on loading, rather than expecting an array. (eg. more compatible to a standard
36256     * Grid query { data : [ .....] }
36257     */
36258     
36259     root : false,
36260      /**
36261     * @cfg {String} queryParam (optional) 
36262     * Name of the query as it will be passed on the querystring (defaults to 'node')
36263     * eg. the request will be ?node=[id]
36264     */
36265     
36266     
36267     queryParam: false,
36268     
36269     /**
36270      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
36271      * This is called automatically when a node is expanded, but may be used to reload
36272      * a node (or append new children if the {@link #clearOnLoad} option is false.)
36273      * @param {Roo.tree.TreeNode} node
36274      * @param {Function} callback
36275      */
36276     load : function(node, callback){
36277         if(this.clearOnLoad){
36278             while(node.firstChild){
36279                 node.removeChild(node.firstChild);
36280             }
36281         }
36282         if(node.attributes.children){ // preloaded json children
36283             var cs = node.attributes.children;
36284             for(var i = 0, len = cs.length; i < len; i++){
36285                 node.appendChild(this.createNode(cs[i]));
36286             }
36287             if(typeof callback == "function"){
36288                 callback();
36289             }
36290         }else if(this.dataUrl){
36291             this.requestData(node, callback);
36292         }
36293     },
36294
36295     getParams: function(node){
36296         var buf = [], bp = this.baseParams;
36297         for(var key in bp){
36298             if(typeof bp[key] != "function"){
36299                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
36300             }
36301         }
36302         var n = this.queryParam === false ? 'node' : this.queryParam;
36303         buf.push(n + "=", encodeURIComponent(node.id));
36304         return buf.join("");
36305     },
36306
36307     requestData : function(node, callback){
36308         if(this.fireEvent("beforeload", this, node, callback) !== false){
36309             this.transId = Roo.Ajax.request({
36310                 method:this.requestMethod,
36311                 url: this.dataUrl||this.url,
36312                 success: this.handleResponse,
36313                 failure: this.handleFailure,
36314                 scope: this,
36315                 argument: {callback: callback, node: node},
36316                 params: this.getParams(node)
36317             });
36318         }else{
36319             // if the load is cancelled, make sure we notify
36320             // the node that we are done
36321             if(typeof callback == "function"){
36322                 callback();
36323             }
36324         }
36325     },
36326
36327     isLoading : function(){
36328         return this.transId ? true : false;
36329     },
36330
36331     abort : function(){
36332         if(this.isLoading()){
36333             Roo.Ajax.abort(this.transId);
36334         }
36335     },
36336
36337     // private
36338     createNode : function(attr)
36339     {
36340         // apply baseAttrs, nice idea Corey!
36341         if(this.baseAttrs){
36342             Roo.applyIf(attr, this.baseAttrs);
36343         }
36344         if(this.applyLoader !== false){
36345             attr.loader = this;
36346         }
36347         // uiProvider = depreciated..
36348         
36349         if(typeof(attr.uiProvider) == 'string'){
36350            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
36351                 /**  eval:var:attr */ eval(attr.uiProvider);
36352         }
36353         if(typeof(this.uiProviders['default']) != 'undefined') {
36354             attr.uiProvider = this.uiProviders['default'];
36355         }
36356         
36357         this.fireEvent('create', this, attr);
36358         
36359         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
36360         return(attr.leaf ?
36361                         new Roo.tree.TreeNode(attr) :
36362                         new Roo.tree.AsyncTreeNode(attr));
36363     },
36364
36365     processResponse : function(response, node, callback)
36366     {
36367         var json = response.responseText;
36368         try {
36369             
36370             var o = Roo.decode(json);
36371             
36372             if (this.root === false && typeof(o.success) != undefined) {
36373                 this.root = 'data'; // the default behaviour for list like data..
36374                 }
36375                 
36376             if (this.root !== false &&  !o.success) {
36377                 // it's a failure condition.
36378                 var a = response.argument;
36379                 this.fireEvent("loadexception", this, a.node, response);
36380                 Roo.log("Load failed - should have a handler really");
36381                 return;
36382             }
36383             
36384             
36385             
36386             if (this.root !== false) {
36387                  o = o[this.root];
36388             }
36389             
36390             for(var i = 0, len = o.length; i < len; i++){
36391                 var n = this.createNode(o[i]);
36392                 if(n){
36393                     node.appendChild(n);
36394                 }
36395             }
36396             if(typeof callback == "function"){
36397                 callback(this, node);
36398             }
36399         }catch(e){
36400             this.handleFailure(response);
36401         }
36402     },
36403
36404     handleResponse : function(response){
36405         this.transId = false;
36406         var a = response.argument;
36407         this.processResponse(response, a.node, a.callback);
36408         this.fireEvent("load", this, a.node, response);
36409     },
36410
36411     handleFailure : function(response)
36412     {
36413         // should handle failure better..
36414         this.transId = false;
36415         var a = response.argument;
36416         this.fireEvent("loadexception", this, a.node, response);
36417         if(typeof a.callback == "function"){
36418             a.callback(this, a.node);
36419         }
36420     }
36421 });/*
36422  * Based on:
36423  * Ext JS Library 1.1.1
36424  * Copyright(c) 2006-2007, Ext JS, LLC.
36425  *
36426  * Originally Released Under LGPL - original licence link has changed is not relivant.
36427  *
36428  * Fork - LGPL
36429  * <script type="text/javascript">
36430  */
36431
36432 /**
36433 * @class Roo.tree.TreeFilter
36434 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
36435 * @param {TreePanel} tree
36436 * @param {Object} config (optional)
36437  */
36438 Roo.tree.TreeFilter = function(tree, config){
36439     this.tree = tree;
36440     this.filtered = {};
36441     Roo.apply(this, config);
36442 };
36443
36444 Roo.tree.TreeFilter.prototype = {
36445     clearBlank:false,
36446     reverse:false,
36447     autoClear:false,
36448     remove:false,
36449
36450      /**
36451      * Filter the data by a specific attribute.
36452      * @param {String/RegExp} value Either string that the attribute value
36453      * should start with or a RegExp to test against the attribute
36454      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
36455      * @param {TreeNode} startNode (optional) The node to start the filter at.
36456      */
36457     filter : function(value, attr, startNode){
36458         attr = attr || "text";
36459         var f;
36460         if(typeof value == "string"){
36461             var vlen = value.length;
36462             // auto clear empty filter
36463             if(vlen == 0 && this.clearBlank){
36464                 this.clear();
36465                 return;
36466             }
36467             value = value.toLowerCase();
36468             f = function(n){
36469                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
36470             };
36471         }else if(value.exec){ // regex?
36472             f = function(n){
36473                 return value.test(n.attributes[attr]);
36474             };
36475         }else{
36476             throw 'Illegal filter type, must be string or regex';
36477         }
36478         this.filterBy(f, null, startNode);
36479         },
36480
36481     /**
36482      * Filter by a function. The passed function will be called with each
36483      * node in the tree (or from the startNode). If the function returns true, the node is kept
36484      * otherwise it is filtered. If a node is filtered, its children are also filtered.
36485      * @param {Function} fn The filter function
36486      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
36487      */
36488     filterBy : function(fn, scope, startNode){
36489         startNode = startNode || this.tree.root;
36490         if(this.autoClear){
36491             this.clear();
36492         }
36493         var af = this.filtered, rv = this.reverse;
36494         var f = function(n){
36495             if(n == startNode){
36496                 return true;
36497             }
36498             if(af[n.id]){
36499                 return false;
36500             }
36501             var m = fn.call(scope || n, n);
36502             if(!m || rv){
36503                 af[n.id] = n;
36504                 n.ui.hide();
36505                 return false;
36506             }
36507             return true;
36508         };
36509         startNode.cascade(f);
36510         if(this.remove){
36511            for(var id in af){
36512                if(typeof id != "function"){
36513                    var n = af[id];
36514                    if(n && n.parentNode){
36515                        n.parentNode.removeChild(n);
36516                    }
36517                }
36518            }
36519         }
36520     },
36521
36522     /**
36523      * Clears the current filter. Note: with the "remove" option
36524      * set a filter cannot be cleared.
36525      */
36526     clear : function(){
36527         var t = this.tree;
36528         var af = this.filtered;
36529         for(var id in af){
36530             if(typeof id != "function"){
36531                 var n = af[id];
36532                 if(n){
36533                     n.ui.show();
36534                 }
36535             }
36536         }
36537         this.filtered = {};
36538     }
36539 };
36540 /*
36541  * Based on:
36542  * Ext JS Library 1.1.1
36543  * Copyright(c) 2006-2007, Ext JS, LLC.
36544  *
36545  * Originally Released Under LGPL - original licence link has changed is not relivant.
36546  *
36547  * Fork - LGPL
36548  * <script type="text/javascript">
36549  */
36550  
36551
36552 /**
36553  * @class Roo.tree.TreeSorter
36554  * Provides sorting of nodes in a TreePanel
36555  * 
36556  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
36557  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
36558  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
36559  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
36560  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
36561  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
36562  * @constructor
36563  * @param {TreePanel} tree
36564  * @param {Object} config
36565  */
36566 Roo.tree.TreeSorter = function(tree, config){
36567     Roo.apply(this, config);
36568     tree.on("beforechildrenrendered", this.doSort, this);
36569     tree.on("append", this.updateSort, this);
36570     tree.on("insert", this.updateSort, this);
36571     
36572     var dsc = this.dir && this.dir.toLowerCase() == "desc";
36573     var p = this.property || "text";
36574     var sortType = this.sortType;
36575     var fs = this.folderSort;
36576     var cs = this.caseSensitive === true;
36577     var leafAttr = this.leafAttr || 'leaf';
36578
36579     this.sortFn = function(n1, n2){
36580         if(fs){
36581             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
36582                 return 1;
36583             }
36584             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
36585                 return -1;
36586             }
36587         }
36588         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
36589         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
36590         if(v1 < v2){
36591                         return dsc ? +1 : -1;
36592                 }else if(v1 > v2){
36593                         return dsc ? -1 : +1;
36594         }else{
36595                 return 0;
36596         }
36597     };
36598 };
36599
36600 Roo.tree.TreeSorter.prototype = {
36601     doSort : function(node){
36602         node.sort(this.sortFn);
36603     },
36604     
36605     compareNodes : function(n1, n2){
36606         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
36607     },
36608     
36609     updateSort : function(tree, node){
36610         if(node.childrenRendered){
36611             this.doSort.defer(1, this, [node]);
36612         }
36613     }
36614 };/*
36615  * Based on:
36616  * Ext JS Library 1.1.1
36617  * Copyright(c) 2006-2007, Ext JS, LLC.
36618  *
36619  * Originally Released Under LGPL - original licence link has changed is not relivant.
36620  *
36621  * Fork - LGPL
36622  * <script type="text/javascript">
36623  */
36624
36625 if(Roo.dd.DropZone){
36626     
36627 Roo.tree.TreeDropZone = function(tree, config){
36628     this.allowParentInsert = false;
36629     this.allowContainerDrop = false;
36630     this.appendOnly = false;
36631     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
36632     this.tree = tree;
36633     this.lastInsertClass = "x-tree-no-status";
36634     this.dragOverData = {};
36635 };
36636
36637 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
36638     ddGroup : "TreeDD",
36639     scroll:  true,
36640     
36641     expandDelay : 1000,
36642     
36643     expandNode : function(node){
36644         if(node.hasChildNodes() && !node.isExpanded()){
36645             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
36646         }
36647     },
36648     
36649     queueExpand : function(node){
36650         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
36651     },
36652     
36653     cancelExpand : function(){
36654         if(this.expandProcId){
36655             clearTimeout(this.expandProcId);
36656             this.expandProcId = false;
36657         }
36658     },
36659     
36660     isValidDropPoint : function(n, pt, dd, e, data){
36661         if(!n || !data){ return false; }
36662         var targetNode = n.node;
36663         var dropNode = data.node;
36664         // default drop rules
36665         if(!(targetNode && targetNode.isTarget && pt)){
36666             return false;
36667         }
36668         if(pt == "append" && targetNode.allowChildren === false){
36669             return false;
36670         }
36671         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
36672             return false;
36673         }
36674         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
36675             return false;
36676         }
36677         // reuse the object
36678         var overEvent = this.dragOverData;
36679         overEvent.tree = this.tree;
36680         overEvent.target = targetNode;
36681         overEvent.data = data;
36682         overEvent.point = pt;
36683         overEvent.source = dd;
36684         overEvent.rawEvent = e;
36685         overEvent.dropNode = dropNode;
36686         overEvent.cancel = false;  
36687         var result = this.tree.fireEvent("nodedragover", overEvent);
36688         return overEvent.cancel === false && result !== false;
36689     },
36690     
36691     getDropPoint : function(e, n, dd)
36692     {
36693         var tn = n.node;
36694         if(tn.isRoot){
36695             return tn.allowChildren !== false ? "append" : false; // always append for root
36696         }
36697         var dragEl = n.ddel;
36698         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
36699         var y = Roo.lib.Event.getPageY(e);
36700         //var noAppend = tn.allowChildren === false || tn.isLeaf();
36701         
36702         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
36703         var noAppend = tn.allowChildren === false;
36704         if(this.appendOnly || tn.parentNode.allowChildren === false){
36705             return noAppend ? false : "append";
36706         }
36707         var noBelow = false;
36708         if(!this.allowParentInsert){
36709             noBelow = tn.hasChildNodes() && tn.isExpanded();
36710         }
36711         var q = (b - t) / (noAppend ? 2 : 3);
36712         if(y >= t && y < (t + q)){
36713             return "above";
36714         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
36715             return "below";
36716         }else{
36717             return "append";
36718         }
36719     },
36720     
36721     onNodeEnter : function(n, dd, e, data)
36722     {
36723         this.cancelExpand();
36724     },
36725     
36726     onNodeOver : function(n, dd, e, data)
36727     {
36728        
36729         var pt = this.getDropPoint(e, n, dd);
36730         var node = n.node;
36731         
36732         // auto node expand check
36733         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
36734             this.queueExpand(node);
36735         }else if(pt != "append"){
36736             this.cancelExpand();
36737         }
36738         
36739         // set the insert point style on the target node
36740         var returnCls = this.dropNotAllowed;
36741         if(this.isValidDropPoint(n, pt, dd, e, data)){
36742            if(pt){
36743                var el = n.ddel;
36744                var cls;
36745                if(pt == "above"){
36746                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
36747                    cls = "x-tree-drag-insert-above";
36748                }else if(pt == "below"){
36749                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
36750                    cls = "x-tree-drag-insert-below";
36751                }else{
36752                    returnCls = "x-tree-drop-ok-append";
36753                    cls = "x-tree-drag-append";
36754                }
36755                if(this.lastInsertClass != cls){
36756                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
36757                    this.lastInsertClass = cls;
36758                }
36759            }
36760        }
36761        return returnCls;
36762     },
36763     
36764     onNodeOut : function(n, dd, e, data){
36765         
36766         this.cancelExpand();
36767         this.removeDropIndicators(n);
36768     },
36769     
36770     onNodeDrop : function(n, dd, e, data){
36771         var point = this.getDropPoint(e, n, dd);
36772         var targetNode = n.node;
36773         targetNode.ui.startDrop();
36774         if(!this.isValidDropPoint(n, point, dd, e, data)){
36775             targetNode.ui.endDrop();
36776             return false;
36777         }
36778         // first try to find the drop node
36779         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
36780         var dropEvent = {
36781             tree : this.tree,
36782             target: targetNode,
36783             data: data,
36784             point: point,
36785             source: dd,
36786             rawEvent: e,
36787             dropNode: dropNode,
36788             cancel: !dropNode   
36789         };
36790         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
36791         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
36792             targetNode.ui.endDrop();
36793             return false;
36794         }
36795         // allow target changing
36796         targetNode = dropEvent.target;
36797         if(point == "append" && !targetNode.isExpanded()){
36798             targetNode.expand(false, null, function(){
36799                 this.completeDrop(dropEvent);
36800             }.createDelegate(this));
36801         }else{
36802             this.completeDrop(dropEvent);
36803         }
36804         return true;
36805     },
36806     
36807     completeDrop : function(de){
36808         var ns = de.dropNode, p = de.point, t = de.target;
36809         if(!(ns instanceof Array)){
36810             ns = [ns];
36811         }
36812         var n;
36813         for(var i = 0, len = ns.length; i < len; i++){
36814             n = ns[i];
36815             if(p == "above"){
36816                 t.parentNode.insertBefore(n, t);
36817             }else if(p == "below"){
36818                 t.parentNode.insertBefore(n, t.nextSibling);
36819             }else{
36820                 t.appendChild(n);
36821             }
36822         }
36823         n.ui.focus();
36824         if(this.tree.hlDrop){
36825             n.ui.highlight();
36826         }
36827         t.ui.endDrop();
36828         this.tree.fireEvent("nodedrop", de);
36829     },
36830     
36831     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
36832         if(this.tree.hlDrop){
36833             dropNode.ui.focus();
36834             dropNode.ui.highlight();
36835         }
36836         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
36837     },
36838     
36839     getTree : function(){
36840         return this.tree;
36841     },
36842     
36843     removeDropIndicators : function(n){
36844         if(n && n.ddel){
36845             var el = n.ddel;
36846             Roo.fly(el).removeClass([
36847                     "x-tree-drag-insert-above",
36848                     "x-tree-drag-insert-below",
36849                     "x-tree-drag-append"]);
36850             this.lastInsertClass = "_noclass";
36851         }
36852     },
36853     
36854     beforeDragDrop : function(target, e, id){
36855         this.cancelExpand();
36856         return true;
36857     },
36858     
36859     afterRepair : function(data){
36860         if(data && Roo.enableFx){
36861             data.node.ui.highlight();
36862         }
36863         this.hideProxy();
36864     } 
36865     
36866 });
36867
36868 }
36869 /*
36870  * Based on:
36871  * Ext JS Library 1.1.1
36872  * Copyright(c) 2006-2007, Ext JS, LLC.
36873  *
36874  * Originally Released Under LGPL - original licence link has changed is not relivant.
36875  *
36876  * Fork - LGPL
36877  * <script type="text/javascript">
36878  */
36879  
36880
36881 if(Roo.dd.DragZone){
36882 Roo.tree.TreeDragZone = function(tree, config){
36883     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
36884     this.tree = tree;
36885 };
36886
36887 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
36888     ddGroup : "TreeDD",
36889    
36890     onBeforeDrag : function(data, e){
36891         var n = data.node;
36892         return n && n.draggable && !n.disabled;
36893     },
36894      
36895     
36896     onInitDrag : function(e){
36897         var data = this.dragData;
36898         this.tree.getSelectionModel().select(data.node);
36899         this.proxy.update("");
36900         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
36901         this.tree.fireEvent("startdrag", this.tree, data.node, e);
36902     },
36903     
36904     getRepairXY : function(e, data){
36905         return data.node.ui.getDDRepairXY();
36906     },
36907     
36908     onEndDrag : function(data, e){
36909         this.tree.fireEvent("enddrag", this.tree, data.node, e);
36910         
36911         
36912     },
36913     
36914     onValidDrop : function(dd, e, id){
36915         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
36916         this.hideProxy();
36917     },
36918     
36919     beforeInvalidDrop : function(e, id){
36920         // this scrolls the original position back into view
36921         var sm = this.tree.getSelectionModel();
36922         sm.clearSelections();
36923         sm.select(this.dragData.node);
36924     }
36925 });
36926 }/*
36927  * Based on:
36928  * Ext JS Library 1.1.1
36929  * Copyright(c) 2006-2007, Ext JS, LLC.
36930  *
36931  * Originally Released Under LGPL - original licence link has changed is not relivant.
36932  *
36933  * Fork - LGPL
36934  * <script type="text/javascript">
36935  */
36936 /**
36937  * @class Roo.tree.TreeEditor
36938  * @extends Roo.Editor
36939  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
36940  * as the editor field.
36941  * @constructor
36942  * @param {Object} config (used to be the tree panel.)
36943  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
36944  * 
36945  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
36946  * @cfg {Roo.form.TextField|Object} field The field configuration
36947  *
36948  * 
36949  */
36950 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
36951     var tree = config;
36952     var field;
36953     if (oldconfig) { // old style..
36954         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
36955     } else {
36956         // new style..
36957         tree = config.tree;
36958         config.field = config.field  || {};
36959         config.field.xtype = 'TextField';
36960         field = Roo.factory(config.field, Roo.form);
36961     }
36962     config = config || {};
36963     
36964     
36965     this.addEvents({
36966         /**
36967          * @event beforenodeedit
36968          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
36969          * false from the handler of this event.
36970          * @param {Editor} this
36971          * @param {Roo.tree.Node} node 
36972          */
36973         "beforenodeedit" : true
36974     });
36975     
36976     //Roo.log(config);
36977     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
36978
36979     this.tree = tree;
36980
36981     tree.on('beforeclick', this.beforeNodeClick, this);
36982     tree.getTreeEl().on('mousedown', this.hide, this);
36983     this.on('complete', this.updateNode, this);
36984     this.on('beforestartedit', this.fitToTree, this);
36985     this.on('startedit', this.bindScroll, this, {delay:10});
36986     this.on('specialkey', this.onSpecialKey, this);
36987 };
36988
36989 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
36990     /**
36991      * @cfg {String} alignment
36992      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
36993      */
36994     alignment: "l-l",
36995     // inherit
36996     autoSize: false,
36997     /**
36998      * @cfg {Boolean} hideEl
36999      * True to hide the bound element while the editor is displayed (defaults to false)
37000      */
37001     hideEl : false,
37002     /**
37003      * @cfg {String} cls
37004      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
37005      */
37006     cls: "x-small-editor x-tree-editor",
37007     /**
37008      * @cfg {Boolean} shim
37009      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
37010      */
37011     shim:false,
37012     // inherit
37013     shadow:"frame",
37014     /**
37015      * @cfg {Number} maxWidth
37016      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
37017      * the containing tree element's size, it will be automatically limited for you to the container width, taking
37018      * scroll and client offsets into account prior to each edit.
37019      */
37020     maxWidth: 250,
37021
37022     editDelay : 350,
37023
37024     // private
37025     fitToTree : function(ed, el){
37026         var td = this.tree.getTreeEl().dom, nd = el.dom;
37027         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
37028             td.scrollLeft = nd.offsetLeft;
37029         }
37030         var w = Math.min(
37031                 this.maxWidth,
37032                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
37033         this.setSize(w, '');
37034         
37035         return this.fireEvent('beforenodeedit', this, this.editNode);
37036         
37037     },
37038
37039     // private
37040     triggerEdit : function(node){
37041         this.completeEdit();
37042         this.editNode = node;
37043         this.startEdit(node.ui.textNode, node.text);
37044     },
37045
37046     // private
37047     bindScroll : function(){
37048         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
37049     },
37050
37051     // private
37052     beforeNodeClick : function(node, e){
37053         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
37054         this.lastClick = new Date();
37055         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
37056             e.stopEvent();
37057             this.triggerEdit(node);
37058             return false;
37059         }
37060         return true;
37061     },
37062
37063     // private
37064     updateNode : function(ed, value){
37065         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
37066         this.editNode.setText(value);
37067     },
37068
37069     // private
37070     onHide : function(){
37071         Roo.tree.TreeEditor.superclass.onHide.call(this);
37072         if(this.editNode){
37073             this.editNode.ui.focus();
37074         }
37075     },
37076
37077     // private
37078     onSpecialKey : function(field, e){
37079         var k = e.getKey();
37080         if(k == e.ESC){
37081             e.stopEvent();
37082             this.cancelEdit();
37083         }else if(k == e.ENTER && !e.hasModifier()){
37084             e.stopEvent();
37085             this.completeEdit();
37086         }
37087     }
37088 });//<Script type="text/javascript">
37089 /*
37090  * Based on:
37091  * Ext JS Library 1.1.1
37092  * Copyright(c) 2006-2007, Ext JS, LLC.
37093  *
37094  * Originally Released Under LGPL - original licence link has changed is not relivant.
37095  *
37096  * Fork - LGPL
37097  * <script type="text/javascript">
37098  */
37099  
37100 /**
37101  * Not documented??? - probably should be...
37102  */
37103
37104 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
37105     //focus: Roo.emptyFn, // prevent odd scrolling behavior
37106     
37107     renderElements : function(n, a, targetNode, bulkRender){
37108         //consel.log("renderElements?");
37109         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
37110
37111         var t = n.getOwnerTree();
37112         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
37113         
37114         var cols = t.columns;
37115         var bw = t.borderWidth;
37116         var c = cols[0];
37117         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
37118          var cb = typeof a.checked == "boolean";
37119         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
37120         var colcls = 'x-t-' + tid + '-c0';
37121         var buf = [
37122             '<li class="x-tree-node">',
37123             
37124                 
37125                 '<div class="x-tree-node-el ', a.cls,'">',
37126                     // extran...
37127                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
37128                 
37129                 
37130                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
37131                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
37132                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
37133                            (a.icon ? ' x-tree-node-inline-icon' : ''),
37134                            (a.iconCls ? ' '+a.iconCls : ''),
37135                            '" unselectable="on" />',
37136                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
37137                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
37138                              
37139                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
37140                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
37141                             '<span unselectable="on" qtip="' + tx + '">',
37142                              tx,
37143                              '</span></a>' ,
37144                     '</div>',
37145                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
37146                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
37147                  ];
37148         for(var i = 1, len = cols.length; i < len; i++){
37149             c = cols[i];
37150             colcls = 'x-t-' + tid + '-c' +i;
37151             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
37152             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
37153                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
37154                       "</div>");
37155          }
37156          
37157          buf.push(
37158             '</a>',
37159             '<div class="x-clear"></div></div>',
37160             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
37161             "</li>");
37162         
37163         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
37164             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
37165                                 n.nextSibling.ui.getEl(), buf.join(""));
37166         }else{
37167             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
37168         }
37169         var el = this.wrap.firstChild;
37170         this.elRow = el;
37171         this.elNode = el.firstChild;
37172         this.ranchor = el.childNodes[1];
37173         this.ctNode = this.wrap.childNodes[1];
37174         var cs = el.firstChild.childNodes;
37175         this.indentNode = cs[0];
37176         this.ecNode = cs[1];
37177         this.iconNode = cs[2];
37178         var index = 3;
37179         if(cb){
37180             this.checkbox = cs[3];
37181             index++;
37182         }
37183         this.anchor = cs[index];
37184         
37185         this.textNode = cs[index].firstChild;
37186         
37187         //el.on("click", this.onClick, this);
37188         //el.on("dblclick", this.onDblClick, this);
37189         
37190         
37191        // console.log(this);
37192     },
37193     initEvents : function(){
37194         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
37195         
37196             
37197         var a = this.ranchor;
37198
37199         var el = Roo.get(a);
37200
37201         if(Roo.isOpera){ // opera render bug ignores the CSS
37202             el.setStyle("text-decoration", "none");
37203         }
37204
37205         el.on("click", this.onClick, this);
37206         el.on("dblclick", this.onDblClick, this);
37207         el.on("contextmenu", this.onContextMenu, this);
37208         
37209     },
37210     
37211     /*onSelectedChange : function(state){
37212         if(state){
37213             this.focus();
37214             this.addClass("x-tree-selected");
37215         }else{
37216             //this.blur();
37217             this.removeClass("x-tree-selected");
37218         }
37219     },*/
37220     addClass : function(cls){
37221         if(this.elRow){
37222             Roo.fly(this.elRow).addClass(cls);
37223         }
37224         
37225     },
37226     
37227     
37228     removeClass : function(cls){
37229         if(this.elRow){
37230             Roo.fly(this.elRow).removeClass(cls);
37231         }
37232     }
37233
37234     
37235     
37236 });//<Script type="text/javascript">
37237
37238 /*
37239  * Based on:
37240  * Ext JS Library 1.1.1
37241  * Copyright(c) 2006-2007, Ext JS, LLC.
37242  *
37243  * Originally Released Under LGPL - original licence link has changed is not relivant.
37244  *
37245  * Fork - LGPL
37246  * <script type="text/javascript">
37247  */
37248  
37249
37250 /**
37251  * @class Roo.tree.ColumnTree
37252  * @extends Roo.data.TreePanel
37253  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
37254  * @cfg {int} borderWidth  compined right/left border allowance
37255  * @constructor
37256  * @param {String/HTMLElement/Element} el The container element
37257  * @param {Object} config
37258  */
37259 Roo.tree.ColumnTree =  function(el, config)
37260 {
37261    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
37262    this.addEvents({
37263         /**
37264         * @event resize
37265         * Fire this event on a container when it resizes
37266         * @param {int} w Width
37267         * @param {int} h Height
37268         */
37269        "resize" : true
37270     });
37271     this.on('resize', this.onResize, this);
37272 };
37273
37274 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
37275     //lines:false,
37276     
37277     
37278     borderWidth: Roo.isBorderBox ? 0 : 2, 
37279     headEls : false,
37280     
37281     render : function(){
37282         // add the header.....
37283        
37284         Roo.tree.ColumnTree.superclass.render.apply(this);
37285         
37286         this.el.addClass('x-column-tree');
37287         
37288         this.headers = this.el.createChild(
37289             {cls:'x-tree-headers'},this.innerCt.dom);
37290    
37291         var cols = this.columns, c;
37292         var totalWidth = 0;
37293         this.headEls = [];
37294         var  len = cols.length;
37295         for(var i = 0; i < len; i++){
37296              c = cols[i];
37297              totalWidth += c.width;
37298             this.headEls.push(this.headers.createChild({
37299                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
37300                  cn: {
37301                      cls:'x-tree-hd-text',
37302                      html: c.header
37303                  },
37304                  style:'width:'+(c.width-this.borderWidth)+'px;'
37305              }));
37306         }
37307         this.headers.createChild({cls:'x-clear'});
37308         // prevent floats from wrapping when clipped
37309         this.headers.setWidth(totalWidth);
37310         //this.innerCt.setWidth(totalWidth);
37311         this.innerCt.setStyle({ overflow: 'auto' });
37312         this.onResize(this.width, this.height);
37313              
37314         
37315     },
37316     onResize : function(w,h)
37317     {
37318         this.height = h;
37319         this.width = w;
37320         // resize cols..
37321         this.innerCt.setWidth(this.width);
37322         this.innerCt.setHeight(this.height-20);
37323         
37324         // headers...
37325         var cols = this.columns, c;
37326         var totalWidth = 0;
37327         var expEl = false;
37328         var len = cols.length;
37329         for(var i = 0; i < len; i++){
37330             c = cols[i];
37331             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
37332                 // it's the expander..
37333                 expEl  = this.headEls[i];
37334                 continue;
37335             }
37336             totalWidth += c.width;
37337             
37338         }
37339         if (expEl) {
37340             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
37341         }
37342         this.headers.setWidth(w-20);
37343
37344         
37345         
37346         
37347     }
37348 });
37349 /*
37350  * Based on:
37351  * Ext JS Library 1.1.1
37352  * Copyright(c) 2006-2007, Ext JS, LLC.
37353  *
37354  * Originally Released Under LGPL - original licence link has changed is not relivant.
37355  *
37356  * Fork - LGPL
37357  * <script type="text/javascript">
37358  */
37359  
37360 /**
37361  * @class Roo.menu.Menu
37362  * @extends Roo.util.Observable
37363  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
37364  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
37365  * @constructor
37366  * Creates a new Menu
37367  * @param {Object} config Configuration options
37368  */
37369 Roo.menu.Menu = function(config){
37370     
37371     Roo.menu.Menu.superclass.constructor.call(this, config);
37372     
37373     this.id = this.id || Roo.id();
37374     this.addEvents({
37375         /**
37376          * @event beforeshow
37377          * Fires before this menu is displayed
37378          * @param {Roo.menu.Menu} this
37379          */
37380         beforeshow : true,
37381         /**
37382          * @event beforehide
37383          * Fires before this menu is hidden
37384          * @param {Roo.menu.Menu} this
37385          */
37386         beforehide : true,
37387         /**
37388          * @event show
37389          * Fires after this menu is displayed
37390          * @param {Roo.menu.Menu} this
37391          */
37392         show : true,
37393         /**
37394          * @event hide
37395          * Fires after this menu is hidden
37396          * @param {Roo.menu.Menu} this
37397          */
37398         hide : true,
37399         /**
37400          * @event click
37401          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
37402          * @param {Roo.menu.Menu} this
37403          * @param {Roo.menu.Item} menuItem The menu item that was clicked
37404          * @param {Roo.EventObject} e
37405          */
37406         click : true,
37407         /**
37408          * @event mouseover
37409          * Fires when the mouse is hovering over this menu
37410          * @param {Roo.menu.Menu} this
37411          * @param {Roo.EventObject} e
37412          * @param {Roo.menu.Item} menuItem The menu item that was clicked
37413          */
37414         mouseover : true,
37415         /**
37416          * @event mouseout
37417          * Fires when the mouse exits this menu
37418          * @param {Roo.menu.Menu} this
37419          * @param {Roo.EventObject} e
37420          * @param {Roo.menu.Item} menuItem The menu item that was clicked
37421          */
37422         mouseout : true,
37423         /**
37424          * @event itemclick
37425          * Fires when a menu item contained in this menu is clicked
37426          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
37427          * @param {Roo.EventObject} e
37428          */
37429         itemclick: true
37430     });
37431     if (this.registerMenu) {
37432         Roo.menu.MenuMgr.register(this);
37433     }
37434     
37435     var mis = this.items;
37436     this.items = new Roo.util.MixedCollection();
37437     if(mis){
37438         this.add.apply(this, mis);
37439     }
37440 };
37441
37442 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
37443     /**
37444      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
37445      */
37446     minWidth : 120,
37447     /**
37448      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
37449      * for bottom-right shadow (defaults to "sides")
37450      */
37451     shadow : "sides",
37452     /**
37453      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
37454      * this menu (defaults to "tl-tr?")
37455      */
37456     subMenuAlign : "tl-tr?",
37457     /**
37458      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
37459      * relative to its element of origin (defaults to "tl-bl?")
37460      */
37461     defaultAlign : "tl-bl?",
37462     /**
37463      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
37464      */
37465     allowOtherMenus : false,
37466     /**
37467      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
37468      */
37469     registerMenu : true,
37470
37471     hidden:true,
37472
37473     // private
37474     render : function(){
37475         if(this.el){
37476             return;
37477         }
37478         var el = this.el = new Roo.Layer({
37479             cls: "x-menu",
37480             shadow:this.shadow,
37481             constrain: false,
37482             parentEl: this.parentEl || document.body,
37483             zindex:15000
37484         });
37485
37486         this.keyNav = new Roo.menu.MenuNav(this);
37487
37488         if(this.plain){
37489             el.addClass("x-menu-plain");
37490         }
37491         if(this.cls){
37492             el.addClass(this.cls);
37493         }
37494         // generic focus element
37495         this.focusEl = el.createChild({
37496             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
37497         });
37498         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
37499         //disabling touch- as it's causing issues ..
37500         //ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
37501         ul.on('click'   , this.onClick, this);
37502         
37503         
37504         ul.on("mouseover", this.onMouseOver, this);
37505         ul.on("mouseout", this.onMouseOut, this);
37506         this.items.each(function(item){
37507             if (item.hidden) {
37508                 return;
37509             }
37510             
37511             var li = document.createElement("li");
37512             li.className = "x-menu-list-item";
37513             ul.dom.appendChild(li);
37514             item.render(li, this);
37515         }, this);
37516         this.ul = ul;
37517         this.autoWidth();
37518     },
37519
37520     // private
37521     autoWidth : function(){
37522         var el = this.el, ul = this.ul;
37523         if(!el){
37524             return;
37525         }
37526         var w = this.width;
37527         if(w){
37528             el.setWidth(w);
37529         }else if(Roo.isIE){
37530             el.setWidth(this.minWidth);
37531             var t = el.dom.offsetWidth; // force recalc
37532             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
37533         }
37534     },
37535
37536     // private
37537     delayAutoWidth : function(){
37538         if(this.rendered){
37539             if(!this.awTask){
37540                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
37541             }
37542             this.awTask.delay(20);
37543         }
37544     },
37545
37546     // private
37547     findTargetItem : function(e){
37548         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
37549         if(t && t.menuItemId){
37550             return this.items.get(t.menuItemId);
37551         }
37552     },
37553
37554     // private
37555     onClick : function(e){
37556         Roo.log("menu.onClick");
37557         var t = this.findTargetItem(e);
37558         if(!t){
37559             return;
37560         }
37561         Roo.log(e);
37562         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
37563             if(t == this.activeItem && t.shouldDeactivate(e)){
37564                 this.activeItem.deactivate();
37565                 delete this.activeItem;
37566                 return;
37567             }
37568             if(t.canActivate){
37569                 this.setActiveItem(t, true);
37570             }
37571             return;
37572             
37573             
37574         }
37575         
37576         t.onClick(e);
37577         this.fireEvent("click", this, t, e);
37578     },
37579
37580     // private
37581     setActiveItem : function(item, autoExpand){
37582         if(item != this.activeItem){
37583             if(this.activeItem){
37584                 this.activeItem.deactivate();
37585             }
37586             this.activeItem = item;
37587             item.activate(autoExpand);
37588         }else if(autoExpand){
37589             item.expandMenu();
37590         }
37591     },
37592
37593     // private
37594     tryActivate : function(start, step){
37595         var items = this.items;
37596         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
37597             var item = items.get(i);
37598             if(!item.disabled && item.canActivate){
37599                 this.setActiveItem(item, false);
37600                 return item;
37601             }
37602         }
37603         return false;
37604     },
37605
37606     // private
37607     onMouseOver : function(e){
37608         var t;
37609         if(t = this.findTargetItem(e)){
37610             if(t.canActivate && !t.disabled){
37611                 this.setActiveItem(t, true);
37612             }
37613         }
37614         this.fireEvent("mouseover", this, e, t);
37615     },
37616
37617     // private
37618     onMouseOut : function(e){
37619         var t;
37620         if(t = this.findTargetItem(e)){
37621             if(t == this.activeItem && t.shouldDeactivate(e)){
37622                 this.activeItem.deactivate();
37623                 delete this.activeItem;
37624             }
37625         }
37626         this.fireEvent("mouseout", this, e, t);
37627     },
37628
37629     /**
37630      * Read-only.  Returns true if the menu is currently displayed, else false.
37631      * @type Boolean
37632      */
37633     isVisible : function(){
37634         return this.el && !this.hidden;
37635     },
37636
37637     /**
37638      * Displays this menu relative to another element
37639      * @param {String/HTMLElement/Roo.Element} element The element to align to
37640      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
37641      * the element (defaults to this.defaultAlign)
37642      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
37643      */
37644     show : function(el, pos, parentMenu){
37645         this.parentMenu = parentMenu;
37646         if(!this.el){
37647             this.render();
37648         }
37649         this.fireEvent("beforeshow", this);
37650         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
37651     },
37652
37653     /**
37654      * Displays this menu at a specific xy position
37655      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
37656      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
37657      */
37658     showAt : function(xy, parentMenu, /* private: */_e){
37659         this.parentMenu = parentMenu;
37660         if(!this.el){
37661             this.render();
37662         }
37663         if(_e !== false){
37664             this.fireEvent("beforeshow", this);
37665             xy = this.el.adjustForConstraints(xy);
37666         }
37667         this.el.setXY(xy);
37668         this.el.show();
37669         this.hidden = false;
37670         this.focus();
37671         this.fireEvent("show", this);
37672     },
37673
37674     focus : function(){
37675         if(!this.hidden){
37676             this.doFocus.defer(50, this);
37677         }
37678     },
37679
37680     doFocus : function(){
37681         if(!this.hidden){
37682             this.focusEl.focus();
37683         }
37684     },
37685
37686     /**
37687      * Hides this menu and optionally all parent menus
37688      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
37689      */
37690     hide : function(deep){
37691         if(this.el && this.isVisible()){
37692             this.fireEvent("beforehide", this);
37693             if(this.activeItem){
37694                 this.activeItem.deactivate();
37695                 this.activeItem = null;
37696             }
37697             this.el.hide();
37698             this.hidden = true;
37699             this.fireEvent("hide", this);
37700         }
37701         if(deep === true && this.parentMenu){
37702             this.parentMenu.hide(true);
37703         }
37704     },
37705
37706     /**
37707      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
37708      * Any of the following are valid:
37709      * <ul>
37710      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
37711      * <li>An HTMLElement object which will be converted to a menu item</li>
37712      * <li>A menu item config object that will be created as a new menu item</li>
37713      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
37714      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
37715      * </ul>
37716      * Usage:
37717      * <pre><code>
37718 // Create the menu
37719 var menu = new Roo.menu.Menu();
37720
37721 // Create a menu item to add by reference
37722 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
37723
37724 // Add a bunch of items at once using different methods.
37725 // Only the last item added will be returned.
37726 var item = menu.add(
37727     menuItem,                // add existing item by ref
37728     'Dynamic Item',          // new TextItem
37729     '-',                     // new separator
37730     { text: 'Config Item' }  // new item by config
37731 );
37732 </code></pre>
37733      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
37734      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
37735      */
37736     add : function(){
37737         var a = arguments, l = a.length, item;
37738         for(var i = 0; i < l; i++){
37739             var el = a[i];
37740             if ((typeof(el) == "object") && el.xtype && el.xns) {
37741                 el = Roo.factory(el, Roo.menu);
37742             }
37743             
37744             if(el.render){ // some kind of Item
37745                 item = this.addItem(el);
37746             }else if(typeof el == "string"){ // string
37747                 if(el == "separator" || el == "-"){
37748                     item = this.addSeparator();
37749                 }else{
37750                     item = this.addText(el);
37751                 }
37752             }else if(el.tagName || el.el){ // element
37753                 item = this.addElement(el);
37754             }else if(typeof el == "object"){ // must be menu item config?
37755                 item = this.addMenuItem(el);
37756             }
37757         }
37758         return item;
37759     },
37760
37761     /**
37762      * Returns this menu's underlying {@link Roo.Element} object
37763      * @return {Roo.Element} The element
37764      */
37765     getEl : function(){
37766         if(!this.el){
37767             this.render();
37768         }
37769         return this.el;
37770     },
37771
37772     /**
37773      * Adds a separator bar to the menu
37774      * @return {Roo.menu.Item} The menu item that was added
37775      */
37776     addSeparator : function(){
37777         return this.addItem(new Roo.menu.Separator());
37778     },
37779
37780     /**
37781      * Adds an {@link Roo.Element} object to the menu
37782      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
37783      * @return {Roo.menu.Item} The menu item that was added
37784      */
37785     addElement : function(el){
37786         return this.addItem(new Roo.menu.BaseItem(el));
37787     },
37788
37789     /**
37790      * Adds an existing object based on {@link Roo.menu.Item} to the menu
37791      * @param {Roo.menu.Item} item The menu item to add
37792      * @return {Roo.menu.Item} The menu item that was added
37793      */
37794     addItem : function(item){
37795         this.items.add(item);
37796         if(this.ul){
37797             var li = document.createElement("li");
37798             li.className = "x-menu-list-item";
37799             this.ul.dom.appendChild(li);
37800             item.render(li, this);
37801             this.delayAutoWidth();
37802         }
37803         return item;
37804     },
37805
37806     /**
37807      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
37808      * @param {Object} config A MenuItem config object
37809      * @return {Roo.menu.Item} The menu item that was added
37810      */
37811     addMenuItem : function(config){
37812         if(!(config instanceof Roo.menu.Item)){
37813             if(typeof config.checked == "boolean"){ // must be check menu item config?
37814                 config = new Roo.menu.CheckItem(config);
37815             }else{
37816                 config = new Roo.menu.Item(config);
37817             }
37818         }
37819         return this.addItem(config);
37820     },
37821
37822     /**
37823      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
37824      * @param {String} text The text to display in the menu item
37825      * @return {Roo.menu.Item} The menu item that was added
37826      */
37827     addText : function(text){
37828         return this.addItem(new Roo.menu.TextItem({ text : text }));
37829     },
37830
37831     /**
37832      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
37833      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
37834      * @param {Roo.menu.Item} item The menu item to add
37835      * @return {Roo.menu.Item} The menu item that was added
37836      */
37837     insert : function(index, item){
37838         this.items.insert(index, item);
37839         if(this.ul){
37840             var li = document.createElement("li");
37841             li.className = "x-menu-list-item";
37842             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
37843             item.render(li, this);
37844             this.delayAutoWidth();
37845         }
37846         return item;
37847     },
37848
37849     /**
37850      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
37851      * @param {Roo.menu.Item} item The menu item to remove
37852      */
37853     remove : function(item){
37854         this.items.removeKey(item.id);
37855         item.destroy();
37856     },
37857
37858     /**
37859      * Removes and destroys all items in the menu
37860      */
37861     removeAll : function(){
37862         var f;
37863         while(f = this.items.first()){
37864             this.remove(f);
37865         }
37866     }
37867 });
37868
37869 // MenuNav is a private utility class used internally by the Menu
37870 Roo.menu.MenuNav = function(menu){
37871     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
37872     this.scope = this.menu = menu;
37873 };
37874
37875 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
37876     doRelay : function(e, h){
37877         var k = e.getKey();
37878         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
37879             this.menu.tryActivate(0, 1);
37880             return false;
37881         }
37882         return h.call(this.scope || this, e, this.menu);
37883     },
37884
37885     up : function(e, m){
37886         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
37887             m.tryActivate(m.items.length-1, -1);
37888         }
37889     },
37890
37891     down : function(e, m){
37892         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
37893             m.tryActivate(0, 1);
37894         }
37895     },
37896
37897     right : function(e, m){
37898         if(m.activeItem){
37899             m.activeItem.expandMenu(true);
37900         }
37901     },
37902
37903     left : function(e, m){
37904         m.hide();
37905         if(m.parentMenu && m.parentMenu.activeItem){
37906             m.parentMenu.activeItem.activate();
37907         }
37908     },
37909
37910     enter : function(e, m){
37911         if(m.activeItem){
37912             e.stopPropagation();
37913             m.activeItem.onClick(e);
37914             m.fireEvent("click", this, m.activeItem);
37915             return true;
37916         }
37917     }
37918 });/*
37919  * Based on:
37920  * Ext JS Library 1.1.1
37921  * Copyright(c) 2006-2007, Ext JS, LLC.
37922  *
37923  * Originally Released Under LGPL - original licence link has changed is not relivant.
37924  *
37925  * Fork - LGPL
37926  * <script type="text/javascript">
37927  */
37928  
37929 /**
37930  * @class Roo.menu.MenuMgr
37931  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
37932  * @singleton
37933  */
37934 Roo.menu.MenuMgr = function(){
37935    var menus, active, groups = {}, attached = false, lastShow = new Date();
37936
37937    // private - called when first menu is created
37938    function init(){
37939        menus = {};
37940        active = new Roo.util.MixedCollection();
37941        Roo.get(document).addKeyListener(27, function(){
37942            if(active.length > 0){
37943                hideAll();
37944            }
37945        });
37946    }
37947
37948    // private
37949    function hideAll(){
37950        if(active && active.length > 0){
37951            var c = active.clone();
37952            c.each(function(m){
37953                m.hide();
37954            });
37955        }
37956    }
37957
37958    // private
37959    function onHide(m){
37960        active.remove(m);
37961        if(active.length < 1){
37962            Roo.get(document).un("mousedown", onMouseDown);
37963            attached = false;
37964        }
37965    }
37966
37967    // private
37968    function onShow(m){
37969        var last = active.last();
37970        lastShow = new Date();
37971        active.add(m);
37972        if(!attached){
37973            Roo.get(document).on("mousedown", onMouseDown);
37974            attached = true;
37975        }
37976        if(m.parentMenu){
37977           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
37978           m.parentMenu.activeChild = m;
37979        }else if(last && last.isVisible()){
37980           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
37981        }
37982    }
37983
37984    // private
37985    function onBeforeHide(m){
37986        if(m.activeChild){
37987            m.activeChild.hide();
37988        }
37989        if(m.autoHideTimer){
37990            clearTimeout(m.autoHideTimer);
37991            delete m.autoHideTimer;
37992        }
37993    }
37994
37995    // private
37996    function onBeforeShow(m){
37997        var pm = m.parentMenu;
37998        if(!pm && !m.allowOtherMenus){
37999            hideAll();
38000        }else if(pm && pm.activeChild && active != m){
38001            pm.activeChild.hide();
38002        }
38003    }
38004
38005    // private
38006    function onMouseDown(e){
38007        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
38008            hideAll();
38009        }
38010    }
38011
38012    // private
38013    function onBeforeCheck(mi, state){
38014        if(state){
38015            var g = groups[mi.group];
38016            for(var i = 0, l = g.length; i < l; i++){
38017                if(g[i] != mi){
38018                    g[i].setChecked(false);
38019                }
38020            }
38021        }
38022    }
38023
38024    return {
38025
38026        /**
38027         * Hides all menus that are currently visible
38028         */
38029        hideAll : function(){
38030             hideAll();  
38031        },
38032
38033        // private
38034        register : function(menu){
38035            if(!menus){
38036                init();
38037            }
38038            menus[menu.id] = menu;
38039            menu.on("beforehide", onBeforeHide);
38040            menu.on("hide", onHide);
38041            menu.on("beforeshow", onBeforeShow);
38042            menu.on("show", onShow);
38043            var g = menu.group;
38044            if(g && menu.events["checkchange"]){
38045                if(!groups[g]){
38046                    groups[g] = [];
38047                }
38048                groups[g].push(menu);
38049                menu.on("checkchange", onCheck);
38050            }
38051        },
38052
38053         /**
38054          * Returns a {@link Roo.menu.Menu} object
38055          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
38056          * be used to generate and return a new Menu instance.
38057          */
38058        get : function(menu){
38059            if(typeof menu == "string"){ // menu id
38060                return menus[menu];
38061            }else if(menu.events){  // menu instance
38062                return menu;
38063            }else if(typeof menu.length == 'number'){ // array of menu items?
38064                return new Roo.menu.Menu({items:menu});
38065            }else{ // otherwise, must be a config
38066                return new Roo.menu.Menu(menu);
38067            }
38068        },
38069
38070        // private
38071        unregister : function(menu){
38072            delete menus[menu.id];
38073            menu.un("beforehide", onBeforeHide);
38074            menu.un("hide", onHide);
38075            menu.un("beforeshow", onBeforeShow);
38076            menu.un("show", onShow);
38077            var g = menu.group;
38078            if(g && menu.events["checkchange"]){
38079                groups[g].remove(menu);
38080                menu.un("checkchange", onCheck);
38081            }
38082        },
38083
38084        // private
38085        registerCheckable : function(menuItem){
38086            var g = menuItem.group;
38087            if(g){
38088                if(!groups[g]){
38089                    groups[g] = [];
38090                }
38091                groups[g].push(menuItem);
38092                menuItem.on("beforecheckchange", onBeforeCheck);
38093            }
38094        },
38095
38096        // private
38097        unregisterCheckable : function(menuItem){
38098            var g = menuItem.group;
38099            if(g){
38100                groups[g].remove(menuItem);
38101                menuItem.un("beforecheckchange", onBeforeCheck);
38102            }
38103        }
38104    };
38105 }();/*
38106  * Based on:
38107  * Ext JS Library 1.1.1
38108  * Copyright(c) 2006-2007, Ext JS, LLC.
38109  *
38110  * Originally Released Under LGPL - original licence link has changed is not relivant.
38111  *
38112  * Fork - LGPL
38113  * <script type="text/javascript">
38114  */
38115  
38116
38117 /**
38118  * @class Roo.menu.BaseItem
38119  * @extends Roo.Component
38120  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
38121  * management and base configuration options shared by all menu components.
38122  * @constructor
38123  * Creates a new BaseItem
38124  * @param {Object} config Configuration options
38125  */
38126 Roo.menu.BaseItem = function(config){
38127     Roo.menu.BaseItem.superclass.constructor.call(this, config);
38128
38129     this.addEvents({
38130         /**
38131          * @event click
38132          * Fires when this item is clicked
38133          * @param {Roo.menu.BaseItem} this
38134          * @param {Roo.EventObject} e
38135          */
38136         click: true,
38137         /**
38138          * @event activate
38139          * Fires when this item is activated
38140          * @param {Roo.menu.BaseItem} this
38141          */
38142         activate : true,
38143         /**
38144          * @event deactivate
38145          * Fires when this item is deactivated
38146          * @param {Roo.menu.BaseItem} this
38147          */
38148         deactivate : true
38149     });
38150
38151     if(this.handler){
38152         this.on("click", this.handler, this.scope, true);
38153     }
38154 };
38155
38156 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
38157     /**
38158      * @cfg {Function} handler
38159      * A function that will handle the click event of this menu item (defaults to undefined)
38160      */
38161     /**
38162      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
38163      */
38164     canActivate : false,
38165     
38166      /**
38167      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
38168      */
38169     hidden: false,
38170     
38171     /**
38172      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
38173      */
38174     activeClass : "x-menu-item-active",
38175     /**
38176      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
38177      */
38178     hideOnClick : true,
38179     /**
38180      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
38181      */
38182     hideDelay : 100,
38183
38184     // private
38185     ctype: "Roo.menu.BaseItem",
38186
38187     // private
38188     actionMode : "container",
38189
38190     // private
38191     render : function(container, parentMenu){
38192         this.parentMenu = parentMenu;
38193         Roo.menu.BaseItem.superclass.render.call(this, container);
38194         this.container.menuItemId = this.id;
38195     },
38196
38197     // private
38198     onRender : function(container, position){
38199         this.el = Roo.get(this.el);
38200         container.dom.appendChild(this.el.dom);
38201     },
38202
38203     // private
38204     onClick : function(e){
38205         if(!this.disabled && this.fireEvent("click", this, e) !== false
38206                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
38207             this.handleClick(e);
38208         }else{
38209             e.stopEvent();
38210         }
38211     },
38212
38213     // private
38214     activate : function(){
38215         if(this.disabled){
38216             return false;
38217         }
38218         var li = this.container;
38219         li.addClass(this.activeClass);
38220         this.region = li.getRegion().adjust(2, 2, -2, -2);
38221         this.fireEvent("activate", this);
38222         return true;
38223     },
38224
38225     // private
38226     deactivate : function(){
38227         this.container.removeClass(this.activeClass);
38228         this.fireEvent("deactivate", this);
38229     },
38230
38231     // private
38232     shouldDeactivate : function(e){
38233         return !this.region || !this.region.contains(e.getPoint());
38234     },
38235
38236     // private
38237     handleClick : function(e){
38238         if(this.hideOnClick){
38239             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
38240         }
38241     },
38242
38243     // private
38244     expandMenu : function(autoActivate){
38245         // do nothing
38246     },
38247
38248     // private
38249     hideMenu : function(){
38250         // do nothing
38251     }
38252 });/*
38253  * Based on:
38254  * Ext JS Library 1.1.1
38255  * Copyright(c) 2006-2007, Ext JS, LLC.
38256  *
38257  * Originally Released Under LGPL - original licence link has changed is not relivant.
38258  *
38259  * Fork - LGPL
38260  * <script type="text/javascript">
38261  */
38262  
38263 /**
38264  * @class Roo.menu.Adapter
38265  * @extends Roo.menu.BaseItem
38266  * 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.
38267  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
38268  * @constructor
38269  * Creates a new Adapter
38270  * @param {Object} config Configuration options
38271  */
38272 Roo.menu.Adapter = function(component, config){
38273     Roo.menu.Adapter.superclass.constructor.call(this, config);
38274     this.component = component;
38275 };
38276 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
38277     // private
38278     canActivate : true,
38279
38280     // private
38281     onRender : function(container, position){
38282         this.component.render(container);
38283         this.el = this.component.getEl();
38284     },
38285
38286     // private
38287     activate : function(){
38288         if(this.disabled){
38289             return false;
38290         }
38291         this.component.focus();
38292         this.fireEvent("activate", this);
38293         return true;
38294     },
38295
38296     // private
38297     deactivate : function(){
38298         this.fireEvent("deactivate", this);
38299     },
38300
38301     // private
38302     disable : function(){
38303         this.component.disable();
38304         Roo.menu.Adapter.superclass.disable.call(this);
38305     },
38306
38307     // private
38308     enable : function(){
38309         this.component.enable();
38310         Roo.menu.Adapter.superclass.enable.call(this);
38311     }
38312 });/*
38313  * Based on:
38314  * Ext JS Library 1.1.1
38315  * Copyright(c) 2006-2007, Ext JS, LLC.
38316  *
38317  * Originally Released Under LGPL - original licence link has changed is not relivant.
38318  *
38319  * Fork - LGPL
38320  * <script type="text/javascript">
38321  */
38322
38323 /**
38324  * @class Roo.menu.TextItem
38325  * @extends Roo.menu.BaseItem
38326  * Adds a static text string to a menu, usually used as either a heading or group separator.
38327  * Note: old style constructor with text is still supported.
38328  * 
38329  * @constructor
38330  * Creates a new TextItem
38331  * @param {Object} cfg Configuration
38332  */
38333 Roo.menu.TextItem = function(cfg){
38334     if (typeof(cfg) == 'string') {
38335         this.text = cfg;
38336     } else {
38337         Roo.apply(this,cfg);
38338     }
38339     
38340     Roo.menu.TextItem.superclass.constructor.call(this);
38341 };
38342
38343 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
38344     /**
38345      * @cfg {Boolean} text Text to show on item.
38346      */
38347     text : '',
38348     
38349     /**
38350      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
38351      */
38352     hideOnClick : false,
38353     /**
38354      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
38355      */
38356     itemCls : "x-menu-text",
38357
38358     // private
38359     onRender : function(){
38360         var s = document.createElement("span");
38361         s.className = this.itemCls;
38362         s.innerHTML = this.text;
38363         this.el = s;
38364         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
38365     }
38366 });/*
38367  * Based on:
38368  * Ext JS Library 1.1.1
38369  * Copyright(c) 2006-2007, Ext JS, LLC.
38370  *
38371  * Originally Released Under LGPL - original licence link has changed is not relivant.
38372  *
38373  * Fork - LGPL
38374  * <script type="text/javascript">
38375  */
38376
38377 /**
38378  * @class Roo.menu.Separator
38379  * @extends Roo.menu.BaseItem
38380  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
38381  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
38382  * @constructor
38383  * @param {Object} config Configuration options
38384  */
38385 Roo.menu.Separator = function(config){
38386     Roo.menu.Separator.superclass.constructor.call(this, config);
38387 };
38388
38389 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
38390     /**
38391      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
38392      */
38393     itemCls : "x-menu-sep",
38394     /**
38395      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
38396      */
38397     hideOnClick : false,
38398
38399     // private
38400     onRender : function(li){
38401         var s = document.createElement("span");
38402         s.className = this.itemCls;
38403         s.innerHTML = "&#160;";
38404         this.el = s;
38405         li.addClass("x-menu-sep-li");
38406         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
38407     }
38408 });/*
38409  * Based on:
38410  * Ext JS Library 1.1.1
38411  * Copyright(c) 2006-2007, Ext JS, LLC.
38412  *
38413  * Originally Released Under LGPL - original licence link has changed is not relivant.
38414  *
38415  * Fork - LGPL
38416  * <script type="text/javascript">
38417  */
38418 /**
38419  * @class Roo.menu.Item
38420  * @extends Roo.menu.BaseItem
38421  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
38422  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
38423  * activation and click handling.
38424  * @constructor
38425  * Creates a new Item
38426  * @param {Object} config Configuration options
38427  */
38428 Roo.menu.Item = function(config){
38429     Roo.menu.Item.superclass.constructor.call(this, config);
38430     if(this.menu){
38431         this.menu = Roo.menu.MenuMgr.get(this.menu);
38432     }
38433 };
38434 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
38435     
38436     /**
38437      * @cfg {String} text
38438      * The text to show on the menu item.
38439      */
38440     text: '',
38441      /**
38442      * @cfg {String} HTML to render in menu
38443      * The text to show on the menu item (HTML version).
38444      */
38445     html: '',
38446     /**
38447      * @cfg {String} icon
38448      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
38449      */
38450     icon: undefined,
38451     /**
38452      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
38453      */
38454     itemCls : "x-menu-item",
38455     /**
38456      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
38457      */
38458     canActivate : true,
38459     /**
38460      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
38461      */
38462     showDelay: 200,
38463     // doc'd in BaseItem
38464     hideDelay: 200,
38465
38466     // private
38467     ctype: "Roo.menu.Item",
38468     
38469     // private
38470     onRender : function(container, position){
38471         var el = document.createElement("a");
38472         el.hideFocus = true;
38473         el.unselectable = "on";
38474         el.href = this.href || "#";
38475         if(this.hrefTarget){
38476             el.target = this.hrefTarget;
38477         }
38478         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
38479         
38480         var html = this.html.length ? this.html  : String.format('{0}',this.text);
38481         
38482         el.innerHTML = String.format(
38483                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
38484                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
38485         this.el = el;
38486         Roo.menu.Item.superclass.onRender.call(this, container, position);
38487     },
38488
38489     /**
38490      * Sets the text to display in this menu item
38491      * @param {String} text The text to display
38492      * @param {Boolean} isHTML true to indicate text is pure html.
38493      */
38494     setText : function(text, isHTML){
38495         if (isHTML) {
38496             this.html = text;
38497         } else {
38498             this.text = text;
38499             this.html = '';
38500         }
38501         if(this.rendered){
38502             var html = this.html.length ? this.html  : String.format('{0}',this.text);
38503      
38504             this.el.update(String.format(
38505                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
38506                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
38507             this.parentMenu.autoWidth();
38508         }
38509     },
38510
38511     // private
38512     handleClick : function(e){
38513         if(!this.href){ // if no link defined, stop the event automatically
38514             e.stopEvent();
38515         }
38516         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
38517     },
38518
38519     // private
38520     activate : function(autoExpand){
38521         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
38522             this.focus();
38523             if(autoExpand){
38524                 this.expandMenu();
38525             }
38526         }
38527         return true;
38528     },
38529
38530     // private
38531     shouldDeactivate : function(e){
38532         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
38533             if(this.menu && this.menu.isVisible()){
38534                 return !this.menu.getEl().getRegion().contains(e.getPoint());
38535             }
38536             return true;
38537         }
38538         return false;
38539     },
38540
38541     // private
38542     deactivate : function(){
38543         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
38544         this.hideMenu();
38545     },
38546
38547     // private
38548     expandMenu : function(autoActivate){
38549         if(!this.disabled && this.menu){
38550             clearTimeout(this.hideTimer);
38551             delete this.hideTimer;
38552             if(!this.menu.isVisible() && !this.showTimer){
38553                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
38554             }else if (this.menu.isVisible() && autoActivate){
38555                 this.menu.tryActivate(0, 1);
38556             }
38557         }
38558     },
38559
38560     // private
38561     deferExpand : function(autoActivate){
38562         delete this.showTimer;
38563         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
38564         if(autoActivate){
38565             this.menu.tryActivate(0, 1);
38566         }
38567     },
38568
38569     // private
38570     hideMenu : function(){
38571         clearTimeout(this.showTimer);
38572         delete this.showTimer;
38573         if(!this.hideTimer && this.menu && this.menu.isVisible()){
38574             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
38575         }
38576     },
38577
38578     // private
38579     deferHide : function(){
38580         delete this.hideTimer;
38581         this.menu.hide();
38582     }
38583 });/*
38584  * Based on:
38585  * Ext JS Library 1.1.1
38586  * Copyright(c) 2006-2007, Ext JS, LLC.
38587  *
38588  * Originally Released Under LGPL - original licence link has changed is not relivant.
38589  *
38590  * Fork - LGPL
38591  * <script type="text/javascript">
38592  */
38593  
38594 /**
38595  * @class Roo.menu.CheckItem
38596  * @extends Roo.menu.Item
38597  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
38598  * @constructor
38599  * Creates a new CheckItem
38600  * @param {Object} config Configuration options
38601  */
38602 Roo.menu.CheckItem = function(config){
38603     Roo.menu.CheckItem.superclass.constructor.call(this, config);
38604     this.addEvents({
38605         /**
38606          * @event beforecheckchange
38607          * Fires before the checked value is set, providing an opportunity to cancel if needed
38608          * @param {Roo.menu.CheckItem} this
38609          * @param {Boolean} checked The new checked value that will be set
38610          */
38611         "beforecheckchange" : true,
38612         /**
38613          * @event checkchange
38614          * Fires after the checked value has been set
38615          * @param {Roo.menu.CheckItem} this
38616          * @param {Boolean} checked The checked value that was set
38617          */
38618         "checkchange" : true
38619     });
38620     if(this.checkHandler){
38621         this.on('checkchange', this.checkHandler, this.scope);
38622     }
38623 };
38624 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
38625     /**
38626      * @cfg {String} group
38627      * All check items with the same group name will automatically be grouped into a single-select
38628      * radio button group (defaults to '')
38629      */
38630     /**
38631      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
38632      */
38633     itemCls : "x-menu-item x-menu-check-item",
38634     /**
38635      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
38636      */
38637     groupClass : "x-menu-group-item",
38638
38639     /**
38640      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
38641      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
38642      * initialized with checked = true will be rendered as checked.
38643      */
38644     checked: false,
38645
38646     // private
38647     ctype: "Roo.menu.CheckItem",
38648
38649     // private
38650     onRender : function(c){
38651         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
38652         if(this.group){
38653             this.el.addClass(this.groupClass);
38654         }
38655         Roo.menu.MenuMgr.registerCheckable(this);
38656         if(this.checked){
38657             this.checked = false;
38658             this.setChecked(true, true);
38659         }
38660     },
38661
38662     // private
38663     destroy : function(){
38664         if(this.rendered){
38665             Roo.menu.MenuMgr.unregisterCheckable(this);
38666         }
38667         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
38668     },
38669
38670     /**
38671      * Set the checked state of this item
38672      * @param {Boolean} checked The new checked value
38673      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
38674      */
38675     setChecked : function(state, suppressEvent){
38676         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
38677             if(this.container){
38678                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
38679             }
38680             this.checked = state;
38681             if(suppressEvent !== true){
38682                 this.fireEvent("checkchange", this, state);
38683             }
38684         }
38685     },
38686
38687     // private
38688     handleClick : function(e){
38689        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
38690            this.setChecked(!this.checked);
38691        }
38692        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
38693     }
38694 });/*
38695  * Based on:
38696  * Ext JS Library 1.1.1
38697  * Copyright(c) 2006-2007, Ext JS, LLC.
38698  *
38699  * Originally Released Under LGPL - original licence link has changed is not relivant.
38700  *
38701  * Fork - LGPL
38702  * <script type="text/javascript">
38703  */
38704  
38705 /**
38706  * @class Roo.menu.DateItem
38707  * @extends Roo.menu.Adapter
38708  * A menu item that wraps the {@link Roo.DatPicker} component.
38709  * @constructor
38710  * Creates a new DateItem
38711  * @param {Object} config Configuration options
38712  */
38713 Roo.menu.DateItem = function(config){
38714     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
38715     /** The Roo.DatePicker object @type Roo.DatePicker */
38716     this.picker = this.component;
38717     this.addEvents({select: true});
38718     
38719     this.picker.on("render", function(picker){
38720         picker.getEl().swallowEvent("click");
38721         picker.container.addClass("x-menu-date-item");
38722     });
38723
38724     this.picker.on("select", this.onSelect, this);
38725 };
38726
38727 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
38728     // private
38729     onSelect : function(picker, date){
38730         this.fireEvent("select", this, date, picker);
38731         Roo.menu.DateItem.superclass.handleClick.call(this);
38732     }
38733 });/*
38734  * Based on:
38735  * Ext JS Library 1.1.1
38736  * Copyright(c) 2006-2007, Ext JS, LLC.
38737  *
38738  * Originally Released Under LGPL - original licence link has changed is not relivant.
38739  *
38740  * Fork - LGPL
38741  * <script type="text/javascript">
38742  */
38743  
38744 /**
38745  * @class Roo.menu.ColorItem
38746  * @extends Roo.menu.Adapter
38747  * A menu item that wraps the {@link Roo.ColorPalette} component.
38748  * @constructor
38749  * Creates a new ColorItem
38750  * @param {Object} config Configuration options
38751  */
38752 Roo.menu.ColorItem = function(config){
38753     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
38754     /** The Roo.ColorPalette object @type Roo.ColorPalette */
38755     this.palette = this.component;
38756     this.relayEvents(this.palette, ["select"]);
38757     if(this.selectHandler){
38758         this.on('select', this.selectHandler, this.scope);
38759     }
38760 };
38761 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
38762  * Based on:
38763  * Ext JS Library 1.1.1
38764  * Copyright(c) 2006-2007, Ext JS, LLC.
38765  *
38766  * Originally Released Under LGPL - original licence link has changed is not relivant.
38767  *
38768  * Fork - LGPL
38769  * <script type="text/javascript">
38770  */
38771  
38772
38773 /**
38774  * @class Roo.menu.DateMenu
38775  * @extends Roo.menu.Menu
38776  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
38777  * @constructor
38778  * Creates a new DateMenu
38779  * @param {Object} config Configuration options
38780  */
38781 Roo.menu.DateMenu = function(config){
38782     Roo.menu.DateMenu.superclass.constructor.call(this, config);
38783     this.plain = true;
38784     var di = new Roo.menu.DateItem(config);
38785     this.add(di);
38786     /**
38787      * The {@link Roo.DatePicker} instance for this DateMenu
38788      * @type DatePicker
38789      */
38790     this.picker = di.picker;
38791     /**
38792      * @event select
38793      * @param {DatePicker} picker
38794      * @param {Date} date
38795      */
38796     this.relayEvents(di, ["select"]);
38797     this.on('beforeshow', function(){
38798         if(this.picker){
38799             this.picker.hideMonthPicker(false);
38800         }
38801     }, this);
38802 };
38803 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
38804     cls:'x-date-menu'
38805 });/*
38806  * Based on:
38807  * Ext JS Library 1.1.1
38808  * Copyright(c) 2006-2007, Ext JS, LLC.
38809  *
38810  * Originally Released Under LGPL - original licence link has changed is not relivant.
38811  *
38812  * Fork - LGPL
38813  * <script type="text/javascript">
38814  */
38815  
38816
38817 /**
38818  * @class Roo.menu.ColorMenu
38819  * @extends Roo.menu.Menu
38820  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
38821  * @constructor
38822  * Creates a new ColorMenu
38823  * @param {Object} config Configuration options
38824  */
38825 Roo.menu.ColorMenu = function(config){
38826     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
38827     this.plain = true;
38828     var ci = new Roo.menu.ColorItem(config);
38829     this.add(ci);
38830     /**
38831      * The {@link Roo.ColorPalette} instance for this ColorMenu
38832      * @type ColorPalette
38833      */
38834     this.palette = ci.palette;
38835     /**
38836      * @event select
38837      * @param {ColorPalette} palette
38838      * @param {String} color
38839      */
38840     this.relayEvents(ci, ["select"]);
38841 };
38842 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
38843  * Based on:
38844  * Ext JS Library 1.1.1
38845  * Copyright(c) 2006-2007, Ext JS, LLC.
38846  *
38847  * Originally Released Under LGPL - original licence link has changed is not relivant.
38848  *
38849  * Fork - LGPL
38850  * <script type="text/javascript">
38851  */
38852  
38853 /**
38854  * @class Roo.form.TextItem
38855  * @extends Roo.BoxComponent
38856  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
38857  * @constructor
38858  * Creates a new TextItem
38859  * @param {Object} config Configuration options
38860  */
38861 Roo.form.TextItem = function(config){
38862     Roo.form.TextItem.superclass.constructor.call(this, config);
38863 };
38864
38865 Roo.extend(Roo.form.TextItem, Roo.BoxComponent,  {
38866     
38867     /**
38868      * @cfg {String} tag the tag for this item (default div)
38869      */
38870     tag : 'div',
38871     /**
38872      * @cfg {String} html the content for this item
38873      */
38874     html : '',
38875     
38876     getAutoCreate : function()
38877     {
38878         var cfg = {
38879             id: this.id,
38880             tag: this.tag,
38881             html: this.html,
38882             cls: 'x-form-item'
38883         };
38884         
38885         return cfg;
38886         
38887     },
38888     
38889     onRender : function(ct, position)
38890     {
38891         Roo.form.TextItem.superclass.onRender.call(this, ct, position);
38892         
38893         if(!this.el){
38894             var cfg = this.getAutoCreate();
38895             if(!cfg.name){
38896                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
38897             }
38898             if (!cfg.name.length) {
38899                 delete cfg.name;
38900             }
38901             this.el = ct.createChild(cfg, position);
38902         }
38903     },
38904     /*
38905      * setHTML
38906      * @param {String} html update the Contents of the element.
38907      */
38908     setHTML : function(html)
38909     {
38910         this.fieldEl.dom.innerHTML = html;
38911     }
38912     
38913 });/*
38914  * Based on:
38915  * Ext JS Library 1.1.1
38916  * Copyright(c) 2006-2007, Ext JS, LLC.
38917  *
38918  * Originally Released Under LGPL - original licence link has changed is not relivant.
38919  *
38920  * Fork - LGPL
38921  * <script type="text/javascript">
38922  */
38923  
38924 /**
38925  * @class Roo.form.Field
38926  * @extends Roo.BoxComponent
38927  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
38928  * @constructor
38929  * Creates a new Field
38930  * @param {Object} config Configuration options
38931  */
38932 Roo.form.Field = function(config){
38933     Roo.form.Field.superclass.constructor.call(this, config);
38934 };
38935
38936 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
38937     /**
38938      * @cfg {String} fieldLabel Label to use when rendering a form.
38939      */
38940        /**
38941      * @cfg {String} qtip Mouse over tip
38942      */
38943      
38944     /**
38945      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
38946      */
38947     invalidClass : "x-form-invalid",
38948     /**
38949      * @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")
38950      */
38951     invalidText : "The value in this field is invalid",
38952     /**
38953      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
38954      */
38955     focusClass : "x-form-focus",
38956     /**
38957      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
38958       automatic validation (defaults to "keyup").
38959      */
38960     validationEvent : "keyup",
38961     /**
38962      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
38963      */
38964     validateOnBlur : true,
38965     /**
38966      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
38967      */
38968     validationDelay : 250,
38969     /**
38970      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
38971      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
38972      */
38973     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
38974     /**
38975      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
38976      */
38977     fieldClass : "x-form-field",
38978     /**
38979      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
38980      *<pre>
38981 Value         Description
38982 -----------   ----------------------------------------------------------------------
38983 qtip          Display a quick tip when the user hovers over the field
38984 title         Display a default browser title attribute popup
38985 under         Add a block div beneath the field containing the error text
38986 side          Add an error icon to the right of the field with a popup on hover
38987 [element id]  Add the error text directly to the innerHTML of the specified element
38988 </pre>
38989      */
38990     msgTarget : 'qtip',
38991     /**
38992      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
38993      */
38994     msgFx : 'normal',
38995
38996     /**
38997      * @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.
38998      */
38999     readOnly : false,
39000
39001     /**
39002      * @cfg {Boolean} disabled True to disable the field (defaults to false).
39003      */
39004     disabled : false,
39005
39006     /**
39007      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
39008      */
39009     inputType : undefined,
39010     
39011     /**
39012      * @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).
39013          */
39014         tabIndex : undefined,
39015         
39016     // private
39017     isFormField : true,
39018
39019     // private
39020     hasFocus : false,
39021     /**
39022      * @property {Roo.Element} fieldEl
39023      * Element Containing the rendered Field (with label etc.)
39024      */
39025     /**
39026      * @cfg {Mixed} value A value to initialize this field with.
39027      */
39028     value : undefined,
39029
39030     /**
39031      * @cfg {String} name The field's HTML name attribute.
39032      */
39033     /**
39034      * @cfg {String} cls A CSS class to apply to the field's underlying element.
39035      */
39036     // private
39037     loadedValue : false,
39038      
39039      
39040         // private ??
39041         initComponent : function(){
39042         Roo.form.Field.superclass.initComponent.call(this);
39043         this.addEvents({
39044             /**
39045              * @event focus
39046              * Fires when this field receives input focus.
39047              * @param {Roo.form.Field} this
39048              */
39049             focus : true,
39050             /**
39051              * @event blur
39052              * Fires when this field loses input focus.
39053              * @param {Roo.form.Field} this
39054              */
39055             blur : true,
39056             /**
39057              * @event specialkey
39058              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
39059              * {@link Roo.EventObject#getKey} to determine which key was pressed.
39060              * @param {Roo.form.Field} this
39061              * @param {Roo.EventObject} e The event object
39062              */
39063             specialkey : true,
39064             /**
39065              * @event change
39066              * Fires just before the field blurs if the field value has changed.
39067              * @param {Roo.form.Field} this
39068              * @param {Mixed} newValue The new value
39069              * @param {Mixed} oldValue The original value
39070              */
39071             change : true,
39072             /**
39073              * @event invalid
39074              * Fires after the field has been marked as invalid.
39075              * @param {Roo.form.Field} this
39076              * @param {String} msg The validation message
39077              */
39078             invalid : true,
39079             /**
39080              * @event valid
39081              * Fires after the field has been validated with no errors.
39082              * @param {Roo.form.Field} this
39083              */
39084             valid : true,
39085              /**
39086              * @event keyup
39087              * Fires after the key up
39088              * @param {Roo.form.Field} this
39089              * @param {Roo.EventObject}  e The event Object
39090              */
39091             keyup : true
39092         });
39093     },
39094
39095     /**
39096      * Returns the name attribute of the field if available
39097      * @return {String} name The field name
39098      */
39099     getName: function(){
39100          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
39101     },
39102
39103     // private
39104     onRender : function(ct, position){
39105         Roo.form.Field.superclass.onRender.call(this, ct, position);
39106         if(!this.el){
39107             var cfg = this.getAutoCreate();
39108             if(!cfg.name){
39109                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
39110             }
39111             if (!cfg.name.length) {
39112                 delete cfg.name;
39113             }
39114             if(this.inputType){
39115                 cfg.type = this.inputType;
39116             }
39117             this.el = ct.createChild(cfg, position);
39118         }
39119         var type = this.el.dom.type;
39120         if(type){
39121             if(type == 'password'){
39122                 type = 'text';
39123             }
39124             this.el.addClass('x-form-'+type);
39125         }
39126         if(this.readOnly){
39127             this.el.dom.readOnly = true;
39128         }
39129         if(this.tabIndex !== undefined){
39130             this.el.dom.setAttribute('tabIndex', this.tabIndex);
39131         }
39132
39133         this.el.addClass([this.fieldClass, this.cls]);
39134         this.initValue();
39135     },
39136
39137     /**
39138      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
39139      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
39140      * @return {Roo.form.Field} this
39141      */
39142     applyTo : function(target){
39143         this.allowDomMove = false;
39144         this.el = Roo.get(target);
39145         this.render(this.el.dom.parentNode);
39146         return this;
39147     },
39148
39149     // private
39150     initValue : function(){
39151         if(this.value !== undefined){
39152             this.setValue(this.value);
39153         }else if(this.el.dom.value.length > 0){
39154             this.setValue(this.el.dom.value);
39155         }
39156     },
39157
39158     /**
39159      * Returns true if this field has been changed since it was originally loaded and is not disabled.
39160      * DEPRICATED  - it never worked well - use hasChanged/resetHasChanged.
39161      */
39162     isDirty : function() {
39163         if(this.disabled) {
39164             return false;
39165         }
39166         return String(this.getValue()) !== String(this.originalValue);
39167     },
39168
39169     /**
39170      * stores the current value in loadedValue
39171      */
39172     resetHasChanged : function()
39173     {
39174         this.loadedValue = String(this.getValue());
39175     },
39176     /**
39177      * checks the current value against the 'loaded' value.
39178      * Note - will return false if 'resetHasChanged' has not been called first.
39179      */
39180     hasChanged : function()
39181     {
39182         if(this.disabled || this.readOnly) {
39183             return false;
39184         }
39185         return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
39186     },
39187     
39188     
39189     
39190     // private
39191     afterRender : function(){
39192         Roo.form.Field.superclass.afterRender.call(this);
39193         this.initEvents();
39194     },
39195
39196     // private
39197     fireKey : function(e){
39198         //Roo.log('field ' + e.getKey());
39199         if(e.isNavKeyPress()){
39200             this.fireEvent("specialkey", this, e);
39201         }
39202     },
39203
39204     /**
39205      * Resets the current field value to the originally loaded value and clears any validation messages
39206      */
39207     reset : function(){
39208         this.setValue(this.resetValue);
39209         this.originalValue = this.getValue();
39210         this.clearInvalid();
39211     },
39212
39213     // private
39214     initEvents : function(){
39215         // safari killled keypress - so keydown is now used..
39216         this.el.on("keydown" , this.fireKey,  this);
39217         this.el.on("focus", this.onFocus,  this);
39218         this.el.on("blur", this.onBlur,  this);
39219         this.el.relayEvent('keyup', this);
39220
39221         // reference to original value for reset
39222         this.originalValue = this.getValue();
39223         this.resetValue =  this.getValue();
39224     },
39225
39226     // private
39227     onFocus : function(){
39228         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
39229             this.el.addClass(this.focusClass);
39230         }
39231         if(!this.hasFocus){
39232             this.hasFocus = true;
39233             this.startValue = this.getValue();
39234             this.fireEvent("focus", this);
39235         }
39236     },
39237
39238     beforeBlur : Roo.emptyFn,
39239
39240     // private
39241     onBlur : function(){
39242         this.beforeBlur();
39243         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
39244             this.el.removeClass(this.focusClass);
39245         }
39246         this.hasFocus = false;
39247         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
39248             this.validate();
39249         }
39250         var v = this.getValue();
39251         if(String(v) !== String(this.startValue)){
39252             this.fireEvent('change', this, v, this.startValue);
39253         }
39254         this.fireEvent("blur", this);
39255     },
39256
39257     /**
39258      * Returns whether or not the field value is currently valid
39259      * @param {Boolean} preventMark True to disable marking the field invalid
39260      * @return {Boolean} True if the value is valid, else false
39261      */
39262     isValid : function(preventMark){
39263         if(this.disabled){
39264             return true;
39265         }
39266         var restore = this.preventMark;
39267         this.preventMark = preventMark === true;
39268         var v = this.validateValue(this.processValue(this.getRawValue()));
39269         this.preventMark = restore;
39270         return v;
39271     },
39272
39273     /**
39274      * Validates the field value
39275      * @return {Boolean} True if the value is valid, else false
39276      */
39277     validate : function(){
39278         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
39279             this.clearInvalid();
39280             return true;
39281         }
39282         return false;
39283     },
39284
39285     processValue : function(value){
39286         return value;
39287     },
39288
39289     // private
39290     // Subclasses should provide the validation implementation by overriding this
39291     validateValue : function(value){
39292         return true;
39293     },
39294
39295     /**
39296      * Mark this field as invalid
39297      * @param {String} msg The validation message
39298      */
39299     markInvalid : function(msg){
39300         if(!this.rendered || this.preventMark){ // not rendered
39301             return;
39302         }
39303         
39304         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
39305         
39306         obj.el.addClass(this.invalidClass);
39307         msg = msg || this.invalidText;
39308         switch(this.msgTarget){
39309             case 'qtip':
39310                 obj.el.dom.qtip = msg;
39311                 obj.el.dom.qclass = 'x-form-invalid-tip';
39312                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
39313                     Roo.QuickTips.enable();
39314                 }
39315                 break;
39316             case 'title':
39317                 this.el.dom.title = msg;
39318                 break;
39319             case 'under':
39320                 if(!this.errorEl){
39321                     var elp = this.el.findParent('.x-form-element', 5, true);
39322                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
39323                     this.errorEl.setWidth(elp.getWidth(true)-20);
39324                 }
39325                 this.errorEl.update(msg);
39326                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
39327                 break;
39328             case 'side':
39329                 if(!this.errorIcon){
39330                     var elp = this.el.findParent('.x-form-element', 5, true);
39331                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
39332                 }
39333                 this.alignErrorIcon();
39334                 this.errorIcon.dom.qtip = msg;
39335                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
39336                 this.errorIcon.show();
39337                 this.on('resize', this.alignErrorIcon, this);
39338                 break;
39339             default:
39340                 var t = Roo.getDom(this.msgTarget);
39341                 t.innerHTML = msg;
39342                 t.style.display = this.msgDisplay;
39343                 break;
39344         }
39345         this.fireEvent('invalid', this, msg);
39346     },
39347
39348     // private
39349     alignErrorIcon : function(){
39350         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
39351     },
39352
39353     /**
39354      * Clear any invalid styles/messages for this field
39355      */
39356     clearInvalid : function(){
39357         if(!this.rendered || this.preventMark){ // not rendered
39358             return;
39359         }
39360         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
39361         
39362         obj.el.removeClass(this.invalidClass);
39363         switch(this.msgTarget){
39364             case 'qtip':
39365                 obj.el.dom.qtip = '';
39366                 break;
39367             case 'title':
39368                 this.el.dom.title = '';
39369                 break;
39370             case 'under':
39371                 if(this.errorEl){
39372                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
39373                 }
39374                 break;
39375             case 'side':
39376                 if(this.errorIcon){
39377                     this.errorIcon.dom.qtip = '';
39378                     this.errorIcon.hide();
39379                     this.un('resize', this.alignErrorIcon, this);
39380                 }
39381                 break;
39382             default:
39383                 var t = Roo.getDom(this.msgTarget);
39384                 t.innerHTML = '';
39385                 t.style.display = 'none';
39386                 break;
39387         }
39388         this.fireEvent('valid', this);
39389     },
39390
39391     /**
39392      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
39393      * @return {Mixed} value The field value
39394      */
39395     getRawValue : function(){
39396         var v = this.el.getValue();
39397         
39398         return v;
39399     },
39400
39401     /**
39402      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
39403      * @return {Mixed} value The field value
39404      */
39405     getValue : function(){
39406         var v = this.el.getValue();
39407          
39408         return v;
39409     },
39410
39411     /**
39412      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
39413      * @param {Mixed} value The value to set
39414      */
39415     setRawValue : function(v){
39416         return this.el.dom.value = (v === null || v === undefined ? '' : v);
39417     },
39418
39419     /**
39420      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
39421      * @param {Mixed} value The value to set
39422      */
39423     setValue : function(v){
39424         this.value = v;
39425         if(this.rendered){
39426             this.el.dom.value = (v === null || v === undefined ? '' : v);
39427              this.validate();
39428         }
39429     },
39430
39431     adjustSize : function(w, h){
39432         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
39433         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
39434         return s;
39435     },
39436
39437     adjustWidth : function(tag, w){
39438         tag = tag.toLowerCase();
39439         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
39440             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
39441                 if(tag == 'input'){
39442                     return w + 2;
39443                 }
39444                 if(tag == 'textarea'){
39445                     return w-2;
39446                 }
39447             }else if(Roo.isOpera){
39448                 if(tag == 'input'){
39449                     return w + 2;
39450                 }
39451                 if(tag == 'textarea'){
39452                     return w-2;
39453                 }
39454             }
39455         }
39456         return w;
39457     }
39458 });
39459
39460
39461 // anything other than normal should be considered experimental
39462 Roo.form.Field.msgFx = {
39463     normal : {
39464         show: function(msgEl, f){
39465             msgEl.setDisplayed('block');
39466         },
39467
39468         hide : function(msgEl, f){
39469             msgEl.setDisplayed(false).update('');
39470         }
39471     },
39472
39473     slide : {
39474         show: function(msgEl, f){
39475             msgEl.slideIn('t', {stopFx:true});
39476         },
39477
39478         hide : function(msgEl, f){
39479             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
39480         }
39481     },
39482
39483     slideRight : {
39484         show: function(msgEl, f){
39485             msgEl.fixDisplay();
39486             msgEl.alignTo(f.el, 'tl-tr');
39487             msgEl.slideIn('l', {stopFx:true});
39488         },
39489
39490         hide : function(msgEl, f){
39491             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
39492         }
39493     }
39494 };/*
39495  * Based on:
39496  * Ext JS Library 1.1.1
39497  * Copyright(c) 2006-2007, Ext JS, LLC.
39498  *
39499  * Originally Released Under LGPL - original licence link has changed is not relivant.
39500  *
39501  * Fork - LGPL
39502  * <script type="text/javascript">
39503  */
39504  
39505
39506 /**
39507  * @class Roo.form.TextField
39508  * @extends Roo.form.Field
39509  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
39510  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
39511  * @constructor
39512  * Creates a new TextField
39513  * @param {Object} config Configuration options
39514  */
39515 Roo.form.TextField = function(config){
39516     Roo.form.TextField.superclass.constructor.call(this, config);
39517     this.addEvents({
39518         /**
39519          * @event autosize
39520          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
39521          * according to the default logic, but this event provides a hook for the developer to apply additional
39522          * logic at runtime to resize the field if needed.
39523              * @param {Roo.form.Field} this This text field
39524              * @param {Number} width The new field width
39525              */
39526         autosize : true
39527     });
39528 };
39529
39530 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
39531     /**
39532      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
39533      */
39534     grow : false,
39535     /**
39536      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
39537      */
39538     growMin : 30,
39539     /**
39540      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
39541      */
39542     growMax : 800,
39543     /**
39544      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
39545      */
39546     vtype : null,
39547     /**
39548      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
39549      */
39550     maskRe : null,
39551     /**
39552      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
39553      */
39554     disableKeyFilter : false,
39555     /**
39556      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
39557      */
39558     allowBlank : true,
39559     /**
39560      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
39561      */
39562     minLength : 0,
39563     /**
39564      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
39565      */
39566     maxLength : Number.MAX_VALUE,
39567     /**
39568      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
39569      */
39570     minLengthText : "The minimum length for this field is {0}",
39571     /**
39572      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
39573      */
39574     maxLengthText : "The maximum length for this field is {0}",
39575     /**
39576      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
39577      */
39578     selectOnFocus : false,
39579     /**
39580      * @cfg {Boolean} allowLeadingSpace True to prevent the stripping of leading white space 
39581      */    
39582     allowLeadingSpace : false,
39583     /**
39584      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
39585      */
39586     blankText : "This field is required",
39587     /**
39588      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
39589      * If available, this function will be called only after the basic validators all return true, and will be passed the
39590      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
39591      */
39592     validator : null,
39593     /**
39594      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
39595      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
39596      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
39597      */
39598     regex : null,
39599     /**
39600      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
39601      */
39602     regexText : "",
39603     /**
39604      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
39605      */
39606     emptyText : null,
39607    
39608
39609     // private
39610     initEvents : function()
39611     {
39612         if (this.emptyText) {
39613             this.el.attr('placeholder', this.emptyText);
39614         }
39615         
39616         Roo.form.TextField.superclass.initEvents.call(this);
39617         if(this.validationEvent == 'keyup'){
39618             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
39619             this.el.on('keyup', this.filterValidation, this);
39620         }
39621         else if(this.validationEvent !== false){
39622             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
39623         }
39624         
39625         if(this.selectOnFocus){
39626             this.on("focus", this.preFocus, this);
39627         }
39628         if (!this.allowLeadingSpace) {
39629             this.on('blur', this.cleanLeadingSpace, this);
39630         }
39631         
39632         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
39633             this.el.on("keypress", this.filterKeys, this);
39634         }
39635         if(this.grow){
39636             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
39637             this.el.on("click", this.autoSize,  this);
39638         }
39639         if(this.el.is('input[type=password]') && Roo.isSafari){
39640             this.el.on('keydown', this.SafariOnKeyDown, this);
39641         }
39642     },
39643
39644     processValue : function(value){
39645         if(this.stripCharsRe){
39646             var newValue = value.replace(this.stripCharsRe, '');
39647             if(newValue !== value){
39648                 this.setRawValue(newValue);
39649                 return newValue;
39650             }
39651         }
39652         return value;
39653     },
39654
39655     filterValidation : function(e){
39656         if(!e.isNavKeyPress()){
39657             this.validationTask.delay(this.validationDelay);
39658         }
39659     },
39660
39661     // private
39662     onKeyUp : function(e){
39663         if(!e.isNavKeyPress()){
39664             this.autoSize();
39665         }
39666     },
39667     // private - clean the leading white space
39668     cleanLeadingSpace : function(e)
39669     {
39670         if ( this.inputType == 'file') {
39671             return;
39672         }
39673         
39674         this.setValue((this.getValue() + '').replace(/^\s+/,''));
39675     },
39676     /**
39677      * Resets the current field value to the originally-loaded value and clears any validation messages.
39678      *  
39679      */
39680     reset : function(){
39681         Roo.form.TextField.superclass.reset.call(this);
39682        
39683     }, 
39684     // private
39685     preFocus : function(){
39686         
39687         if(this.selectOnFocus){
39688             this.el.dom.select();
39689         }
39690     },
39691
39692     
39693     // private
39694     filterKeys : function(e){
39695         var k = e.getKey();
39696         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
39697             return;
39698         }
39699         var c = e.getCharCode(), cc = String.fromCharCode(c);
39700         if(Roo.isIE && (e.isSpecialKey() || !cc)){
39701             return;
39702         }
39703         if(!this.maskRe.test(cc)){
39704             e.stopEvent();
39705         }
39706     },
39707
39708     setValue : function(v){
39709         
39710         Roo.form.TextField.superclass.setValue.apply(this, arguments);
39711         
39712         this.autoSize();
39713     },
39714
39715     /**
39716      * Validates a value according to the field's validation rules and marks the field as invalid
39717      * if the validation fails
39718      * @param {Mixed} value The value to validate
39719      * @return {Boolean} True if the value is valid, else false
39720      */
39721     validateValue : function(value){
39722         if(value.length < 1)  { // if it's blank
39723              if(this.allowBlank){
39724                 this.clearInvalid();
39725                 return true;
39726              }else{
39727                 this.markInvalid(this.blankText);
39728                 return false;
39729              }
39730         }
39731         if(value.length < this.minLength){
39732             this.markInvalid(String.format(this.minLengthText, this.minLength));
39733             return false;
39734         }
39735         if(value.length > this.maxLength){
39736             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
39737             return false;
39738         }
39739         if(this.vtype){
39740             var vt = Roo.form.VTypes;
39741             if(!vt[this.vtype](value, this)){
39742                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
39743                 return false;
39744             }
39745         }
39746         if(typeof this.validator == "function"){
39747             var msg = this.validator(value);
39748             if(msg !== true){
39749                 this.markInvalid(msg);
39750                 return false;
39751             }
39752         }
39753         if(this.regex && !this.regex.test(value)){
39754             this.markInvalid(this.regexText);
39755             return false;
39756         }
39757         return true;
39758     },
39759
39760     /**
39761      * Selects text in this field
39762      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
39763      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
39764      */
39765     selectText : function(start, end){
39766         var v = this.getRawValue();
39767         if(v.length > 0){
39768             start = start === undefined ? 0 : start;
39769             end = end === undefined ? v.length : end;
39770             var d = this.el.dom;
39771             if(d.setSelectionRange){
39772                 d.setSelectionRange(start, end);
39773             }else if(d.createTextRange){
39774                 var range = d.createTextRange();
39775                 range.moveStart("character", start);
39776                 range.moveEnd("character", v.length-end);
39777                 range.select();
39778             }
39779         }
39780     },
39781
39782     /**
39783      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
39784      * This only takes effect if grow = true, and fires the autosize event.
39785      */
39786     autoSize : function(){
39787         if(!this.grow || !this.rendered){
39788             return;
39789         }
39790         if(!this.metrics){
39791             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
39792         }
39793         var el = this.el;
39794         var v = el.dom.value;
39795         var d = document.createElement('div');
39796         d.appendChild(document.createTextNode(v));
39797         v = d.innerHTML;
39798         d = null;
39799         v += "&#160;";
39800         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
39801         this.el.setWidth(w);
39802         this.fireEvent("autosize", this, w);
39803     },
39804     
39805     // private
39806     SafariOnKeyDown : function(event)
39807     {
39808         // this is a workaround for a password hang bug on chrome/ webkit.
39809         
39810         var isSelectAll = false;
39811         
39812         if(this.el.dom.selectionEnd > 0){
39813             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
39814         }
39815         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
39816             event.preventDefault();
39817             this.setValue('');
39818             return;
39819         }
39820         
39821         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
39822             
39823             event.preventDefault();
39824             // this is very hacky as keydown always get's upper case.
39825             
39826             var cc = String.fromCharCode(event.getCharCode());
39827             
39828             
39829             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
39830             
39831         }
39832         
39833         
39834     }
39835 });/*
39836  * Based on:
39837  * Ext JS Library 1.1.1
39838  * Copyright(c) 2006-2007, Ext JS, LLC.
39839  *
39840  * Originally Released Under LGPL - original licence link has changed is not relivant.
39841  *
39842  * Fork - LGPL
39843  * <script type="text/javascript">
39844  */
39845  
39846 /**
39847  * @class Roo.form.Hidden
39848  * @extends Roo.form.TextField
39849  * Simple Hidden element used on forms 
39850  * 
39851  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
39852  * 
39853  * @constructor
39854  * Creates a new Hidden form element.
39855  * @param {Object} config Configuration options
39856  */
39857
39858
39859
39860 // easy hidden field...
39861 Roo.form.Hidden = function(config){
39862     Roo.form.Hidden.superclass.constructor.call(this, config);
39863 };
39864   
39865 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
39866     fieldLabel:      '',
39867     inputType:      'hidden',
39868     width:          50,
39869     allowBlank:     true,
39870     labelSeparator: '',
39871     hidden:         true,
39872     itemCls :       'x-form-item-display-none'
39873
39874
39875 });
39876
39877
39878 /*
39879  * Based on:
39880  * Ext JS Library 1.1.1
39881  * Copyright(c) 2006-2007, Ext JS, LLC.
39882  *
39883  * Originally Released Under LGPL - original licence link has changed is not relivant.
39884  *
39885  * Fork - LGPL
39886  * <script type="text/javascript">
39887  */
39888  
39889 /**
39890  * @class Roo.form.TriggerField
39891  * @extends Roo.form.TextField
39892  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
39893  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
39894  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
39895  * for which you can provide a custom implementation.  For example:
39896  * <pre><code>
39897 var trigger = new Roo.form.TriggerField();
39898 trigger.onTriggerClick = myTriggerFn;
39899 trigger.applyTo('my-field');
39900 </code></pre>
39901  *
39902  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
39903  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
39904  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
39905  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
39906  * @constructor
39907  * Create a new TriggerField.
39908  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
39909  * to the base TextField)
39910  */
39911 Roo.form.TriggerField = function(config){
39912     this.mimicing = false;
39913     Roo.form.TriggerField.superclass.constructor.call(this, config);
39914 };
39915
39916 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
39917     /**
39918      * @cfg {String} triggerClass A CSS class to apply to the trigger
39919      */
39920     /**
39921      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
39922      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
39923      */
39924     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
39925     /**
39926      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
39927      */
39928     hideTrigger:false,
39929
39930     /** @cfg {Boolean} grow @hide */
39931     /** @cfg {Number} growMin @hide */
39932     /** @cfg {Number} growMax @hide */
39933
39934     /**
39935      * @hide 
39936      * @method
39937      */
39938     autoSize: Roo.emptyFn,
39939     // private
39940     monitorTab : true,
39941     // private
39942     deferHeight : true,
39943
39944     
39945     actionMode : 'wrap',
39946     // private
39947     onResize : function(w, h){
39948         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
39949         if(typeof w == 'number'){
39950             var x = w - this.trigger.getWidth();
39951             this.el.setWidth(this.adjustWidth('input', x));
39952             this.trigger.setStyle('left', x+'px');
39953         }
39954     },
39955
39956     // private
39957     adjustSize : Roo.BoxComponent.prototype.adjustSize,
39958
39959     // private
39960     getResizeEl : function(){
39961         return this.wrap;
39962     },
39963
39964     // private
39965     getPositionEl : function(){
39966         return this.wrap;
39967     },
39968
39969     // private
39970     alignErrorIcon : function(){
39971         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
39972     },
39973
39974     // private
39975     onRender : function(ct, position){
39976         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
39977         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
39978         this.trigger = this.wrap.createChild(this.triggerConfig ||
39979                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
39980         if(this.hideTrigger){
39981             this.trigger.setDisplayed(false);
39982         }
39983         this.initTrigger();
39984         if(!this.width){
39985             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
39986         }
39987     },
39988
39989     // private
39990     initTrigger : function(){
39991         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
39992         this.trigger.addClassOnOver('x-form-trigger-over');
39993         this.trigger.addClassOnClick('x-form-trigger-click');
39994     },
39995
39996     // private
39997     onDestroy : function(){
39998         if(this.trigger){
39999             this.trigger.removeAllListeners();
40000             this.trigger.remove();
40001         }
40002         if(this.wrap){
40003             this.wrap.remove();
40004         }
40005         Roo.form.TriggerField.superclass.onDestroy.call(this);
40006     },
40007
40008     // private
40009     onFocus : function(){
40010         Roo.form.TriggerField.superclass.onFocus.call(this);
40011         if(!this.mimicing){
40012             this.wrap.addClass('x-trigger-wrap-focus');
40013             this.mimicing = true;
40014             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
40015             if(this.monitorTab){
40016                 this.el.on("keydown", this.checkTab, this);
40017             }
40018         }
40019     },
40020
40021     // private
40022     checkTab : function(e){
40023         if(e.getKey() == e.TAB){
40024             this.triggerBlur();
40025         }
40026     },
40027
40028     // private
40029     onBlur : function(){
40030         // do nothing
40031     },
40032
40033     // private
40034     mimicBlur : function(e, t){
40035         if(!this.wrap.contains(t) && this.validateBlur()){
40036             this.triggerBlur();
40037         }
40038     },
40039
40040     // private
40041     triggerBlur : function(){
40042         this.mimicing = false;
40043         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
40044         if(this.monitorTab){
40045             this.el.un("keydown", this.checkTab, this);
40046         }
40047         this.wrap.removeClass('x-trigger-wrap-focus');
40048         Roo.form.TriggerField.superclass.onBlur.call(this);
40049     },
40050
40051     // private
40052     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
40053     validateBlur : function(e, t){
40054         return true;
40055     },
40056
40057     // private
40058     onDisable : function(){
40059         Roo.form.TriggerField.superclass.onDisable.call(this);
40060         if(this.wrap){
40061             this.wrap.addClass('x-item-disabled');
40062         }
40063     },
40064
40065     // private
40066     onEnable : function(){
40067         Roo.form.TriggerField.superclass.onEnable.call(this);
40068         if(this.wrap){
40069             this.wrap.removeClass('x-item-disabled');
40070         }
40071     },
40072
40073     // private
40074     onShow : function(){
40075         var ae = this.getActionEl();
40076         
40077         if(ae){
40078             ae.dom.style.display = '';
40079             ae.dom.style.visibility = 'visible';
40080         }
40081     },
40082
40083     // private
40084     
40085     onHide : function(){
40086         var ae = this.getActionEl();
40087         ae.dom.style.display = 'none';
40088     },
40089
40090     /**
40091      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
40092      * by an implementing function.
40093      * @method
40094      * @param {EventObject} e
40095      */
40096     onTriggerClick : Roo.emptyFn
40097 });
40098
40099 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
40100 // to be extended by an implementing class.  For an example of implementing this class, see the custom
40101 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
40102 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
40103     initComponent : function(){
40104         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
40105
40106         this.triggerConfig = {
40107             tag:'span', cls:'x-form-twin-triggers', cn:[
40108             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
40109             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
40110         ]};
40111     },
40112
40113     getTrigger : function(index){
40114         return this.triggers[index];
40115     },
40116
40117     initTrigger : function(){
40118         var ts = this.trigger.select('.x-form-trigger', true);
40119         this.wrap.setStyle('overflow', 'hidden');
40120         var triggerField = this;
40121         ts.each(function(t, all, index){
40122             t.hide = function(){
40123                 var w = triggerField.wrap.getWidth();
40124                 this.dom.style.display = 'none';
40125                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
40126             };
40127             t.show = function(){
40128                 var w = triggerField.wrap.getWidth();
40129                 this.dom.style.display = '';
40130                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
40131             };
40132             var triggerIndex = 'Trigger'+(index+1);
40133
40134             if(this['hide'+triggerIndex]){
40135                 t.dom.style.display = 'none';
40136             }
40137             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
40138             t.addClassOnOver('x-form-trigger-over');
40139             t.addClassOnClick('x-form-trigger-click');
40140         }, this);
40141         this.triggers = ts.elements;
40142     },
40143
40144     onTrigger1Click : Roo.emptyFn,
40145     onTrigger2Click : Roo.emptyFn
40146 });/*
40147  * Based on:
40148  * Ext JS Library 1.1.1
40149  * Copyright(c) 2006-2007, Ext JS, LLC.
40150  *
40151  * Originally Released Under LGPL - original licence link has changed is not relivant.
40152  *
40153  * Fork - LGPL
40154  * <script type="text/javascript">
40155  */
40156  
40157 /**
40158  * @class Roo.form.TextArea
40159  * @extends Roo.form.TextField
40160  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
40161  * support for auto-sizing.
40162  * @constructor
40163  * Creates a new TextArea
40164  * @param {Object} config Configuration options
40165  */
40166 Roo.form.TextArea = function(config){
40167     Roo.form.TextArea.superclass.constructor.call(this, config);
40168     // these are provided exchanges for backwards compat
40169     // minHeight/maxHeight were replaced by growMin/growMax to be
40170     // compatible with TextField growing config values
40171     if(this.minHeight !== undefined){
40172         this.growMin = this.minHeight;
40173     }
40174     if(this.maxHeight !== undefined){
40175         this.growMax = this.maxHeight;
40176     }
40177 };
40178
40179 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
40180     /**
40181      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
40182      */
40183     growMin : 60,
40184     /**
40185      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
40186      */
40187     growMax: 1000,
40188     /**
40189      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
40190      * in the field (equivalent to setting overflow: hidden, defaults to false)
40191      */
40192     preventScrollbars: false,
40193     /**
40194      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
40195      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
40196      */
40197
40198     // private
40199     onRender : function(ct, position){
40200         if(!this.el){
40201             this.defaultAutoCreate = {
40202                 tag: "textarea",
40203                 style:"width:300px;height:60px;",
40204                 autocomplete: "new-password"
40205             };
40206         }
40207         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
40208         if(this.grow){
40209             this.textSizeEl = Roo.DomHelper.append(document.body, {
40210                 tag: "pre", cls: "x-form-grow-sizer"
40211             });
40212             if(this.preventScrollbars){
40213                 this.el.setStyle("overflow", "hidden");
40214             }
40215             this.el.setHeight(this.growMin);
40216         }
40217     },
40218
40219     onDestroy : function(){
40220         if(this.textSizeEl){
40221             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
40222         }
40223         Roo.form.TextArea.superclass.onDestroy.call(this);
40224     },
40225
40226     // private
40227     onKeyUp : function(e){
40228         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
40229             this.autoSize();
40230         }
40231     },
40232
40233     /**
40234      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
40235      * This only takes effect if grow = true, and fires the autosize event if the height changes.
40236      */
40237     autoSize : function(){
40238         if(!this.grow || !this.textSizeEl){
40239             return;
40240         }
40241         var el = this.el;
40242         var v = el.dom.value;
40243         var ts = this.textSizeEl;
40244
40245         ts.innerHTML = '';
40246         ts.appendChild(document.createTextNode(v));
40247         v = ts.innerHTML;
40248
40249         Roo.fly(ts).setWidth(this.el.getWidth());
40250         if(v.length < 1){
40251             v = "&#160;&#160;";
40252         }else{
40253             if(Roo.isIE){
40254                 v = v.replace(/\n/g, '<p>&#160;</p>');
40255             }
40256             v += "&#160;\n&#160;";
40257         }
40258         ts.innerHTML = v;
40259         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
40260         if(h != this.lastHeight){
40261             this.lastHeight = h;
40262             this.el.setHeight(h);
40263             this.fireEvent("autosize", this, h);
40264         }
40265     }
40266 });/*
40267  * Based on:
40268  * Ext JS Library 1.1.1
40269  * Copyright(c) 2006-2007, Ext JS, LLC.
40270  *
40271  * Originally Released Under LGPL - original licence link has changed is not relivant.
40272  *
40273  * Fork - LGPL
40274  * <script type="text/javascript">
40275  */
40276  
40277
40278 /**
40279  * @class Roo.form.NumberField
40280  * @extends Roo.form.TextField
40281  * Numeric text field that provides automatic keystroke filtering and numeric validation.
40282  * @constructor
40283  * Creates a new NumberField
40284  * @param {Object} config Configuration options
40285  */
40286 Roo.form.NumberField = function(config){
40287     Roo.form.NumberField.superclass.constructor.call(this, config);
40288 };
40289
40290 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
40291     /**
40292      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
40293      */
40294     fieldClass: "x-form-field x-form-num-field",
40295     /**
40296      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40297      */
40298     allowDecimals : true,
40299     /**
40300      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40301      */
40302     decimalSeparator : ".",
40303     /**
40304      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40305      */
40306     decimalPrecision : 2,
40307     /**
40308      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40309      */
40310     allowNegative : true,
40311     /**
40312      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40313      */
40314     minValue : Number.NEGATIVE_INFINITY,
40315     /**
40316      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40317      */
40318     maxValue : Number.MAX_VALUE,
40319     /**
40320      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40321      */
40322     minText : "The minimum value for this field is {0}",
40323     /**
40324      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40325      */
40326     maxText : "The maximum value for this field is {0}",
40327     /**
40328      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40329      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40330      */
40331     nanText : "{0} is not a valid number",
40332
40333     // private
40334     initEvents : function(){
40335         Roo.form.NumberField.superclass.initEvents.call(this);
40336         var allowed = "0123456789";
40337         if(this.allowDecimals){
40338             allowed += this.decimalSeparator;
40339         }
40340         if(this.allowNegative){
40341             allowed += "-";
40342         }
40343         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40344         var keyPress = function(e){
40345             var k = e.getKey();
40346             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40347                 return;
40348             }
40349             var c = e.getCharCode();
40350             if(allowed.indexOf(String.fromCharCode(c)) === -1){
40351                 e.stopEvent();
40352             }
40353         };
40354         this.el.on("keypress", keyPress, this);
40355     },
40356
40357     // private
40358     validateValue : function(value){
40359         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
40360             return false;
40361         }
40362         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
40363              return true;
40364         }
40365         var num = this.parseValue(value);
40366         if(isNaN(num)){
40367             this.markInvalid(String.format(this.nanText, value));
40368             return false;
40369         }
40370         if(num < this.minValue){
40371             this.markInvalid(String.format(this.minText, this.minValue));
40372             return false;
40373         }
40374         if(num > this.maxValue){
40375             this.markInvalid(String.format(this.maxText, this.maxValue));
40376             return false;
40377         }
40378         return true;
40379     },
40380
40381     getValue : function(){
40382         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
40383     },
40384
40385     // private
40386     parseValue : function(value){
40387         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40388         return isNaN(value) ? '' : value;
40389     },
40390
40391     // private
40392     fixPrecision : function(value){
40393         var nan = isNaN(value);
40394         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40395             return nan ? '' : value;
40396         }
40397         return parseFloat(value).toFixed(this.decimalPrecision);
40398     },
40399
40400     setValue : function(v){
40401         v = this.fixPrecision(v);
40402         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
40403     },
40404
40405     // private
40406     decimalPrecisionFcn : function(v){
40407         return Math.floor(v);
40408     },
40409
40410     beforeBlur : function(){
40411         var v = this.parseValue(this.getRawValue());
40412         if(v){
40413             this.setValue(v);
40414         }
40415     }
40416 });/*
40417  * Based on:
40418  * Ext JS Library 1.1.1
40419  * Copyright(c) 2006-2007, Ext JS, LLC.
40420  *
40421  * Originally Released Under LGPL - original licence link has changed is not relivant.
40422  *
40423  * Fork - LGPL
40424  * <script type="text/javascript">
40425  */
40426  
40427 /**
40428  * @class Roo.form.DateField
40429  * @extends Roo.form.TriggerField
40430  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
40431 * @constructor
40432 * Create a new DateField
40433 * @param {Object} config
40434  */
40435 Roo.form.DateField = function(config)
40436 {
40437     Roo.form.DateField.superclass.constructor.call(this, config);
40438     
40439       this.addEvents({
40440          
40441         /**
40442          * @event select
40443          * Fires when a date is selected
40444              * @param {Roo.form.DateField} combo This combo box
40445              * @param {Date} date The date selected
40446              */
40447         'select' : true
40448          
40449     });
40450     
40451     
40452     if(typeof this.minValue == "string") {
40453         this.minValue = this.parseDate(this.minValue);
40454     }
40455     if(typeof this.maxValue == "string") {
40456         this.maxValue = this.parseDate(this.maxValue);
40457     }
40458     this.ddMatch = null;
40459     if(this.disabledDates){
40460         var dd = this.disabledDates;
40461         var re = "(?:";
40462         for(var i = 0; i < dd.length; i++){
40463             re += dd[i];
40464             if(i != dd.length-1) {
40465                 re += "|";
40466             }
40467         }
40468         this.ddMatch = new RegExp(re + ")");
40469     }
40470 };
40471
40472 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
40473     /**
40474      * @cfg {String} format
40475      * The default date format string which can be overriden for localization support.  The format must be
40476      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
40477      */
40478     format : "m/d/y",
40479     /**
40480      * @cfg {String} altFormats
40481      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
40482      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
40483      */
40484     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
40485     /**
40486      * @cfg {Array} disabledDays
40487      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
40488      */
40489     disabledDays : null,
40490     /**
40491      * @cfg {String} disabledDaysText
40492      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
40493      */
40494     disabledDaysText : "Disabled",
40495     /**
40496      * @cfg {Array} disabledDates
40497      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
40498      * expression so they are very powerful. Some examples:
40499      * <ul>
40500      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
40501      * <li>["03/08", "09/16"] would disable those days for every year</li>
40502      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
40503      * <li>["03/../2006"] would disable every day in March 2006</li>
40504      * <li>["^03"] would disable every day in every March</li>
40505      * </ul>
40506      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
40507      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
40508      */
40509     disabledDates : null,
40510     /**
40511      * @cfg {String} disabledDatesText
40512      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
40513      */
40514     disabledDatesText : "Disabled",
40515     /**
40516      * @cfg {Date/String} minValue
40517      * The minimum allowed date. Can be either a Javascript date object or a string date in a
40518      * valid format (defaults to null).
40519      */
40520     minValue : null,
40521     /**
40522      * @cfg {Date/String} maxValue
40523      * The maximum allowed date. Can be either a Javascript date object or a string date in a
40524      * valid format (defaults to null).
40525      */
40526     maxValue : null,
40527     /**
40528      * @cfg {String} minText
40529      * The error text to display when the date in the cell is before minValue (defaults to
40530      * 'The date in this field must be after {minValue}').
40531      */
40532     minText : "The date in this field must be equal to or after {0}",
40533     /**
40534      * @cfg {String} maxText
40535      * The error text to display when the date in the cell is after maxValue (defaults to
40536      * 'The date in this field must be before {maxValue}').
40537      */
40538     maxText : "The date in this field must be equal to or before {0}",
40539     /**
40540      * @cfg {String} invalidText
40541      * The error text to display when the date in the field is invalid (defaults to
40542      * '{value} is not a valid date - it must be in the format {format}').
40543      */
40544     invalidText : "{0} is not a valid date - it must be in the format {1}",
40545     /**
40546      * @cfg {String} triggerClass
40547      * An additional CSS class used to style the trigger button.  The trigger will always get the
40548      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
40549      * which displays a calendar icon).
40550      */
40551     triggerClass : 'x-form-date-trigger',
40552     
40553
40554     /**
40555      * @cfg {Boolean} useIso
40556      * if enabled, then the date field will use a hidden field to store the 
40557      * real value as iso formated date. default (false)
40558      */ 
40559     useIso : false,
40560     /**
40561      * @cfg {String/Object} autoCreate
40562      * A DomHelper element spec, or true for a default element spec (defaults to
40563      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
40564      */ 
40565     // private
40566     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
40567     
40568     // private
40569     hiddenField: false,
40570     
40571     onRender : function(ct, position)
40572     {
40573         Roo.form.DateField.superclass.onRender.call(this, ct, position);
40574         if (this.useIso) {
40575             //this.el.dom.removeAttribute('name'); 
40576             Roo.log("Changing name?");
40577             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
40578             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
40579                     'before', true);
40580             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
40581             // prevent input submission
40582             this.hiddenName = this.name;
40583         }
40584             
40585             
40586     },
40587     
40588     // private
40589     validateValue : function(value)
40590     {
40591         value = this.formatDate(value);
40592         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
40593             Roo.log('super failed');
40594             return false;
40595         }
40596         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
40597              return true;
40598         }
40599         var svalue = value;
40600         value = this.parseDate(value);
40601         if(!value){
40602             Roo.log('parse date failed' + svalue);
40603             this.markInvalid(String.format(this.invalidText, svalue, this.format));
40604             return false;
40605         }
40606         var time = value.getTime();
40607         if(this.minValue && time < this.minValue.getTime()){
40608             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
40609             return false;
40610         }
40611         if(this.maxValue && time > this.maxValue.getTime()){
40612             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
40613             return false;
40614         }
40615         if(this.disabledDays){
40616             var day = value.getDay();
40617             for(var i = 0; i < this.disabledDays.length; i++) {
40618                 if(day === this.disabledDays[i]){
40619                     this.markInvalid(this.disabledDaysText);
40620                     return false;
40621                 }
40622             }
40623         }
40624         var fvalue = this.formatDate(value);
40625         if(this.ddMatch && this.ddMatch.test(fvalue)){
40626             this.markInvalid(String.format(this.disabledDatesText, fvalue));
40627             return false;
40628         }
40629         return true;
40630     },
40631
40632     // private
40633     // Provides logic to override the default TriggerField.validateBlur which just returns true
40634     validateBlur : function(){
40635         return !this.menu || !this.menu.isVisible();
40636     },
40637     
40638     getName: function()
40639     {
40640         // returns hidden if it's set..
40641         if (!this.rendered) {return ''};
40642         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
40643         
40644     },
40645
40646     /**
40647      * Returns the current date value of the date field.
40648      * @return {Date} The date value
40649      */
40650     getValue : function(){
40651         
40652         return  this.hiddenField ?
40653                 this.hiddenField.value :
40654                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
40655     },
40656
40657     /**
40658      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
40659      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
40660      * (the default format used is "m/d/y").
40661      * <br />Usage:
40662      * <pre><code>
40663 //All of these calls set the same date value (May 4, 2006)
40664
40665 //Pass a date object:
40666 var dt = new Date('5/4/06');
40667 dateField.setValue(dt);
40668
40669 //Pass a date string (default format):
40670 dateField.setValue('5/4/06');
40671
40672 //Pass a date string (custom format):
40673 dateField.format = 'Y-m-d';
40674 dateField.setValue('2006-5-4');
40675 </code></pre>
40676      * @param {String/Date} date The date or valid date string
40677      */
40678     setValue : function(date){
40679         if (this.hiddenField) {
40680             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
40681         }
40682         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
40683         // make sure the value field is always stored as a date..
40684         this.value = this.parseDate(date);
40685         
40686         
40687     },
40688
40689     // private
40690     parseDate : function(value){
40691         if(!value || value instanceof Date){
40692             return value;
40693         }
40694         var v = Date.parseDate(value, this.format);
40695          if (!v && this.useIso) {
40696             v = Date.parseDate(value, 'Y-m-d');
40697         }
40698         if(!v && this.altFormats){
40699             if(!this.altFormatsArray){
40700                 this.altFormatsArray = this.altFormats.split("|");
40701             }
40702             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
40703                 v = Date.parseDate(value, this.altFormatsArray[i]);
40704             }
40705         }
40706         return v;
40707     },
40708
40709     // private
40710     formatDate : function(date, fmt){
40711         return (!date || !(date instanceof Date)) ?
40712                date : date.dateFormat(fmt || this.format);
40713     },
40714
40715     // private
40716     menuListeners : {
40717         select: function(m, d){
40718             
40719             this.setValue(d);
40720             this.fireEvent('select', this, d);
40721         },
40722         show : function(){ // retain focus styling
40723             this.onFocus();
40724         },
40725         hide : function(){
40726             this.focus.defer(10, this);
40727             var ml = this.menuListeners;
40728             this.menu.un("select", ml.select,  this);
40729             this.menu.un("show", ml.show,  this);
40730             this.menu.un("hide", ml.hide,  this);
40731         }
40732     },
40733
40734     // private
40735     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
40736     onTriggerClick : function(){
40737         if(this.disabled){
40738             return;
40739         }
40740         if(this.menu == null){
40741             this.menu = new Roo.menu.DateMenu();
40742         }
40743         Roo.apply(this.menu.picker,  {
40744             showClear: this.allowBlank,
40745             minDate : this.minValue,
40746             maxDate : this.maxValue,
40747             disabledDatesRE : this.ddMatch,
40748             disabledDatesText : this.disabledDatesText,
40749             disabledDays : this.disabledDays,
40750             disabledDaysText : this.disabledDaysText,
40751             format : this.useIso ? 'Y-m-d' : this.format,
40752             minText : String.format(this.minText, this.formatDate(this.minValue)),
40753             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
40754         });
40755         this.menu.on(Roo.apply({}, this.menuListeners, {
40756             scope:this
40757         }));
40758         this.menu.picker.setValue(this.getValue() || new Date());
40759         this.menu.show(this.el, "tl-bl?");
40760     },
40761
40762     beforeBlur : function(){
40763         var v = this.parseDate(this.getRawValue());
40764         if(v){
40765             this.setValue(v);
40766         }
40767     },
40768
40769     /*@
40770      * overide
40771      * 
40772      */
40773     isDirty : function() {
40774         if(this.disabled) {
40775             return false;
40776         }
40777         
40778         if(typeof(this.startValue) === 'undefined'){
40779             return false;
40780         }
40781         
40782         return String(this.getValue()) !== String(this.startValue);
40783         
40784     },
40785     // @overide
40786     cleanLeadingSpace : function(e)
40787     {
40788        return;
40789     }
40790     
40791 });/*
40792  * Based on:
40793  * Ext JS Library 1.1.1
40794  * Copyright(c) 2006-2007, Ext JS, LLC.
40795  *
40796  * Originally Released Under LGPL - original licence link has changed is not relivant.
40797  *
40798  * Fork - LGPL
40799  * <script type="text/javascript">
40800  */
40801  
40802 /**
40803  * @class Roo.form.MonthField
40804  * @extends Roo.form.TriggerField
40805  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
40806 * @constructor
40807 * Create a new MonthField
40808 * @param {Object} config
40809  */
40810 Roo.form.MonthField = function(config){
40811     
40812     Roo.form.MonthField.superclass.constructor.call(this, config);
40813     
40814       this.addEvents({
40815          
40816         /**
40817          * @event select
40818          * Fires when a date is selected
40819              * @param {Roo.form.MonthFieeld} combo This combo box
40820              * @param {Date} date The date selected
40821              */
40822         'select' : true
40823          
40824     });
40825     
40826     
40827     if(typeof this.minValue == "string") {
40828         this.minValue = this.parseDate(this.minValue);
40829     }
40830     if(typeof this.maxValue == "string") {
40831         this.maxValue = this.parseDate(this.maxValue);
40832     }
40833     this.ddMatch = null;
40834     if(this.disabledDates){
40835         var dd = this.disabledDates;
40836         var re = "(?:";
40837         for(var i = 0; i < dd.length; i++){
40838             re += dd[i];
40839             if(i != dd.length-1) {
40840                 re += "|";
40841             }
40842         }
40843         this.ddMatch = new RegExp(re + ")");
40844     }
40845 };
40846
40847 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
40848     /**
40849      * @cfg {String} format
40850      * The default date format string which can be overriden for localization support.  The format must be
40851      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
40852      */
40853     format : "M Y",
40854     /**
40855      * @cfg {String} altFormats
40856      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
40857      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
40858      */
40859     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
40860     /**
40861      * @cfg {Array} disabledDays
40862      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
40863      */
40864     disabledDays : [0,1,2,3,4,5,6],
40865     /**
40866      * @cfg {String} disabledDaysText
40867      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
40868      */
40869     disabledDaysText : "Disabled",
40870     /**
40871      * @cfg {Array} disabledDates
40872      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
40873      * expression so they are very powerful. Some examples:
40874      * <ul>
40875      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
40876      * <li>["03/08", "09/16"] would disable those days for every year</li>
40877      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
40878      * <li>["03/../2006"] would disable every day in March 2006</li>
40879      * <li>["^03"] would disable every day in every March</li>
40880      * </ul>
40881      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
40882      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
40883      */
40884     disabledDates : null,
40885     /**
40886      * @cfg {String} disabledDatesText
40887      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
40888      */
40889     disabledDatesText : "Disabled",
40890     /**
40891      * @cfg {Date/String} minValue
40892      * The minimum allowed date. Can be either a Javascript date object or a string date in a
40893      * valid format (defaults to null).
40894      */
40895     minValue : null,
40896     /**
40897      * @cfg {Date/String} maxValue
40898      * The maximum allowed date. Can be either a Javascript date object or a string date in a
40899      * valid format (defaults to null).
40900      */
40901     maxValue : null,
40902     /**
40903      * @cfg {String} minText
40904      * The error text to display when the date in the cell is before minValue (defaults to
40905      * 'The date in this field must be after {minValue}').
40906      */
40907     minText : "The date in this field must be equal to or after {0}",
40908     /**
40909      * @cfg {String} maxTextf
40910      * The error text to display when the date in the cell is after maxValue (defaults to
40911      * 'The date in this field must be before {maxValue}').
40912      */
40913     maxText : "The date in this field must be equal to or before {0}",
40914     /**
40915      * @cfg {String} invalidText
40916      * The error text to display when the date in the field is invalid (defaults to
40917      * '{value} is not a valid date - it must be in the format {format}').
40918      */
40919     invalidText : "{0} is not a valid date - it must be in the format {1}",
40920     /**
40921      * @cfg {String} triggerClass
40922      * An additional CSS class used to style the trigger button.  The trigger will always get the
40923      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
40924      * which displays a calendar icon).
40925      */
40926     triggerClass : 'x-form-date-trigger',
40927     
40928
40929     /**
40930      * @cfg {Boolean} useIso
40931      * if enabled, then the date field will use a hidden field to store the 
40932      * real value as iso formated date. default (true)
40933      */ 
40934     useIso : true,
40935     /**
40936      * @cfg {String/Object} autoCreate
40937      * A DomHelper element spec, or true for a default element spec (defaults to
40938      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
40939      */ 
40940     // private
40941     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
40942     
40943     // private
40944     hiddenField: false,
40945     
40946     hideMonthPicker : false,
40947     
40948     onRender : function(ct, position)
40949     {
40950         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
40951         if (this.useIso) {
40952             this.el.dom.removeAttribute('name'); 
40953             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
40954                     'before', true);
40955             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
40956             // prevent input submission
40957             this.hiddenName = this.name;
40958         }
40959             
40960             
40961     },
40962     
40963     // private
40964     validateValue : function(value)
40965     {
40966         value = this.formatDate(value);
40967         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
40968             return false;
40969         }
40970         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
40971              return true;
40972         }
40973         var svalue = value;
40974         value = this.parseDate(value);
40975         if(!value){
40976             this.markInvalid(String.format(this.invalidText, svalue, this.format));
40977             return false;
40978         }
40979         var time = value.getTime();
40980         if(this.minValue && time < this.minValue.getTime()){
40981             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
40982             return false;
40983         }
40984         if(this.maxValue && time > this.maxValue.getTime()){
40985             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
40986             return false;
40987         }
40988         /*if(this.disabledDays){
40989             var day = value.getDay();
40990             for(var i = 0; i < this.disabledDays.length; i++) {
40991                 if(day === this.disabledDays[i]){
40992                     this.markInvalid(this.disabledDaysText);
40993                     return false;
40994                 }
40995             }
40996         }
40997         */
40998         var fvalue = this.formatDate(value);
40999         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
41000             this.markInvalid(String.format(this.disabledDatesText, fvalue));
41001             return false;
41002         }
41003         */
41004         return true;
41005     },
41006
41007     // private
41008     // Provides logic to override the default TriggerField.validateBlur which just returns true
41009     validateBlur : function(){
41010         return !this.menu || !this.menu.isVisible();
41011     },
41012
41013     /**
41014      * Returns the current date value of the date field.
41015      * @return {Date} The date value
41016      */
41017     getValue : function(){
41018         
41019         
41020         
41021         return  this.hiddenField ?
41022                 this.hiddenField.value :
41023                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
41024     },
41025
41026     /**
41027      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
41028      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
41029      * (the default format used is "m/d/y").
41030      * <br />Usage:
41031      * <pre><code>
41032 //All of these calls set the same date value (May 4, 2006)
41033
41034 //Pass a date object:
41035 var dt = new Date('5/4/06');
41036 monthField.setValue(dt);
41037
41038 //Pass a date string (default format):
41039 monthField.setValue('5/4/06');
41040
41041 //Pass a date string (custom format):
41042 monthField.format = 'Y-m-d';
41043 monthField.setValue('2006-5-4');
41044 </code></pre>
41045      * @param {String/Date} date The date or valid date string
41046      */
41047     setValue : function(date){
41048         Roo.log('month setValue' + date);
41049         // can only be first of month..
41050         
41051         var val = this.parseDate(date);
41052         
41053         if (this.hiddenField) {
41054             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
41055         }
41056         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
41057         this.value = this.parseDate(date);
41058     },
41059
41060     // private
41061     parseDate : function(value){
41062         if(!value || value instanceof Date){
41063             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
41064             return value;
41065         }
41066         var v = Date.parseDate(value, this.format);
41067         if (!v && this.useIso) {
41068             v = Date.parseDate(value, 'Y-m-d');
41069         }
41070         if (v) {
41071             // 
41072             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
41073         }
41074         
41075         
41076         if(!v && this.altFormats){
41077             if(!this.altFormatsArray){
41078                 this.altFormatsArray = this.altFormats.split("|");
41079             }
41080             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
41081                 v = Date.parseDate(value, this.altFormatsArray[i]);
41082             }
41083         }
41084         return v;
41085     },
41086
41087     // private
41088     formatDate : function(date, fmt){
41089         return (!date || !(date instanceof Date)) ?
41090                date : date.dateFormat(fmt || this.format);
41091     },
41092
41093     // private
41094     menuListeners : {
41095         select: function(m, d){
41096             this.setValue(d);
41097             this.fireEvent('select', this, d);
41098         },
41099         show : function(){ // retain focus styling
41100             this.onFocus();
41101         },
41102         hide : function(){
41103             this.focus.defer(10, this);
41104             var ml = this.menuListeners;
41105             this.menu.un("select", ml.select,  this);
41106             this.menu.un("show", ml.show,  this);
41107             this.menu.un("hide", ml.hide,  this);
41108         }
41109     },
41110     // private
41111     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
41112     onTriggerClick : function(){
41113         if(this.disabled){
41114             return;
41115         }
41116         if(this.menu == null){
41117             this.menu = new Roo.menu.DateMenu();
41118            
41119         }
41120         
41121         Roo.apply(this.menu.picker,  {
41122             
41123             showClear: this.allowBlank,
41124             minDate : this.minValue,
41125             maxDate : this.maxValue,
41126             disabledDatesRE : this.ddMatch,
41127             disabledDatesText : this.disabledDatesText,
41128             
41129             format : this.useIso ? 'Y-m-d' : this.format,
41130             minText : String.format(this.minText, this.formatDate(this.minValue)),
41131             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
41132             
41133         });
41134          this.menu.on(Roo.apply({}, this.menuListeners, {
41135             scope:this
41136         }));
41137        
41138         
41139         var m = this.menu;
41140         var p = m.picker;
41141         
41142         // hide month picker get's called when we called by 'before hide';
41143         
41144         var ignorehide = true;
41145         p.hideMonthPicker  = function(disableAnim){
41146             if (ignorehide) {
41147                 return;
41148             }
41149              if(this.monthPicker){
41150                 Roo.log("hideMonthPicker called");
41151                 if(disableAnim === true){
41152                     this.monthPicker.hide();
41153                 }else{
41154                     this.monthPicker.slideOut('t', {duration:.2});
41155                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
41156                     p.fireEvent("select", this, this.value);
41157                     m.hide();
41158                 }
41159             }
41160         }
41161         
41162         Roo.log('picker set value');
41163         Roo.log(this.getValue());
41164         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
41165         m.show(this.el, 'tl-bl?');
41166         ignorehide  = false;
41167         // this will trigger hideMonthPicker..
41168         
41169         
41170         // hidden the day picker
41171         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
41172         
41173         
41174         
41175       
41176         
41177         p.showMonthPicker.defer(100, p);
41178     
41179         
41180        
41181     },
41182
41183     beforeBlur : function(){
41184         var v = this.parseDate(this.getRawValue());
41185         if(v){
41186             this.setValue(v);
41187         }
41188     }
41189
41190     /** @cfg {Boolean} grow @hide */
41191     /** @cfg {Number} growMin @hide */
41192     /** @cfg {Number} growMax @hide */
41193     /**
41194      * @hide
41195      * @method autoSize
41196      */
41197 });/*
41198  * Based on:
41199  * Ext JS Library 1.1.1
41200  * Copyright(c) 2006-2007, Ext JS, LLC.
41201  *
41202  * Originally Released Under LGPL - original licence link has changed is not relivant.
41203  *
41204  * Fork - LGPL
41205  * <script type="text/javascript">
41206  */
41207  
41208
41209 /**
41210  * @class Roo.form.ComboBox
41211  * @extends Roo.form.TriggerField
41212  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
41213  * @constructor
41214  * Create a new ComboBox.
41215  * @param {Object} config Configuration options
41216  */
41217 Roo.form.ComboBox = function(config){
41218     Roo.form.ComboBox.superclass.constructor.call(this, config);
41219     this.addEvents({
41220         /**
41221          * @event expand
41222          * Fires when the dropdown list is expanded
41223              * @param {Roo.form.ComboBox} combo This combo box
41224              */
41225         'expand' : true,
41226         /**
41227          * @event collapse
41228          * Fires when the dropdown list is collapsed
41229              * @param {Roo.form.ComboBox} combo This combo box
41230              */
41231         'collapse' : true,
41232         /**
41233          * @event beforeselect
41234          * Fires before a list item is selected. Return false to cancel the selection.
41235              * @param {Roo.form.ComboBox} combo This combo box
41236              * @param {Roo.data.Record} record The data record returned from the underlying store
41237              * @param {Number} index The index of the selected item in the dropdown list
41238              */
41239         'beforeselect' : true,
41240         /**
41241          * @event select
41242          * Fires when a list item is selected
41243              * @param {Roo.form.ComboBox} combo This combo box
41244              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
41245              * @param {Number} index The index of the selected item in the dropdown list
41246              */
41247         'select' : true,
41248         /**
41249          * @event beforequery
41250          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
41251          * The event object passed has these properties:
41252              * @param {Roo.form.ComboBox} combo This combo box
41253              * @param {String} query The query
41254              * @param {Boolean} forceAll true to force "all" query
41255              * @param {Boolean} cancel true to cancel the query
41256              * @param {Object} e The query event object
41257              */
41258         'beforequery': true,
41259          /**
41260          * @event add
41261          * Fires when the 'add' icon is pressed (add a listener to enable add button)
41262              * @param {Roo.form.ComboBox} combo This combo box
41263              */
41264         'add' : true,
41265         /**
41266          * @event edit
41267          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
41268              * @param {Roo.form.ComboBox} combo This combo box
41269              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
41270              */
41271         'edit' : true
41272         
41273         
41274     });
41275     if(this.transform){
41276         this.allowDomMove = false;
41277         var s = Roo.getDom(this.transform);
41278         if(!this.hiddenName){
41279             this.hiddenName = s.name;
41280         }
41281         if(!this.store){
41282             this.mode = 'local';
41283             var d = [], opts = s.options;
41284             for(var i = 0, len = opts.length;i < len; i++){
41285                 var o = opts[i];
41286                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
41287                 if(o.selected) {
41288                     this.value = value;
41289                 }
41290                 d.push([value, o.text]);
41291             }
41292             this.store = new Roo.data.SimpleStore({
41293                 'id': 0,
41294                 fields: ['value', 'text'],
41295                 data : d
41296             });
41297             this.valueField = 'value';
41298             this.displayField = 'text';
41299         }
41300         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
41301         if(!this.lazyRender){
41302             this.target = true;
41303             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
41304             s.parentNode.removeChild(s); // remove it
41305             this.render(this.el.parentNode);
41306         }else{
41307             s.parentNode.removeChild(s); // remove it
41308         }
41309
41310     }
41311     if (this.store) {
41312         this.store = Roo.factory(this.store, Roo.data);
41313     }
41314     
41315     this.selectedIndex = -1;
41316     if(this.mode == 'local'){
41317         if(config.queryDelay === undefined){
41318             this.queryDelay = 10;
41319         }
41320         if(config.minChars === undefined){
41321             this.minChars = 0;
41322         }
41323     }
41324 };
41325
41326 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
41327     /**
41328      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
41329      */
41330     /**
41331      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
41332      * rendering into an Roo.Editor, defaults to false)
41333      */
41334     /**
41335      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
41336      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
41337      */
41338     /**
41339      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
41340      */
41341     /**
41342      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
41343      * the dropdown list (defaults to undefined, with no header element)
41344      */
41345
41346      /**
41347      * @cfg {String/Roo.Template} tpl The template to use to render the output
41348      */
41349      
41350     // private
41351     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
41352     /**
41353      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
41354      */
41355     listWidth: undefined,
41356     /**
41357      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
41358      * mode = 'remote' or 'text' if mode = 'local')
41359      */
41360     displayField: undefined,
41361     /**
41362      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
41363      * mode = 'remote' or 'value' if mode = 'local'). 
41364      * Note: use of a valueField requires the user make a selection
41365      * in order for a value to be mapped.
41366      */
41367     valueField: undefined,
41368     
41369     
41370     /**
41371      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
41372      * field's data value (defaults to the underlying DOM element's name)
41373      */
41374     hiddenName: undefined,
41375     /**
41376      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
41377      */
41378     listClass: '',
41379     /**
41380      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
41381      */
41382     selectedClass: 'x-combo-selected',
41383     /**
41384      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
41385      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
41386      * which displays a downward arrow icon).
41387      */
41388     triggerClass : 'x-form-arrow-trigger',
41389     /**
41390      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
41391      */
41392     shadow:'sides',
41393     /**
41394      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
41395      * anchor positions (defaults to 'tl-bl')
41396      */
41397     listAlign: 'tl-bl?',
41398     /**
41399      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
41400      */
41401     maxHeight: 300,
41402     /**
41403      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
41404      * query specified by the allQuery config option (defaults to 'query')
41405      */
41406     triggerAction: 'query',
41407     /**
41408      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
41409      * (defaults to 4, does not apply if editable = false)
41410      */
41411     minChars : 4,
41412     /**
41413      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
41414      * delay (typeAheadDelay) if it matches a known value (defaults to false)
41415      */
41416     typeAhead: false,
41417     /**
41418      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
41419      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
41420      */
41421     queryDelay: 500,
41422     /**
41423      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
41424      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
41425      */
41426     pageSize: 0,
41427     /**
41428      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
41429      * when editable = true (defaults to false)
41430      */
41431     selectOnFocus:false,
41432     /**
41433      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
41434      */
41435     queryParam: 'query',
41436     /**
41437      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
41438      * when mode = 'remote' (defaults to 'Loading...')
41439      */
41440     loadingText: 'Loading...',
41441     /**
41442      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
41443      */
41444     resizable: false,
41445     /**
41446      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
41447      */
41448     handleHeight : 8,
41449     /**
41450      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
41451      * traditional select (defaults to true)
41452      */
41453     editable: true,
41454     /**
41455      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
41456      */
41457     allQuery: '',
41458     /**
41459      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
41460      */
41461     mode: 'remote',
41462     /**
41463      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
41464      * listWidth has a higher value)
41465      */
41466     minListWidth : 70,
41467     /**
41468      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
41469      * allow the user to set arbitrary text into the field (defaults to false)
41470      */
41471     forceSelection:false,
41472     /**
41473      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
41474      * if typeAhead = true (defaults to 250)
41475      */
41476     typeAheadDelay : 250,
41477     /**
41478      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
41479      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
41480      */
41481     valueNotFoundText : undefined,
41482     /**
41483      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
41484      */
41485     blockFocus : false,
41486     
41487     /**
41488      * @cfg {Boolean} disableClear Disable showing of clear button.
41489      */
41490     disableClear : false,
41491     /**
41492      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
41493      */
41494     alwaysQuery : false,
41495     
41496     //private
41497     addicon : false,
41498     editicon: false,
41499     
41500     // element that contains real text value.. (when hidden is used..)
41501      
41502     // private
41503     onRender : function(ct, position)
41504     {
41505         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
41506         
41507         if(this.hiddenName){
41508             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
41509                     'before', true);
41510             this.hiddenField.value =
41511                 this.hiddenValue !== undefined ? this.hiddenValue :
41512                 this.value !== undefined ? this.value : '';
41513
41514             // prevent input submission
41515             this.el.dom.removeAttribute('name');
41516              
41517              
41518         }
41519         
41520         if(Roo.isGecko){
41521             this.el.dom.setAttribute('autocomplete', 'off');
41522         }
41523
41524         var cls = 'x-combo-list';
41525
41526         this.list = new Roo.Layer({
41527             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
41528         });
41529
41530         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
41531         this.list.setWidth(lw);
41532         this.list.swallowEvent('mousewheel');
41533         this.assetHeight = 0;
41534
41535         if(this.title){
41536             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
41537             this.assetHeight += this.header.getHeight();
41538         }
41539
41540         this.innerList = this.list.createChild({cls:cls+'-inner'});
41541         this.innerList.on('mouseover', this.onViewOver, this);
41542         this.innerList.on('mousemove', this.onViewMove, this);
41543         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
41544         
41545         if(this.allowBlank && !this.pageSize && !this.disableClear){
41546             this.footer = this.list.createChild({cls:cls+'-ft'});
41547             this.pageTb = new Roo.Toolbar(this.footer);
41548            
41549         }
41550         if(this.pageSize){
41551             this.footer = this.list.createChild({cls:cls+'-ft'});
41552             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
41553                     {pageSize: this.pageSize});
41554             
41555         }
41556         
41557         if (this.pageTb && this.allowBlank && !this.disableClear) {
41558             var _this = this;
41559             this.pageTb.add(new Roo.Toolbar.Fill(), {
41560                 cls: 'x-btn-icon x-btn-clear',
41561                 text: '&#160;',
41562                 handler: function()
41563                 {
41564                     _this.collapse();
41565                     _this.clearValue();
41566                     _this.onSelect(false, -1);
41567                 }
41568             });
41569         }
41570         if (this.footer) {
41571             this.assetHeight += this.footer.getHeight();
41572         }
41573         
41574
41575         if(!this.tpl){
41576             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
41577         }
41578
41579         this.view = new Roo.View(this.innerList, this.tpl, {
41580             singleSelect:true,
41581             store: this.store,
41582             selectedClass: this.selectedClass
41583         });
41584
41585         this.view.on('click', this.onViewClick, this);
41586
41587         this.store.on('beforeload', this.onBeforeLoad, this);
41588         this.store.on('load', this.onLoad, this);
41589         this.store.on('loadexception', this.onLoadException, this);
41590
41591         if(this.resizable){
41592             this.resizer = new Roo.Resizable(this.list,  {
41593                pinned:true, handles:'se'
41594             });
41595             this.resizer.on('resize', function(r, w, h){
41596                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
41597                 this.listWidth = w;
41598                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
41599                 this.restrictHeight();
41600             }, this);
41601             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
41602         }
41603         if(!this.editable){
41604             this.editable = true;
41605             this.setEditable(false);
41606         }  
41607         
41608         
41609         if (typeof(this.events.add.listeners) != 'undefined') {
41610             
41611             this.addicon = this.wrap.createChild(
41612                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
41613        
41614             this.addicon.on('click', function(e) {
41615                 this.fireEvent('add', this);
41616             }, this);
41617         }
41618         if (typeof(this.events.edit.listeners) != 'undefined') {
41619             
41620             this.editicon = this.wrap.createChild(
41621                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
41622             if (this.addicon) {
41623                 this.editicon.setStyle('margin-left', '40px');
41624             }
41625             this.editicon.on('click', function(e) {
41626                 
41627                 // we fire even  if inothing is selected..
41628                 this.fireEvent('edit', this, this.lastData );
41629                 
41630             }, this);
41631         }
41632         
41633         
41634         
41635     },
41636
41637     // private
41638     initEvents : function(){
41639         Roo.form.ComboBox.superclass.initEvents.call(this);
41640
41641         this.keyNav = new Roo.KeyNav(this.el, {
41642             "up" : function(e){
41643                 this.inKeyMode = true;
41644                 this.selectPrev();
41645             },
41646
41647             "down" : function(e){
41648                 if(!this.isExpanded()){
41649                     this.onTriggerClick();
41650                 }else{
41651                     this.inKeyMode = true;
41652                     this.selectNext();
41653                 }
41654             },
41655
41656             "enter" : function(e){
41657                 this.onViewClick();
41658                 //return true;
41659             },
41660
41661             "esc" : function(e){
41662                 this.collapse();
41663             },
41664
41665             "tab" : function(e){
41666                 this.onViewClick(false);
41667                 this.fireEvent("specialkey", this, e);
41668                 return true;
41669             },
41670
41671             scope : this,
41672
41673             doRelay : function(foo, bar, hname){
41674                 if(hname == 'down' || this.scope.isExpanded()){
41675                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
41676                 }
41677                 return true;
41678             },
41679
41680             forceKeyDown: true
41681         });
41682         this.queryDelay = Math.max(this.queryDelay || 10,
41683                 this.mode == 'local' ? 10 : 250);
41684         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
41685         if(this.typeAhead){
41686             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
41687         }
41688         if(this.editable !== false){
41689             this.el.on("keyup", this.onKeyUp, this);
41690         }
41691         if(this.forceSelection){
41692             this.on('blur', this.doForce, this);
41693         }
41694     },
41695
41696     onDestroy : function(){
41697         if(this.view){
41698             this.view.setStore(null);
41699             this.view.el.removeAllListeners();
41700             this.view.el.remove();
41701             this.view.purgeListeners();
41702         }
41703         if(this.list){
41704             this.list.destroy();
41705         }
41706         if(this.store){
41707             this.store.un('beforeload', this.onBeforeLoad, this);
41708             this.store.un('load', this.onLoad, this);
41709             this.store.un('loadexception', this.onLoadException, this);
41710         }
41711         Roo.form.ComboBox.superclass.onDestroy.call(this);
41712     },
41713
41714     // private
41715     fireKey : function(e){
41716         if(e.isNavKeyPress() && !this.list.isVisible()){
41717             this.fireEvent("specialkey", this, e);
41718         }
41719     },
41720
41721     // private
41722     onResize: function(w, h){
41723         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
41724         
41725         if(typeof w != 'number'){
41726             // we do not handle it!?!?
41727             return;
41728         }
41729         var tw = this.trigger.getWidth();
41730         tw += this.addicon ? this.addicon.getWidth() : 0;
41731         tw += this.editicon ? this.editicon.getWidth() : 0;
41732         var x = w - tw;
41733         this.el.setWidth( this.adjustWidth('input', x));
41734             
41735         this.trigger.setStyle('left', x+'px');
41736         
41737         if(this.list && this.listWidth === undefined){
41738             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
41739             this.list.setWidth(lw);
41740             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
41741         }
41742         
41743     
41744         
41745     },
41746
41747     /**
41748      * Allow or prevent the user from directly editing the field text.  If false is passed,
41749      * the user will only be able to select from the items defined in the dropdown list.  This method
41750      * is the runtime equivalent of setting the 'editable' config option at config time.
41751      * @param {Boolean} value True to allow the user to directly edit the field text
41752      */
41753     setEditable : function(value){
41754         if(value == this.editable){
41755             return;
41756         }
41757         this.editable = value;
41758         if(!value){
41759             this.el.dom.setAttribute('readOnly', true);
41760             this.el.on('mousedown', this.onTriggerClick,  this);
41761             this.el.addClass('x-combo-noedit');
41762         }else{
41763             this.el.dom.setAttribute('readOnly', false);
41764             this.el.un('mousedown', this.onTriggerClick,  this);
41765             this.el.removeClass('x-combo-noedit');
41766         }
41767     },
41768
41769     // private
41770     onBeforeLoad : function(){
41771         if(!this.hasFocus){
41772             return;
41773         }
41774         this.innerList.update(this.loadingText ?
41775                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
41776         this.restrictHeight();
41777         this.selectedIndex = -1;
41778     },
41779
41780     // private
41781     onLoad : function(){
41782         if(!this.hasFocus){
41783             return;
41784         }
41785         if(this.store.getCount() > 0){
41786             this.expand();
41787             this.restrictHeight();
41788             if(this.lastQuery == this.allQuery){
41789                 if(this.editable){
41790                     this.el.dom.select();
41791                 }
41792                 if(!this.selectByValue(this.value, true)){
41793                     this.select(0, true);
41794                 }
41795             }else{
41796                 this.selectNext();
41797                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
41798                     this.taTask.delay(this.typeAheadDelay);
41799                 }
41800             }
41801         }else{
41802             this.onEmptyResults();
41803         }
41804         //this.el.focus();
41805     },
41806     // private
41807     onLoadException : function()
41808     {
41809         this.collapse();
41810         Roo.log(this.store.reader.jsonData);
41811         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
41812             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
41813         }
41814         
41815         
41816     },
41817     // private
41818     onTypeAhead : function(){
41819         if(this.store.getCount() > 0){
41820             var r = this.store.getAt(0);
41821             var newValue = r.data[this.displayField];
41822             var len = newValue.length;
41823             var selStart = this.getRawValue().length;
41824             if(selStart != len){
41825                 this.setRawValue(newValue);
41826                 this.selectText(selStart, newValue.length);
41827             }
41828         }
41829     },
41830
41831     // private
41832     onSelect : function(record, index){
41833         if(this.fireEvent('beforeselect', this, record, index) !== false){
41834             this.setFromData(index > -1 ? record.data : false);
41835             this.collapse();
41836             this.fireEvent('select', this, record, index);
41837         }
41838     },
41839
41840     /**
41841      * Returns the currently selected field value or empty string if no value is set.
41842      * @return {String} value The selected value
41843      */
41844     getValue : function(){
41845         if(this.valueField){
41846             return typeof this.value != 'undefined' ? this.value : '';
41847         }
41848         return Roo.form.ComboBox.superclass.getValue.call(this);
41849     },
41850
41851     /**
41852      * Clears any text/value currently set in the field
41853      */
41854     clearValue : function(){
41855         if(this.hiddenField){
41856             this.hiddenField.value = '';
41857         }
41858         this.value = '';
41859         this.setRawValue('');
41860         this.lastSelectionText = '';
41861         
41862     },
41863
41864     /**
41865      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
41866      * will be displayed in the field.  If the value does not match the data value of an existing item,
41867      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
41868      * Otherwise the field will be blank (although the value will still be set).
41869      * @param {String} value The value to match
41870      */
41871     setValue : function(v){
41872         var text = v;
41873         if(this.valueField){
41874             var r = this.findRecord(this.valueField, v);
41875             if(r){
41876                 text = r.data[this.displayField];
41877             }else if(this.valueNotFoundText !== undefined){
41878                 text = this.valueNotFoundText;
41879             }
41880         }
41881         this.lastSelectionText = text;
41882         if(this.hiddenField){
41883             this.hiddenField.value = v;
41884         }
41885         Roo.form.ComboBox.superclass.setValue.call(this, text);
41886         this.value = v;
41887     },
41888     /**
41889      * @property {Object} the last set data for the element
41890      */
41891     
41892     lastData : false,
41893     /**
41894      * Sets the value of the field based on a object which is related to the record format for the store.
41895      * @param {Object} value the value to set as. or false on reset?
41896      */
41897     setFromData : function(o){
41898         var dv = ''; // display value
41899         var vv = ''; // value value..
41900         this.lastData = o;
41901         if (this.displayField) {
41902             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
41903         } else {
41904             // this is an error condition!!!
41905             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
41906         }
41907         
41908         if(this.valueField){
41909             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
41910         }
41911         if(this.hiddenField){
41912             this.hiddenField.value = vv;
41913             
41914             this.lastSelectionText = dv;
41915             Roo.form.ComboBox.superclass.setValue.call(this, dv);
41916             this.value = vv;
41917             return;
41918         }
41919         // no hidden field.. - we store the value in 'value', but still display
41920         // display field!!!!
41921         this.lastSelectionText = dv;
41922         Roo.form.ComboBox.superclass.setValue.call(this, dv);
41923         this.value = vv;
41924         
41925         
41926     },
41927     // private
41928     reset : function(){
41929         // overridden so that last data is reset..
41930         this.setValue(this.resetValue);
41931         this.originalValue = this.getValue();
41932         this.clearInvalid();
41933         this.lastData = false;
41934         if (this.view) {
41935             this.view.clearSelections();
41936         }
41937     },
41938     // private
41939     findRecord : function(prop, value){
41940         var record;
41941         if(this.store.getCount() > 0){
41942             this.store.each(function(r){
41943                 if(r.data[prop] == value){
41944                     record = r;
41945                     return false;
41946                 }
41947                 return true;
41948             });
41949         }
41950         return record;
41951     },
41952     
41953     getName: function()
41954     {
41955         // returns hidden if it's set..
41956         if (!this.rendered) {return ''};
41957         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
41958         
41959     },
41960     // private
41961     onViewMove : function(e, t){
41962         this.inKeyMode = false;
41963     },
41964
41965     // private
41966     onViewOver : function(e, t){
41967         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
41968             return;
41969         }
41970         var item = this.view.findItemFromChild(t);
41971         if(item){
41972             var index = this.view.indexOf(item);
41973             this.select(index, false);
41974         }
41975     },
41976
41977     // private
41978     onViewClick : function(doFocus)
41979     {
41980         var index = this.view.getSelectedIndexes()[0];
41981         var r = this.store.getAt(index);
41982         if(r){
41983             this.onSelect(r, index);
41984         }
41985         if(doFocus !== false && !this.blockFocus){
41986             this.el.focus();
41987         }
41988     },
41989
41990     // private
41991     restrictHeight : function(){
41992         this.innerList.dom.style.height = '';
41993         var inner = this.innerList.dom;
41994         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
41995         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
41996         this.list.beginUpdate();
41997         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
41998         this.list.alignTo(this.el, this.listAlign);
41999         this.list.endUpdate();
42000     },
42001
42002     // private
42003     onEmptyResults : function(){
42004         this.collapse();
42005     },
42006
42007     /**
42008      * Returns true if the dropdown list is expanded, else false.
42009      */
42010     isExpanded : function(){
42011         return this.list.isVisible();
42012     },
42013
42014     /**
42015      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
42016      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
42017      * @param {String} value The data value of the item to select
42018      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
42019      * selected item if it is not currently in view (defaults to true)
42020      * @return {Boolean} True if the value matched an item in the list, else false
42021      */
42022     selectByValue : function(v, scrollIntoView){
42023         if(v !== undefined && v !== null){
42024             var r = this.findRecord(this.valueField || this.displayField, v);
42025             if(r){
42026                 this.select(this.store.indexOf(r), scrollIntoView);
42027                 return true;
42028             }
42029         }
42030         return false;
42031     },
42032
42033     /**
42034      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
42035      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
42036      * @param {Number} index The zero-based index of the list item to select
42037      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
42038      * selected item if it is not currently in view (defaults to true)
42039      */
42040     select : function(index, scrollIntoView){
42041         this.selectedIndex = index;
42042         this.view.select(index);
42043         if(scrollIntoView !== false){
42044             var el = this.view.getNode(index);
42045             if(el){
42046                 this.innerList.scrollChildIntoView(el, false);
42047             }
42048         }
42049     },
42050
42051     // private
42052     selectNext : function(){
42053         var ct = this.store.getCount();
42054         if(ct > 0){
42055             if(this.selectedIndex == -1){
42056                 this.select(0);
42057             }else if(this.selectedIndex < ct-1){
42058                 this.select(this.selectedIndex+1);
42059             }
42060         }
42061     },
42062
42063     // private
42064     selectPrev : function(){
42065         var ct = this.store.getCount();
42066         if(ct > 0){
42067             if(this.selectedIndex == -1){
42068                 this.select(0);
42069             }else if(this.selectedIndex != 0){
42070                 this.select(this.selectedIndex-1);
42071             }
42072         }
42073     },
42074
42075     // private
42076     onKeyUp : function(e){
42077         if(this.editable !== false && !e.isSpecialKey()){
42078             this.lastKey = e.getKey();
42079             this.dqTask.delay(this.queryDelay);
42080         }
42081     },
42082
42083     // private
42084     validateBlur : function(){
42085         return !this.list || !this.list.isVisible();   
42086     },
42087
42088     // private
42089     initQuery : function(){
42090         this.doQuery(this.getRawValue());
42091     },
42092
42093     // private
42094     doForce : function(){
42095         if(this.el.dom.value.length > 0){
42096             this.el.dom.value =
42097                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
42098              
42099         }
42100     },
42101
42102     /**
42103      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
42104      * query allowing the query action to be canceled if needed.
42105      * @param {String} query The SQL query to execute
42106      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
42107      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
42108      * saved in the current store (defaults to false)
42109      */
42110     doQuery : function(q, forceAll){
42111         if(q === undefined || q === null){
42112             q = '';
42113         }
42114         var qe = {
42115             query: q,
42116             forceAll: forceAll,
42117             combo: this,
42118             cancel:false
42119         };
42120         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
42121             return false;
42122         }
42123         q = qe.query;
42124         forceAll = qe.forceAll;
42125         if(forceAll === true || (q.length >= this.minChars)){
42126             if(this.lastQuery != q || this.alwaysQuery){
42127                 this.lastQuery = q;
42128                 if(this.mode == 'local'){
42129                     this.selectedIndex = -1;
42130                     if(forceAll){
42131                         this.store.clearFilter();
42132                     }else{
42133                         this.store.filter(this.displayField, q);
42134                     }
42135                     this.onLoad();
42136                 }else{
42137                     this.store.baseParams[this.queryParam] = q;
42138                     this.store.load({
42139                         params: this.getParams(q)
42140                     });
42141                     this.expand();
42142                 }
42143             }else{
42144                 this.selectedIndex = -1;
42145                 this.onLoad();   
42146             }
42147         }
42148     },
42149
42150     // private
42151     getParams : function(q){
42152         var p = {};
42153         //p[this.queryParam] = q;
42154         if(this.pageSize){
42155             p.start = 0;
42156             p.limit = this.pageSize;
42157         }
42158         return p;
42159     },
42160
42161     /**
42162      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
42163      */
42164     collapse : function(){
42165         if(!this.isExpanded()){
42166             return;
42167         }
42168         this.list.hide();
42169         Roo.get(document).un('mousedown', this.collapseIf, this);
42170         Roo.get(document).un('mousewheel', this.collapseIf, this);
42171         if (!this.editable) {
42172             Roo.get(document).un('keydown', this.listKeyPress, this);
42173         }
42174         this.fireEvent('collapse', this);
42175     },
42176
42177     // private
42178     collapseIf : function(e){
42179         if(!e.within(this.wrap) && !e.within(this.list)){
42180             this.collapse();
42181         }
42182     },
42183
42184     /**
42185      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
42186      */
42187     expand : function(){
42188         if(this.isExpanded() || !this.hasFocus){
42189             return;
42190         }
42191         this.list.alignTo(this.el, this.listAlign);
42192         this.list.show();
42193         Roo.get(document).on('mousedown', this.collapseIf, this);
42194         Roo.get(document).on('mousewheel', this.collapseIf, this);
42195         if (!this.editable) {
42196             Roo.get(document).on('keydown', this.listKeyPress, this);
42197         }
42198         
42199         this.fireEvent('expand', this);
42200     },
42201
42202     // private
42203     // Implements the default empty TriggerField.onTriggerClick function
42204     onTriggerClick : function(){
42205         if(this.disabled){
42206             return;
42207         }
42208         if(this.isExpanded()){
42209             this.collapse();
42210             if (!this.blockFocus) {
42211                 this.el.focus();
42212             }
42213             
42214         }else {
42215             this.hasFocus = true;
42216             if(this.triggerAction == 'all') {
42217                 this.doQuery(this.allQuery, true);
42218             } else {
42219                 this.doQuery(this.getRawValue());
42220             }
42221             if (!this.blockFocus) {
42222                 this.el.focus();
42223             }
42224         }
42225     },
42226     listKeyPress : function(e)
42227     {
42228         //Roo.log('listkeypress');
42229         // scroll to first matching element based on key pres..
42230         if (e.isSpecialKey()) {
42231             return false;
42232         }
42233         var k = String.fromCharCode(e.getKey()).toUpperCase();
42234         //Roo.log(k);
42235         var match  = false;
42236         var csel = this.view.getSelectedNodes();
42237         var cselitem = false;
42238         if (csel.length) {
42239             var ix = this.view.indexOf(csel[0]);
42240             cselitem  = this.store.getAt(ix);
42241             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
42242                 cselitem = false;
42243             }
42244             
42245         }
42246         
42247         this.store.each(function(v) { 
42248             if (cselitem) {
42249                 // start at existing selection.
42250                 if (cselitem.id == v.id) {
42251                     cselitem = false;
42252                 }
42253                 return;
42254             }
42255                 
42256             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
42257                 match = this.store.indexOf(v);
42258                 return false;
42259             }
42260         }, this);
42261         
42262         if (match === false) {
42263             return true; // no more action?
42264         }
42265         // scroll to?
42266         this.view.select(match);
42267         var sn = Roo.get(this.view.getSelectedNodes()[0]);
42268         sn.scrollIntoView(sn.dom.parentNode, false);
42269     } 
42270
42271     /** 
42272     * @cfg {Boolean} grow 
42273     * @hide 
42274     */
42275     /** 
42276     * @cfg {Number} growMin 
42277     * @hide 
42278     */
42279     /** 
42280     * @cfg {Number} growMax 
42281     * @hide 
42282     */
42283     /**
42284      * @hide
42285      * @method autoSize
42286      */
42287 });/*
42288  * Copyright(c) 2010-2012, Roo J Solutions Limited
42289  *
42290  * Licence LGPL
42291  *
42292  */
42293
42294 /**
42295  * @class Roo.form.ComboBoxArray
42296  * @extends Roo.form.TextField
42297  * A facebook style adder... for lists of email / people / countries  etc...
42298  * pick multiple items from a combo box, and shows each one.
42299  *
42300  *  Fred [x]  Brian [x]  [Pick another |v]
42301  *
42302  *
42303  *  For this to work: it needs various extra information
42304  *    - normal combo problay has
42305  *      name, hiddenName
42306  *    + displayField, valueField
42307  *
42308  *    For our purpose...
42309  *
42310  *
42311  *   If we change from 'extends' to wrapping...
42312  *   
42313  *  
42314  *
42315  
42316  
42317  * @constructor
42318  * Create a new ComboBoxArray.
42319  * @param {Object} config Configuration options
42320  */
42321  
42322
42323 Roo.form.ComboBoxArray = function(config)
42324 {
42325     this.addEvents({
42326         /**
42327          * @event beforeremove
42328          * Fires before remove the value from the list
42329              * @param {Roo.form.ComboBoxArray} _self This combo box array
42330              * @param {Roo.form.ComboBoxArray.Item} item removed item
42331              */
42332         'beforeremove' : true,
42333         /**
42334          * @event remove
42335          * Fires when remove the value from the list
42336              * @param {Roo.form.ComboBoxArray} _self This combo box array
42337              * @param {Roo.form.ComboBoxArray.Item} item removed item
42338              */
42339         'remove' : true
42340         
42341         
42342     });
42343     
42344     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
42345     
42346     this.items = new Roo.util.MixedCollection(false);
42347     
42348     // construct the child combo...
42349     
42350     
42351     
42352     
42353    
42354     
42355 }
42356
42357  
42358 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
42359
42360     /**
42361      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
42362      */
42363     
42364     lastData : false,
42365     
42366     // behavies liek a hiddne field
42367     inputType:      'hidden',
42368     /**
42369      * @cfg {Number} width The width of the box that displays the selected element
42370      */ 
42371     width:          300,
42372
42373     
42374     
42375     /**
42376      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
42377      */
42378     name : false,
42379     /**
42380      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
42381      */
42382     hiddenName : false,
42383       /**
42384      * @cfg {String} seperator    The value seperator normally ',' 
42385      */
42386     seperator : ',',
42387     
42388     // private the array of items that are displayed..
42389     items  : false,
42390     // private - the hidden field el.
42391     hiddenEl : false,
42392     // private - the filed el..
42393     el : false,
42394     
42395     //validateValue : function() { return true; }, // all values are ok!
42396     //onAddClick: function() { },
42397     
42398     onRender : function(ct, position) 
42399     {
42400         
42401         // create the standard hidden element
42402         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
42403         
42404         
42405         // give fake names to child combo;
42406         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
42407         this.combo.name = this.name ? (this.name+'-subcombo') : this.name;
42408         
42409         this.combo = Roo.factory(this.combo, Roo.form);
42410         this.combo.onRender(ct, position);
42411         if (typeof(this.combo.width) != 'undefined') {
42412             this.combo.onResize(this.combo.width,0);
42413         }
42414         
42415         this.combo.initEvents();
42416         
42417         // assigned so form know we need to do this..
42418         this.store          = this.combo.store;
42419         this.valueField     = this.combo.valueField;
42420         this.displayField   = this.combo.displayField ;
42421         
42422         
42423         this.combo.wrap.addClass('x-cbarray-grp');
42424         
42425         var cbwrap = this.combo.wrap.createChild(
42426             {tag: 'div', cls: 'x-cbarray-cb'},
42427             this.combo.el.dom
42428         );
42429         
42430              
42431         this.hiddenEl = this.combo.wrap.createChild({
42432             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
42433         });
42434         this.el = this.combo.wrap.createChild({
42435             tag: 'input',  type:'hidden' , name: this.name, value : ''
42436         });
42437          //   this.el.dom.removeAttribute("name");
42438         
42439         
42440         this.outerWrap = this.combo.wrap;
42441         this.wrap = cbwrap;
42442         
42443         this.outerWrap.setWidth(this.width);
42444         this.outerWrap.dom.removeChild(this.el.dom);
42445         
42446         this.wrap.dom.appendChild(this.el.dom);
42447         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
42448         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
42449         
42450         this.combo.trigger.setStyle('position','relative');
42451         this.combo.trigger.setStyle('left', '0px');
42452         this.combo.trigger.setStyle('top', '2px');
42453         
42454         this.combo.el.setStyle('vertical-align', 'text-bottom');
42455         
42456         //this.trigger.setStyle('vertical-align', 'top');
42457         
42458         // this should use the code from combo really... on('add' ....)
42459         if (this.adder) {
42460             
42461         
42462             this.adder = this.outerWrap.createChild(
42463                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
42464             var _t = this;
42465             this.adder.on('click', function(e) {
42466                 _t.fireEvent('adderclick', this, e);
42467             }, _t);
42468         }
42469         //var _t = this;
42470         //this.adder.on('click', this.onAddClick, _t);
42471         
42472         
42473         this.combo.on('select', function(cb, rec, ix) {
42474             this.addItem(rec.data);
42475             
42476             cb.setValue('');
42477             cb.el.dom.value = '';
42478             //cb.lastData = rec.data;
42479             // add to list
42480             
42481         }, this);
42482         
42483         
42484     },
42485     
42486     
42487     getName: function()
42488     {
42489         // returns hidden if it's set..
42490         if (!this.rendered) {return ''};
42491         return  this.hiddenName ? this.hiddenName : this.name;
42492         
42493     },
42494     
42495     
42496     onResize: function(w, h){
42497         
42498         return;
42499         // not sure if this is needed..
42500         //this.combo.onResize(w,h);
42501         
42502         if(typeof w != 'number'){
42503             // we do not handle it!?!?
42504             return;
42505         }
42506         var tw = this.combo.trigger.getWidth();
42507         tw += this.addicon ? this.addicon.getWidth() : 0;
42508         tw += this.editicon ? this.editicon.getWidth() : 0;
42509         var x = w - tw;
42510         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
42511             
42512         this.combo.trigger.setStyle('left', '0px');
42513         
42514         if(this.list && this.listWidth === undefined){
42515             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
42516             this.list.setWidth(lw);
42517             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
42518         }
42519         
42520     
42521         
42522     },
42523     
42524     addItem: function(rec)
42525     {
42526         var valueField = this.combo.valueField;
42527         var displayField = this.combo.displayField;
42528         
42529         if (this.items.indexOfKey(rec[valueField]) > -1) {
42530             //console.log("GOT " + rec.data.id);
42531             return;
42532         }
42533         
42534         var x = new Roo.form.ComboBoxArray.Item({
42535             //id : rec[this.idField],
42536             data : rec,
42537             displayField : displayField ,
42538             tipField : displayField ,
42539             cb : this
42540         });
42541         // use the 
42542         this.items.add(rec[valueField],x);
42543         // add it before the element..
42544         this.updateHiddenEl();
42545         x.render(this.outerWrap, this.wrap.dom);
42546         // add the image handler..
42547     },
42548     
42549     updateHiddenEl : function()
42550     {
42551         this.validate();
42552         if (!this.hiddenEl) {
42553             return;
42554         }
42555         var ar = [];
42556         var idField = this.combo.valueField;
42557         
42558         this.items.each(function(f) {
42559             ar.push(f.data[idField]);
42560         });
42561         this.hiddenEl.dom.value = ar.join(this.seperator);
42562         this.validate();
42563     },
42564     
42565     reset : function()
42566     {
42567         this.items.clear();
42568         
42569         Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
42570            el.remove();
42571         });
42572         
42573         this.el.dom.value = '';
42574         if (this.hiddenEl) {
42575             this.hiddenEl.dom.value = '';
42576         }
42577         
42578     },
42579     getValue: function()
42580     {
42581         return this.hiddenEl ? this.hiddenEl.dom.value : '';
42582     },
42583     setValue: function(v) // not a valid action - must use addItems..
42584     {
42585         
42586         this.reset();
42587          
42588         if (this.store.isLocal && (typeof(v) == 'string')) {
42589             // then we can use the store to find the values..
42590             // comma seperated at present.. this needs to allow JSON based encoding..
42591             this.hiddenEl.value  = v;
42592             var v_ar = [];
42593             Roo.each(v.split(this.seperator), function(k) {
42594                 Roo.log("CHECK " + this.valueField + ',' + k);
42595                 var li = this.store.query(this.valueField, k);
42596                 if (!li.length) {
42597                     return;
42598                 }
42599                 var add = {};
42600                 add[this.valueField] = k;
42601                 add[this.displayField] = li.item(0).data[this.displayField];
42602                 
42603                 this.addItem(add);
42604             }, this) 
42605              
42606         }
42607         if (typeof(v) == 'object' ) {
42608             // then let's assume it's an array of objects..
42609             Roo.each(v, function(l) {
42610                 var add = l;
42611                 if (typeof(l) == 'string') {
42612                     add = {};
42613                     add[this.valueField] = l;
42614                     add[this.displayField] = l
42615                 }
42616                 this.addItem(add);
42617             }, this);
42618              
42619         }
42620         
42621         
42622     },
42623     setFromData: function(v)
42624     {
42625         // this recieves an object, if setValues is called.
42626         this.reset();
42627         this.el.dom.value = v[this.displayField];
42628         this.hiddenEl.dom.value = v[this.valueField];
42629         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
42630             return;
42631         }
42632         var kv = v[this.valueField];
42633         var dv = v[this.displayField];
42634         kv = typeof(kv) != 'string' ? '' : kv;
42635         dv = typeof(dv) != 'string' ? '' : dv;
42636         
42637         
42638         var keys = kv.split(this.seperator);
42639         var display = dv.split(this.seperator);
42640         for (var i = 0 ; i < keys.length; i++) {
42641             add = {};
42642             add[this.valueField] = keys[i];
42643             add[this.displayField] = display[i];
42644             this.addItem(add);
42645         }
42646       
42647         
42648     },
42649     
42650     /**
42651      * Validates the combox array value
42652      * @return {Boolean} True if the value is valid, else false
42653      */
42654     validate : function(){
42655         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
42656             this.clearInvalid();
42657             return true;
42658         }
42659         return false;
42660     },
42661     
42662     validateValue : function(value){
42663         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
42664         
42665     },
42666     
42667     /*@
42668      * overide
42669      * 
42670      */
42671     isDirty : function() {
42672         if(this.disabled) {
42673             return false;
42674         }
42675         
42676         try {
42677             var d = Roo.decode(String(this.originalValue));
42678         } catch (e) {
42679             return String(this.getValue()) !== String(this.originalValue);
42680         }
42681         
42682         var originalValue = [];
42683         
42684         for (var i = 0; i < d.length; i++){
42685             originalValue.push(d[i][this.valueField]);
42686         }
42687         
42688         return String(this.getValue()) !== String(originalValue.join(this.seperator));
42689         
42690     }
42691     
42692 });
42693
42694
42695
42696 /**
42697  * @class Roo.form.ComboBoxArray.Item
42698  * @extends Roo.BoxComponent
42699  * A selected item in the list
42700  *  Fred [x]  Brian [x]  [Pick another |v]
42701  * 
42702  * @constructor
42703  * Create a new item.
42704  * @param {Object} config Configuration options
42705  */
42706  
42707 Roo.form.ComboBoxArray.Item = function(config) {
42708     config.id = Roo.id();
42709     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
42710 }
42711
42712 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
42713     data : {},
42714     cb: false,
42715     displayField : false,
42716     tipField : false,
42717     
42718     
42719     defaultAutoCreate : {
42720         tag: 'div',
42721         cls: 'x-cbarray-item',
42722         cn : [ 
42723             { tag: 'div' },
42724             {
42725                 tag: 'img',
42726                 width:16,
42727                 height : 16,
42728                 src : Roo.BLANK_IMAGE_URL ,
42729                 align: 'center'
42730             }
42731         ]
42732         
42733     },
42734     
42735  
42736     onRender : function(ct, position)
42737     {
42738         Roo.form.Field.superclass.onRender.call(this, ct, position);
42739         
42740         if(!this.el){
42741             var cfg = this.getAutoCreate();
42742             this.el = ct.createChild(cfg, position);
42743         }
42744         
42745         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
42746         
42747         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
42748             this.cb.renderer(this.data) :
42749             String.format('{0}',this.data[this.displayField]);
42750         
42751             
42752         this.el.child('div').dom.setAttribute('qtip',
42753                         String.format('{0}',this.data[this.tipField])
42754         );
42755         
42756         this.el.child('img').on('click', this.remove, this);
42757         
42758     },
42759    
42760     remove : function()
42761     {
42762         if(this.cb.disabled){
42763             return;
42764         }
42765         
42766         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
42767             this.cb.items.remove(this);
42768             this.el.child('img').un('click', this.remove, this);
42769             this.el.remove();
42770             this.cb.updateHiddenEl();
42771
42772             this.cb.fireEvent('remove', this.cb, this);
42773         }
42774         
42775     }
42776 });/*
42777  * RooJS Library 1.1.1
42778  * Copyright(c) 2008-2011  Alan Knowles
42779  *
42780  * License - LGPL
42781  */
42782  
42783
42784 /**
42785  * @class Roo.form.ComboNested
42786  * @extends Roo.form.ComboBox
42787  * A combobox for that allows selection of nested items in a list,
42788  * eg.
42789  *
42790  *  Book
42791  *    -> red
42792  *    -> green
42793  *  Table
42794  *    -> square
42795  *      ->red
42796  *      ->green
42797  *    -> rectangle
42798  *      ->green
42799  *      
42800  * 
42801  * @constructor
42802  * Create a new ComboNested
42803  * @param {Object} config Configuration options
42804  */
42805 Roo.form.ComboNested = function(config){
42806     Roo.form.ComboCheck.superclass.constructor.call(this, config);
42807     // should verify some data...
42808     // like
42809     // hiddenName = required..
42810     // displayField = required
42811     // valudField == required
42812     var req= [ 'hiddenName', 'displayField', 'valueField' ];
42813     var _t = this;
42814     Roo.each(req, function(e) {
42815         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
42816             throw "Roo.form.ComboNested : missing value for: " + e;
42817         }
42818     });
42819      
42820     
42821 };
42822
42823 Roo.extend(Roo.form.ComboNested, Roo.form.ComboBox, {
42824    
42825     /*
42826      * @config {Number} max Number of columns to show
42827      */
42828     
42829     maxColumns : 3,
42830    
42831     list : null, // the outermost div..
42832     innerLists : null, // the
42833     views : null,
42834     stores : null,
42835     // private
42836     loadingChildren : false,
42837     
42838     onRender : function(ct, position)
42839     {
42840         Roo.form.ComboBox.superclass.onRender.call(this, ct, position); // skip parent call - got to above..
42841         
42842         if(this.hiddenName){
42843             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
42844                     'before', true);
42845             this.hiddenField.value =
42846                 this.hiddenValue !== undefined ? this.hiddenValue :
42847                 this.value !== undefined ? this.value : '';
42848
42849             // prevent input submission
42850             this.el.dom.removeAttribute('name');
42851              
42852              
42853         }
42854         
42855         if(Roo.isGecko){
42856             this.el.dom.setAttribute('autocomplete', 'off');
42857         }
42858
42859         var cls = 'x-combo-list';
42860
42861         this.list = new Roo.Layer({
42862             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
42863         });
42864
42865         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
42866         this.list.setWidth(lw);
42867         this.list.swallowEvent('mousewheel');
42868         this.assetHeight = 0;
42869
42870         if(this.title){
42871             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
42872             this.assetHeight += this.header.getHeight();
42873         }
42874         this.innerLists = [];
42875         this.views = [];
42876         this.stores = [];
42877         for (var i =0 ; i < this.maxColumns; i++) {
42878             this.onRenderList( cls, i);
42879         }
42880         
42881         // always needs footer, as we are going to have an 'OK' button.
42882         this.footer = this.list.createChild({cls:cls+'-ft'});
42883         this.pageTb = new Roo.Toolbar(this.footer);  
42884         var _this = this;
42885         this.pageTb.add(  {
42886             
42887             text: 'Done',
42888             handler: function()
42889             {
42890                 _this.collapse();
42891             }
42892         });
42893         
42894         if ( this.allowBlank && !this.disableClear) {
42895             
42896             this.pageTb.add(new Roo.Toolbar.Fill(), {
42897                 cls: 'x-btn-icon x-btn-clear',
42898                 text: '&#160;',
42899                 handler: function()
42900                 {
42901                     _this.collapse();
42902                     _this.clearValue();
42903                     _this.onSelect(false, -1);
42904                 }
42905             });
42906         }
42907         if (this.footer) {
42908             this.assetHeight += this.footer.getHeight();
42909         }
42910         
42911     },
42912     onRenderList : function (  cls, i)
42913     {
42914         
42915         var lw = Math.floor(
42916                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
42917         );
42918         
42919         this.list.setWidth(lw); // default to '1'
42920
42921         var il = this.innerLists[i] = this.list.createChild({cls:cls+'-inner'});
42922         //il.on('mouseover', this.onViewOver, this, { list:  i });
42923         //il.on('mousemove', this.onViewMove, this, { list:  i });
42924         il.setWidth(lw);
42925         il.setStyle({ 'overflow-x' : 'hidden'});
42926
42927         if(!this.tpl){
42928             this.tpl = new Roo.Template({
42929                 html :  '<div class="'+cls+'-item '+cls+'-item-{cn:this.isEmpty}">{' + this.displayField + '}</div>',
42930                 isEmpty: function (value, allValues) {
42931                     //Roo.log(value);
42932                     var dl = typeof(value.data) != 'undefined' ? value.data.length : value.length; ///json is a nested response..
42933                     return dl ? 'has-children' : 'no-children'
42934                 }
42935             });
42936         }
42937         
42938         var store  = this.store;
42939         if (i > 0) {
42940             store  = new Roo.data.SimpleStore({
42941                 //fields : this.store.reader.meta.fields,
42942                 reader : this.store.reader,
42943                 data : [ ]
42944             });
42945         }
42946         this.stores[i]  = store;
42947                   
42948         var view = this.views[i] = new Roo.View(
42949             il,
42950             this.tpl,
42951             {
42952                 singleSelect:true,
42953                 store: store,
42954                 selectedClass: this.selectedClass
42955             }
42956         );
42957         view.getEl().setWidth(lw);
42958         view.getEl().setStyle({
42959             position: i < 1 ? 'relative' : 'absolute',
42960             top: 0,
42961             left: (i * lw ) + 'px',
42962             display : i > 0 ? 'none' : 'block'
42963         });
42964         view.on('selectionchange', this.onSelectChange.createDelegate(this, {list : i }, true));
42965         view.on('dblclick', this.onDoubleClick.createDelegate(this, {list : i }, true));
42966         //view.on('click', this.onViewClick, this, { list : i });
42967
42968         store.on('beforeload', this.onBeforeLoad, this);
42969         store.on('load',  this.onLoad, this, { list  : i});
42970         store.on('loadexception', this.onLoadException, this);
42971
42972         // hide the other vies..
42973         
42974         
42975         
42976     },
42977       
42978     restrictHeight : function()
42979     {
42980         var mh = 0;
42981         Roo.each(this.innerLists, function(il,i) {
42982             var el = this.views[i].getEl();
42983             el.dom.style.height = '';
42984             var inner = el.dom;
42985             var h = Math.max(il.clientHeight, il.offsetHeight, il.scrollHeight);
42986             // only adjust heights on other ones..
42987             mh = Math.max(h, mh);
42988             if (i < 1) {
42989                 
42990                 el.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
42991                 il.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
42992                
42993             }
42994             
42995             
42996         }, this);
42997         
42998         this.list.beginUpdate();
42999         this.list.setHeight(mh+this.list.getFrameWidth('tb')+this.assetHeight);
43000         this.list.alignTo(this.el, this.listAlign);
43001         this.list.endUpdate();
43002         
43003     },
43004      
43005     
43006     // -- store handlers..
43007     // private
43008     onBeforeLoad : function()
43009     {
43010         if(!this.hasFocus){
43011             return;
43012         }
43013         this.innerLists[0].update(this.loadingText ?
43014                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
43015         this.restrictHeight();
43016         this.selectedIndex = -1;
43017     },
43018     // private
43019     onLoad : function(a,b,c,d)
43020     {
43021         if (!this.loadingChildren) {
43022             // then we are loading the top level. - hide the children
43023             for (var i = 1;i < this.views.length; i++) {
43024                 this.views[i].getEl().setStyle({ display : 'none' });
43025             }
43026             var lw = Math.floor(
43027                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
43028             );
43029         
43030              this.list.setWidth(lw); // default to '1'
43031
43032             
43033         }
43034         if(!this.hasFocus){
43035             return;
43036         }
43037         
43038         if(this.store.getCount() > 0) {
43039             this.expand();
43040             this.restrictHeight();   
43041         } else {
43042             this.onEmptyResults();
43043         }
43044         
43045         if (!this.loadingChildren) {
43046             this.selectActive();
43047         }
43048         /*
43049         this.stores[1].loadData([]);
43050         this.stores[2].loadData([]);
43051         this.views
43052         */    
43053     
43054         //this.el.focus();
43055     },
43056     
43057     
43058     // private
43059     onLoadException : function()
43060     {
43061         this.collapse();
43062         Roo.log(this.store.reader.jsonData);
43063         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
43064             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
43065         }
43066         
43067         
43068     },
43069     // no cleaning of leading spaces on blur here.
43070     cleanLeadingSpace : function(e) { },
43071     
43072
43073     onSelectChange : function (view, sels, opts )
43074     {
43075         var ix = view.getSelectedIndexes();
43076          
43077         if (opts.list > this.maxColumns - 2) {
43078             if (view.store.getCount()<  1) {
43079                 this.views[opts.list ].getEl().setStyle({ display :   'none' });
43080
43081             } else  {
43082                 if (ix.length) {
43083                     // used to clear ?? but if we are loading unselected 
43084                     this.setFromData(view.store.getAt(ix[0]).data);
43085                 }
43086                 
43087             }
43088             
43089             return;
43090         }
43091         
43092         if (!ix.length) {
43093             // this get's fired when trigger opens..
43094            // this.setFromData({});
43095             var str = this.stores[opts.list+1];
43096             str.data.clear(); // removeall wihtout the fire events..
43097             return;
43098         }
43099         
43100         var rec = view.store.getAt(ix[0]);
43101          
43102         this.setFromData(rec.data);
43103         this.fireEvent('select', this, rec, ix[0]);
43104         
43105         var lw = Math.floor(
43106              (
43107                 (this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')
43108              ) / this.maxColumns
43109         );
43110         this.loadingChildren = true;
43111         this.stores[opts.list+1].loadDataFromChildren( rec );
43112         this.loadingChildren = false;
43113         var dl = this.stores[opts.list+1]. getTotalCount();
43114         
43115         this.views[opts.list+1].getEl().setHeight( this.innerLists[0].getHeight());
43116         
43117         this.views[opts.list+1].getEl().setStyle({ display : dl ? 'block' : 'none' });
43118         for (var i = opts.list+2; i < this.views.length;i++) {
43119             this.views[i].getEl().setStyle({ display : 'none' });
43120         }
43121         
43122         this.innerLists[opts.list+1].setHeight( this.innerLists[0].getHeight());
43123         this.list.setWidth(lw * (opts.list + (dl ? 2 : 1)));
43124         
43125         if (this.isLoading) {
43126            // this.selectActive(opts.list);
43127         }
43128          
43129     },
43130     
43131     
43132     
43133     
43134     onDoubleClick : function()
43135     {
43136         this.collapse(); //??
43137     },
43138     
43139      
43140     
43141     
43142     
43143     // private
43144     recordToStack : function(store, prop, value, stack)
43145     {
43146         var cstore = new Roo.data.SimpleStore({
43147             //fields : this.store.reader.meta.fields, // we need array reader.. for
43148             reader : this.store.reader,
43149             data : [ ]
43150         });
43151         var _this = this;
43152         var record  = false;
43153         var srec = false;
43154         if(store.getCount() < 1){
43155             return false;
43156         }
43157         store.each(function(r){
43158             if(r.data[prop] == value){
43159                 record = r;
43160             srec = r;
43161                 return false;
43162             }
43163             if (r.data.cn && r.data.cn.length) {
43164                 cstore.loadDataFromChildren( r);
43165                 var cret = _this.recordToStack(cstore, prop, value, stack);
43166                 if (cret !== false) {
43167                     record = cret;
43168                     srec = r;
43169                     return false;
43170                 }
43171             }
43172              
43173             return true;
43174         });
43175         if (record == false) {
43176             return false
43177         }
43178         stack.unshift(srec);
43179         return record;
43180     },
43181     
43182     /*
43183      * find the stack of stores that match our value.
43184      *
43185      * 
43186      */
43187     
43188     selectActive : function ()
43189     {
43190         // if store is not loaded, then we will need to wait for that to happen first.
43191         var stack = [];
43192         this.recordToStack(this.store, this.valueField, this.getValue(), stack);
43193         for (var i = 0; i < stack.length; i++ ) {
43194             this.views[i].select(stack[i].store.indexOf(stack[i]), false, false );
43195         }
43196         
43197     }
43198         
43199          
43200     
43201     
43202     
43203     
43204 });/*
43205  * Based on:
43206  * Ext JS Library 1.1.1
43207  * Copyright(c) 2006-2007, Ext JS, LLC.
43208  *
43209  * Originally Released Under LGPL - original licence link has changed is not relivant.
43210  *
43211  * Fork - LGPL
43212  * <script type="text/javascript">
43213  */
43214 /**
43215  * @class Roo.form.Checkbox
43216  * @extends Roo.form.Field
43217  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
43218  * @constructor
43219  * Creates a new Checkbox
43220  * @param {Object} config Configuration options
43221  */
43222 Roo.form.Checkbox = function(config){
43223     Roo.form.Checkbox.superclass.constructor.call(this, config);
43224     this.addEvents({
43225         /**
43226          * @event check
43227          * Fires when the checkbox is checked or unchecked.
43228              * @param {Roo.form.Checkbox} this This checkbox
43229              * @param {Boolean} checked The new checked value
43230              */
43231         check : true
43232     });
43233 };
43234
43235 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
43236     /**
43237      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
43238      */
43239     focusClass : undefined,
43240     /**
43241      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
43242      */
43243     fieldClass: "x-form-field",
43244     /**
43245      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
43246      */
43247     checked: false,
43248     /**
43249      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
43250      * {tag: "input", type: "checkbox", autocomplete: "off"})
43251      */
43252     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
43253     /**
43254      * @cfg {String} boxLabel The text that appears beside the checkbox
43255      */
43256     boxLabel : "",
43257     /**
43258      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
43259      */  
43260     inputValue : '1',
43261     /**
43262      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
43263      */
43264      valueOff: '0', // value when not checked..
43265
43266     actionMode : 'viewEl', 
43267     //
43268     // private
43269     itemCls : 'x-menu-check-item x-form-item',
43270     groupClass : 'x-menu-group-item',
43271     inputType : 'hidden',
43272     
43273     
43274     inSetChecked: false, // check that we are not calling self...
43275     
43276     inputElement: false, // real input element?
43277     basedOn: false, // ????
43278     
43279     isFormField: true, // not sure where this is needed!!!!
43280
43281     onResize : function(){
43282         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
43283         if(!this.boxLabel){
43284             this.el.alignTo(this.wrap, 'c-c');
43285         }
43286     },
43287
43288     initEvents : function(){
43289         Roo.form.Checkbox.superclass.initEvents.call(this);
43290         this.el.on("click", this.onClick,  this);
43291         this.el.on("change", this.onClick,  this);
43292     },
43293
43294
43295     getResizeEl : function(){
43296         return this.wrap;
43297     },
43298
43299     getPositionEl : function(){
43300         return this.wrap;
43301     },
43302
43303     // private
43304     onRender : function(ct, position){
43305         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
43306         /*
43307         if(this.inputValue !== undefined){
43308             this.el.dom.value = this.inputValue;
43309         }
43310         */
43311         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
43312         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
43313         var viewEl = this.wrap.createChild({ 
43314             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
43315         this.viewEl = viewEl;   
43316         this.wrap.on('click', this.onClick,  this); 
43317         
43318         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
43319         this.el.on('propertychange', this.setFromHidden,  this);  //ie
43320         
43321         
43322         
43323         if(this.boxLabel){
43324             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
43325         //    viewEl.on('click', this.onClick,  this); 
43326         }
43327         //if(this.checked){
43328             this.setChecked(this.checked);
43329         //}else{
43330             //this.checked = this.el.dom;
43331         //}
43332
43333     },
43334
43335     // private
43336     initValue : Roo.emptyFn,
43337
43338     /**
43339      * Returns the checked state of the checkbox.
43340      * @return {Boolean} True if checked, else false
43341      */
43342     getValue : function(){
43343         if(this.el){
43344             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
43345         }
43346         return this.valueOff;
43347         
43348     },
43349
43350         // private
43351     onClick : function(){ 
43352         if (this.disabled) {
43353             return;
43354         }
43355         this.setChecked(!this.checked);
43356
43357         //if(this.el.dom.checked != this.checked){
43358         //    this.setValue(this.el.dom.checked);
43359        // }
43360     },
43361
43362     /**
43363      * Sets the checked state of the checkbox.
43364      * On is always based on a string comparison between inputValue and the param.
43365      * @param {Boolean/String} value - the value to set 
43366      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
43367      */
43368     setValue : function(v,suppressEvent){
43369         
43370         
43371         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
43372         //if(this.el && this.el.dom){
43373         //    this.el.dom.checked = this.checked;
43374         //    this.el.dom.defaultChecked = this.checked;
43375         //}
43376         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
43377         //this.fireEvent("check", this, this.checked);
43378     },
43379     // private..
43380     setChecked : function(state,suppressEvent)
43381     {
43382         if (this.inSetChecked) {
43383             this.checked = state;
43384             return;
43385         }
43386         
43387     
43388         if(this.wrap){
43389             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
43390         }
43391         this.checked = state;
43392         if(suppressEvent !== true){
43393             this.fireEvent('check', this, state);
43394         }
43395         this.inSetChecked = true;
43396         this.el.dom.value = state ? this.inputValue : this.valueOff;
43397         this.inSetChecked = false;
43398         
43399     },
43400     // handle setting of hidden value by some other method!!?!?
43401     setFromHidden: function()
43402     {
43403         if(!this.el){
43404             return;
43405         }
43406         //console.log("SET FROM HIDDEN");
43407         //alert('setFrom hidden');
43408         this.setValue(this.el.dom.value);
43409     },
43410     
43411     onDestroy : function()
43412     {
43413         if(this.viewEl){
43414             Roo.get(this.viewEl).remove();
43415         }
43416          
43417         Roo.form.Checkbox.superclass.onDestroy.call(this);
43418     },
43419     
43420     setBoxLabel : function(str)
43421     {
43422         this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
43423     }
43424
43425 });/*
43426  * Based on:
43427  * Ext JS Library 1.1.1
43428  * Copyright(c) 2006-2007, Ext JS, LLC.
43429  *
43430  * Originally Released Under LGPL - original licence link has changed is not relivant.
43431  *
43432  * Fork - LGPL
43433  * <script type="text/javascript">
43434  */
43435  
43436 /**
43437  * @class Roo.form.Radio
43438  * @extends Roo.form.Checkbox
43439  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
43440  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
43441  * @constructor
43442  * Creates a new Radio
43443  * @param {Object} config Configuration options
43444  */
43445 Roo.form.Radio = function(){
43446     Roo.form.Radio.superclass.constructor.apply(this, arguments);
43447 };
43448 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
43449     inputType: 'radio',
43450
43451     /**
43452      * If this radio is part of a group, it will return the selected value
43453      * @return {String}
43454      */
43455     getGroupValue : function(){
43456         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
43457     },
43458     
43459     
43460     onRender : function(ct, position){
43461         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
43462         
43463         if(this.inputValue !== undefined){
43464             this.el.dom.value = this.inputValue;
43465         }
43466          
43467         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
43468         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
43469         //var viewEl = this.wrap.createChild({ 
43470         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
43471         //this.viewEl = viewEl;   
43472         //this.wrap.on('click', this.onClick,  this); 
43473         
43474         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
43475         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
43476         
43477         
43478         
43479         if(this.boxLabel){
43480             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
43481         //    viewEl.on('click', this.onClick,  this); 
43482         }
43483          if(this.checked){
43484             this.el.dom.checked =   'checked' ;
43485         }
43486          
43487     } 
43488     
43489     
43490 });//<script type="text/javascript">
43491
43492 /*
43493  * Based  Ext JS Library 1.1.1
43494  * Copyright(c) 2006-2007, Ext JS, LLC.
43495  * LGPL
43496  *
43497  */
43498  
43499 /**
43500  * @class Roo.HtmlEditorCore
43501  * @extends Roo.Component
43502  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
43503  *
43504  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
43505  */
43506
43507 Roo.HtmlEditorCore = function(config){
43508     
43509     
43510     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
43511     
43512     
43513     this.addEvents({
43514         /**
43515          * @event initialize
43516          * Fires when the editor is fully initialized (including the iframe)
43517          * @param {Roo.HtmlEditorCore} this
43518          */
43519         initialize: true,
43520         /**
43521          * @event activate
43522          * Fires when the editor is first receives the focus. Any insertion must wait
43523          * until after this event.
43524          * @param {Roo.HtmlEditorCore} this
43525          */
43526         activate: true,
43527          /**
43528          * @event beforesync
43529          * Fires before the textarea is updated with content from the editor iframe. Return false
43530          * to cancel the sync.
43531          * @param {Roo.HtmlEditorCore} this
43532          * @param {String} html
43533          */
43534         beforesync: true,
43535          /**
43536          * @event beforepush
43537          * Fires before the iframe editor is updated with content from the textarea. Return false
43538          * to cancel the push.
43539          * @param {Roo.HtmlEditorCore} this
43540          * @param {String} html
43541          */
43542         beforepush: true,
43543          /**
43544          * @event sync
43545          * Fires when the textarea is updated with content from the editor iframe.
43546          * @param {Roo.HtmlEditorCore} this
43547          * @param {String} html
43548          */
43549         sync: true,
43550          /**
43551          * @event push
43552          * Fires when the iframe editor is updated with content from the textarea.
43553          * @param {Roo.HtmlEditorCore} this
43554          * @param {String} html
43555          */
43556         push: true,
43557         
43558         /**
43559          * @event editorevent
43560          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
43561          * @param {Roo.HtmlEditorCore} this
43562          */
43563         editorevent: true
43564         
43565     });
43566     
43567     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
43568     
43569     // defaults : white / black...
43570     this.applyBlacklists();
43571     
43572     
43573     
43574 };
43575
43576
43577 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
43578
43579
43580      /**
43581      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
43582      */
43583     
43584     owner : false,
43585     
43586      /**
43587      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
43588      *                        Roo.resizable.
43589      */
43590     resizable : false,
43591      /**
43592      * @cfg {Number} height (in pixels)
43593      */   
43594     height: 300,
43595    /**
43596      * @cfg {Number} width (in pixels)
43597      */   
43598     width: 500,
43599     
43600     /**
43601      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
43602      * 
43603      */
43604     stylesheets: false,
43605     
43606     // id of frame..
43607     frameId: false,
43608     
43609     // private properties
43610     validationEvent : false,
43611     deferHeight: true,
43612     initialized : false,
43613     activated : false,
43614     sourceEditMode : false,
43615     onFocus : Roo.emptyFn,
43616     iframePad:3,
43617     hideMode:'offsets',
43618     
43619     clearUp: true,
43620     
43621     // blacklist + whitelisted elements..
43622     black: false,
43623     white: false,
43624      
43625     bodyCls : '',
43626
43627     /**
43628      * Protected method that will not generally be called directly. It
43629      * is called when the editor initializes the iframe with HTML contents. Override this method if you
43630      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
43631      */
43632     getDocMarkup : function(){
43633         // body styles..
43634         var st = '';
43635         
43636         // inherit styels from page...?? 
43637         if (this.stylesheets === false) {
43638             
43639             Roo.get(document.head).select('style').each(function(node) {
43640                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
43641             });
43642             
43643             Roo.get(document.head).select('link').each(function(node) { 
43644                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
43645             });
43646             
43647         } else if (!this.stylesheets.length) {
43648                 // simple..
43649                 st = '<style type="text/css">' +
43650                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
43651                    '</style>';
43652         } else {
43653             for (var i in this.stylesheets) { 
43654                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
43655             }
43656             
43657         }
43658         
43659         st +=  '<style type="text/css">' +
43660             'IMG { cursor: pointer } ' +
43661         '</style>';
43662
43663         var cls = 'roo-htmleditor-body';
43664         
43665         if(this.bodyCls.length){
43666             cls += ' ' + this.bodyCls;
43667         }
43668         
43669         return '<html><head>' + st  +
43670             //<style type="text/css">' +
43671             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
43672             //'</style>' +
43673             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
43674     },
43675
43676     // private
43677     onRender : function(ct, position)
43678     {
43679         var _t = this;
43680         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
43681         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
43682         
43683         
43684         this.el.dom.style.border = '0 none';
43685         this.el.dom.setAttribute('tabIndex', -1);
43686         this.el.addClass('x-hidden hide');
43687         
43688         
43689         
43690         if(Roo.isIE){ // fix IE 1px bogus margin
43691             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
43692         }
43693        
43694         
43695         this.frameId = Roo.id();
43696         
43697          
43698         
43699         var iframe = this.owner.wrap.createChild({
43700             tag: 'iframe',
43701             cls: 'form-control', // bootstrap..
43702             id: this.frameId,
43703             name: this.frameId,
43704             frameBorder : 'no',
43705             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
43706         }, this.el
43707         );
43708         
43709         
43710         this.iframe = iframe.dom;
43711
43712          this.assignDocWin();
43713         
43714         this.doc.designMode = 'on';
43715        
43716         this.doc.open();
43717         this.doc.write(this.getDocMarkup());
43718         this.doc.close();
43719
43720         
43721         var task = { // must defer to wait for browser to be ready
43722             run : function(){
43723                 //console.log("run task?" + this.doc.readyState);
43724                 this.assignDocWin();
43725                 if(this.doc.body || this.doc.readyState == 'complete'){
43726                     try {
43727                         this.doc.designMode="on";
43728                     } catch (e) {
43729                         return;
43730                     }
43731                     Roo.TaskMgr.stop(task);
43732                     this.initEditor.defer(10, this);
43733                 }
43734             },
43735             interval : 10,
43736             duration: 10000,
43737             scope: this
43738         };
43739         Roo.TaskMgr.start(task);
43740
43741     },
43742
43743     // private
43744     onResize : function(w, h)
43745     {
43746          Roo.log('resize: ' +w + ',' + h );
43747         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
43748         if(!this.iframe){
43749             return;
43750         }
43751         if(typeof w == 'number'){
43752             
43753             this.iframe.style.width = w + 'px';
43754         }
43755         if(typeof h == 'number'){
43756             
43757             this.iframe.style.height = h + 'px';
43758             if(this.doc){
43759                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
43760             }
43761         }
43762         
43763     },
43764
43765     /**
43766      * Toggles the editor between standard and source edit mode.
43767      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
43768      */
43769     toggleSourceEdit : function(sourceEditMode){
43770         
43771         this.sourceEditMode = sourceEditMode === true;
43772         
43773         if(this.sourceEditMode){
43774  
43775             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
43776             
43777         }else{
43778             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
43779             //this.iframe.className = '';
43780             this.deferFocus();
43781         }
43782         //this.setSize(this.owner.wrap.getSize());
43783         //this.fireEvent('editmodechange', this, this.sourceEditMode);
43784     },
43785
43786     
43787   
43788
43789     /**
43790      * Protected method that will not generally be called directly. If you need/want
43791      * custom HTML cleanup, this is the method you should override.
43792      * @param {String} html The HTML to be cleaned
43793      * return {String} The cleaned HTML
43794      */
43795     cleanHtml : function(html){
43796         html = String(html);
43797         if(html.length > 5){
43798             if(Roo.isSafari){ // strip safari nonsense
43799                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
43800             }
43801         }
43802         if(html == '&nbsp;'){
43803             html = '';
43804         }
43805         return html;
43806     },
43807
43808     /**
43809      * HTML Editor -> Textarea
43810      * Protected method that will not generally be called directly. Syncs the contents
43811      * of the editor iframe with the textarea.
43812      */
43813     syncValue : function(){
43814         if(this.initialized){
43815             var bd = (this.doc.body || this.doc.documentElement);
43816             //this.cleanUpPaste(); -- this is done else where and causes havoc..
43817             var html = bd.innerHTML;
43818             if(Roo.isSafari){
43819                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
43820                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
43821                 if(m && m[1]){
43822                     html = '<div style="'+m[0]+'">' + html + '</div>';
43823                 }
43824             }
43825             html = this.cleanHtml(html);
43826             // fix up the special chars.. normaly like back quotes in word...
43827             // however we do not want to do this with chinese..
43828             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
43829                 
43830                 var cc = match.charCodeAt();
43831
43832                 // Get the character value, handling surrogate pairs
43833                 if (match.length == 2) {
43834                     // It's a surrogate pair, calculate the Unicode code point
43835                     var high = match.charCodeAt(0) - 0xD800;
43836                     var low  = match.charCodeAt(1) - 0xDC00;
43837                     cc = (high * 0x400) + low + 0x10000;
43838                 }  else if (
43839                     (cc >= 0x4E00 && cc < 0xA000 ) ||
43840                     (cc >= 0x3400 && cc < 0x4E00 ) ||
43841                     (cc >= 0xf900 && cc < 0xfb00 )
43842                 ) {
43843                         return match;
43844                 }  
43845          
43846                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
43847                 return "&#" + cc + ";";
43848                 
43849                 
43850             });
43851             
43852             
43853              
43854             if(this.owner.fireEvent('beforesync', this, html) !== false){
43855                 this.el.dom.value = html;
43856                 this.owner.fireEvent('sync', this, html);
43857             }
43858         }
43859     },
43860
43861     /**
43862      * Protected method that will not generally be called directly. Pushes the value of the textarea
43863      * into the iframe editor.
43864      */
43865     pushValue : function(){
43866         if(this.initialized){
43867             var v = this.el.dom.value.trim();
43868             
43869 //            if(v.length < 1){
43870 //                v = '&#160;';
43871 //            }
43872             
43873             if(this.owner.fireEvent('beforepush', this, v) !== false){
43874                 var d = (this.doc.body || this.doc.documentElement);
43875                 d.innerHTML = v;
43876                 this.cleanUpPaste();
43877                 this.el.dom.value = d.innerHTML;
43878                 this.owner.fireEvent('push', this, v);
43879             }
43880         }
43881     },
43882
43883     // private
43884     deferFocus : function(){
43885         this.focus.defer(10, this);
43886     },
43887
43888     // doc'ed in Field
43889     focus : function(){
43890         if(this.win && !this.sourceEditMode){
43891             this.win.focus();
43892         }else{
43893             this.el.focus();
43894         }
43895     },
43896     
43897     assignDocWin: function()
43898     {
43899         var iframe = this.iframe;
43900         
43901          if(Roo.isIE){
43902             this.doc = iframe.contentWindow.document;
43903             this.win = iframe.contentWindow;
43904         } else {
43905 //            if (!Roo.get(this.frameId)) {
43906 //                return;
43907 //            }
43908 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
43909 //            this.win = Roo.get(this.frameId).dom.contentWindow;
43910             
43911             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
43912                 return;
43913             }
43914             
43915             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
43916             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
43917         }
43918     },
43919     
43920     // private
43921     initEditor : function(){
43922         //console.log("INIT EDITOR");
43923         this.assignDocWin();
43924         
43925         
43926         
43927         this.doc.designMode="on";
43928         this.doc.open();
43929         this.doc.write(this.getDocMarkup());
43930         this.doc.close();
43931         
43932         var dbody = (this.doc.body || this.doc.documentElement);
43933         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
43934         // this copies styles from the containing element into thsi one..
43935         // not sure why we need all of this..
43936         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
43937         
43938         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
43939         //ss['background-attachment'] = 'fixed'; // w3c
43940         dbody.bgProperties = 'fixed'; // ie
43941         //Roo.DomHelper.applyStyles(dbody, ss);
43942         Roo.EventManager.on(this.doc, {
43943             //'mousedown': this.onEditorEvent,
43944             'mouseup': this.onEditorEvent,
43945             'dblclick': this.onEditorEvent,
43946             'click': this.onEditorEvent,
43947             'keyup': this.onEditorEvent,
43948             buffer:100,
43949             scope: this
43950         });
43951         if(Roo.isGecko){
43952             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
43953         }
43954         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
43955             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
43956         }
43957         this.initialized = true;
43958
43959         this.owner.fireEvent('initialize', this);
43960         this.pushValue();
43961     },
43962
43963     // private
43964     onDestroy : function(){
43965         
43966         
43967         
43968         if(this.rendered){
43969             
43970             //for (var i =0; i < this.toolbars.length;i++) {
43971             //    // fixme - ask toolbars for heights?
43972             //    this.toolbars[i].onDestroy();
43973            // }
43974             
43975             //this.wrap.dom.innerHTML = '';
43976             //this.wrap.remove();
43977         }
43978     },
43979
43980     // private
43981     onFirstFocus : function(){
43982         
43983         this.assignDocWin();
43984         
43985         
43986         this.activated = true;
43987          
43988     
43989         if(Roo.isGecko){ // prevent silly gecko errors
43990             this.win.focus();
43991             var s = this.win.getSelection();
43992             if(!s.focusNode || s.focusNode.nodeType != 3){
43993                 var r = s.getRangeAt(0);
43994                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
43995                 r.collapse(true);
43996                 this.deferFocus();
43997             }
43998             try{
43999                 this.execCmd('useCSS', true);
44000                 this.execCmd('styleWithCSS', false);
44001             }catch(e){}
44002         }
44003         this.owner.fireEvent('activate', this);
44004     },
44005
44006     // private
44007     adjustFont: function(btn){
44008         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
44009         //if(Roo.isSafari){ // safari
44010         //    adjust *= 2;
44011        // }
44012         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
44013         if(Roo.isSafari){ // safari
44014             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
44015             v =  (v < 10) ? 10 : v;
44016             v =  (v > 48) ? 48 : v;
44017             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
44018             
44019         }
44020         
44021         
44022         v = Math.max(1, v+adjust);
44023         
44024         this.execCmd('FontSize', v  );
44025     },
44026
44027     onEditorEvent : function(e)
44028     {
44029         this.owner.fireEvent('editorevent', this, e);
44030       //  this.updateToolbar();
44031         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
44032     },
44033
44034     insertTag : function(tg)
44035     {
44036         // could be a bit smarter... -> wrap the current selected tRoo..
44037         if (tg.toLowerCase() == 'span' ||
44038             tg.toLowerCase() == 'code' ||
44039             tg.toLowerCase() == 'sup' ||
44040             tg.toLowerCase() == 'sub' 
44041             ) {
44042             
44043             range = this.createRange(this.getSelection());
44044             var wrappingNode = this.doc.createElement(tg.toLowerCase());
44045             wrappingNode.appendChild(range.extractContents());
44046             range.insertNode(wrappingNode);
44047
44048             return;
44049             
44050             
44051             
44052         }
44053         this.execCmd("formatblock",   tg);
44054         
44055     },
44056     
44057     insertText : function(txt)
44058     {
44059         
44060         
44061         var range = this.createRange();
44062         range.deleteContents();
44063                //alert(Sender.getAttribute('label'));
44064                
44065         range.insertNode(this.doc.createTextNode(txt));
44066     } ,
44067     
44068      
44069
44070     /**
44071      * Executes a Midas editor command on the editor document and performs necessary focus and
44072      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
44073      * @param {String} cmd The Midas command
44074      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
44075      */
44076     relayCmd : function(cmd, value){
44077         this.win.focus();
44078         this.execCmd(cmd, value);
44079         this.owner.fireEvent('editorevent', this);
44080         //this.updateToolbar();
44081         this.owner.deferFocus();
44082     },
44083
44084     /**
44085      * Executes a Midas editor command directly on the editor document.
44086      * For visual commands, you should use {@link #relayCmd} instead.
44087      * <b>This should only be called after the editor is initialized.</b>
44088      * @param {String} cmd The Midas command
44089      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
44090      */
44091     execCmd : function(cmd, value){
44092         this.doc.execCommand(cmd, false, value === undefined ? null : value);
44093         this.syncValue();
44094     },
44095  
44096  
44097    
44098     /**
44099      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
44100      * to insert tRoo.
44101      * @param {String} text | dom node.. 
44102      */
44103     insertAtCursor : function(text)
44104     {
44105         
44106         if(!this.activated){
44107             return;
44108         }
44109         /*
44110         if(Roo.isIE){
44111             this.win.focus();
44112             var r = this.doc.selection.createRange();
44113             if(r){
44114                 r.collapse(true);
44115                 r.pasteHTML(text);
44116                 this.syncValue();
44117                 this.deferFocus();
44118             
44119             }
44120             return;
44121         }
44122         */
44123         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
44124             this.win.focus();
44125             
44126             
44127             // from jquery ui (MIT licenced)
44128             var range, node;
44129             var win = this.win;
44130             
44131             if (win.getSelection && win.getSelection().getRangeAt) {
44132                 range = win.getSelection().getRangeAt(0);
44133                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
44134                 range.insertNode(node);
44135             } else if (win.document.selection && win.document.selection.createRange) {
44136                 // no firefox support
44137                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
44138                 win.document.selection.createRange().pasteHTML(txt);
44139             } else {
44140                 // no firefox support
44141                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
44142                 this.execCmd('InsertHTML', txt);
44143             } 
44144             
44145             this.syncValue();
44146             
44147             this.deferFocus();
44148         }
44149     },
44150  // private
44151     mozKeyPress : function(e){
44152         if(e.ctrlKey){
44153             var c = e.getCharCode(), cmd;
44154           
44155             if(c > 0){
44156                 c = String.fromCharCode(c).toLowerCase();
44157                 switch(c){
44158                     case 'b':
44159                         cmd = 'bold';
44160                         break;
44161                     case 'i':
44162                         cmd = 'italic';
44163                         break;
44164                     
44165                     case 'u':
44166                         cmd = 'underline';
44167                         break;
44168                     
44169                     case 'v':
44170                         this.cleanUpPaste.defer(100, this);
44171                         return;
44172                         
44173                 }
44174                 if(cmd){
44175                     this.win.focus();
44176                     this.execCmd(cmd);
44177                     this.deferFocus();
44178                     e.preventDefault();
44179                 }
44180                 
44181             }
44182         }
44183     },
44184
44185     // private
44186     fixKeys : function(){ // load time branching for fastest keydown performance
44187         if(Roo.isIE){
44188             return function(e){
44189                 var k = e.getKey(), r;
44190                 if(k == e.TAB){
44191                     e.stopEvent();
44192                     r = this.doc.selection.createRange();
44193                     if(r){
44194                         r.collapse(true);
44195                         r.pasteHTML('&#160;&#160;&#160;&#160;');
44196                         this.deferFocus();
44197                     }
44198                     return;
44199                 }
44200                 
44201                 if(k == e.ENTER){
44202                     r = this.doc.selection.createRange();
44203                     if(r){
44204                         var target = r.parentElement();
44205                         if(!target || target.tagName.toLowerCase() != 'li'){
44206                             e.stopEvent();
44207                             r.pasteHTML('<br />');
44208                             r.collapse(false);
44209                             r.select();
44210                         }
44211                     }
44212                 }
44213                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
44214                     this.cleanUpPaste.defer(100, this);
44215                     return;
44216                 }
44217                 
44218                 
44219             };
44220         }else if(Roo.isOpera){
44221             return function(e){
44222                 var k = e.getKey();
44223                 if(k == e.TAB){
44224                     e.stopEvent();
44225                     this.win.focus();
44226                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
44227                     this.deferFocus();
44228                 }
44229                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
44230                     this.cleanUpPaste.defer(100, this);
44231                     return;
44232                 }
44233                 
44234             };
44235         }else if(Roo.isSafari){
44236             return function(e){
44237                 var k = e.getKey();
44238                 
44239                 if(k == e.TAB){
44240                     e.stopEvent();
44241                     this.execCmd('InsertText','\t');
44242                     this.deferFocus();
44243                     return;
44244                 }
44245                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
44246                     this.cleanUpPaste.defer(100, this);
44247                     return;
44248                 }
44249                 
44250              };
44251         }
44252     }(),
44253     
44254     getAllAncestors: function()
44255     {
44256         var p = this.getSelectedNode();
44257         var a = [];
44258         if (!p) {
44259             a.push(p); // push blank onto stack..
44260             p = this.getParentElement();
44261         }
44262         
44263         
44264         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
44265             a.push(p);
44266             p = p.parentNode;
44267         }
44268         a.push(this.doc.body);
44269         return a;
44270     },
44271     lastSel : false,
44272     lastSelNode : false,
44273     
44274     
44275     getSelection : function() 
44276     {
44277         this.assignDocWin();
44278         return Roo.isIE ? this.doc.selection : this.win.getSelection();
44279     },
44280     
44281     getSelectedNode: function() 
44282     {
44283         // this may only work on Gecko!!!
44284         
44285         // should we cache this!!!!
44286         
44287         
44288         
44289          
44290         var range = this.createRange(this.getSelection()).cloneRange();
44291         
44292         if (Roo.isIE) {
44293             var parent = range.parentElement();
44294             while (true) {
44295                 var testRange = range.duplicate();
44296                 testRange.moveToElementText(parent);
44297                 if (testRange.inRange(range)) {
44298                     break;
44299                 }
44300                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
44301                     break;
44302                 }
44303                 parent = parent.parentElement;
44304             }
44305             return parent;
44306         }
44307         
44308         // is ancestor a text element.
44309         var ac =  range.commonAncestorContainer;
44310         if (ac.nodeType == 3) {
44311             ac = ac.parentNode;
44312         }
44313         
44314         var ar = ac.childNodes;
44315          
44316         var nodes = [];
44317         var other_nodes = [];
44318         var has_other_nodes = false;
44319         for (var i=0;i<ar.length;i++) {
44320             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
44321                 continue;
44322             }
44323             // fullly contained node.
44324             
44325             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
44326                 nodes.push(ar[i]);
44327                 continue;
44328             }
44329             
44330             // probably selected..
44331             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
44332                 other_nodes.push(ar[i]);
44333                 continue;
44334             }
44335             // outer..
44336             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
44337                 continue;
44338             }
44339             
44340             
44341             has_other_nodes = true;
44342         }
44343         if (!nodes.length && other_nodes.length) {
44344             nodes= other_nodes;
44345         }
44346         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
44347             return false;
44348         }
44349         
44350         return nodes[0];
44351     },
44352     createRange: function(sel)
44353     {
44354         // this has strange effects when using with 
44355         // top toolbar - not sure if it's a great idea.
44356         //this.editor.contentWindow.focus();
44357         if (typeof sel != "undefined") {
44358             try {
44359                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
44360             } catch(e) {
44361                 return this.doc.createRange();
44362             }
44363         } else {
44364             return this.doc.createRange();
44365         }
44366     },
44367     getParentElement: function()
44368     {
44369         
44370         this.assignDocWin();
44371         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
44372         
44373         var range = this.createRange(sel);
44374          
44375         try {
44376             var p = range.commonAncestorContainer;
44377             while (p.nodeType == 3) { // text node
44378                 p = p.parentNode;
44379             }
44380             return p;
44381         } catch (e) {
44382             return null;
44383         }
44384     
44385     },
44386     /***
44387      *
44388      * Range intersection.. the hard stuff...
44389      *  '-1' = before
44390      *  '0' = hits..
44391      *  '1' = after.
44392      *         [ -- selected range --- ]
44393      *   [fail]                        [fail]
44394      *
44395      *    basically..
44396      *      if end is before start or  hits it. fail.
44397      *      if start is after end or hits it fail.
44398      *
44399      *   if either hits (but other is outside. - then it's not 
44400      *   
44401      *    
44402      **/
44403     
44404     
44405     // @see http://www.thismuchiknow.co.uk/?p=64.
44406     rangeIntersectsNode : function(range, node)
44407     {
44408         var nodeRange = node.ownerDocument.createRange();
44409         try {
44410             nodeRange.selectNode(node);
44411         } catch (e) {
44412             nodeRange.selectNodeContents(node);
44413         }
44414     
44415         var rangeStartRange = range.cloneRange();
44416         rangeStartRange.collapse(true);
44417     
44418         var rangeEndRange = range.cloneRange();
44419         rangeEndRange.collapse(false);
44420     
44421         var nodeStartRange = nodeRange.cloneRange();
44422         nodeStartRange.collapse(true);
44423     
44424         var nodeEndRange = nodeRange.cloneRange();
44425         nodeEndRange.collapse(false);
44426     
44427         return rangeStartRange.compareBoundaryPoints(
44428                  Range.START_TO_START, nodeEndRange) == -1 &&
44429                rangeEndRange.compareBoundaryPoints(
44430                  Range.START_TO_START, nodeStartRange) == 1;
44431         
44432          
44433     },
44434     rangeCompareNode : function(range, node)
44435     {
44436         var nodeRange = node.ownerDocument.createRange();
44437         try {
44438             nodeRange.selectNode(node);
44439         } catch (e) {
44440             nodeRange.selectNodeContents(node);
44441         }
44442         
44443         
44444         range.collapse(true);
44445     
44446         nodeRange.collapse(true);
44447      
44448         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
44449         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
44450          
44451         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
44452         
44453         var nodeIsBefore   =  ss == 1;
44454         var nodeIsAfter    = ee == -1;
44455         
44456         if (nodeIsBefore && nodeIsAfter) {
44457             return 0; // outer
44458         }
44459         if (!nodeIsBefore && nodeIsAfter) {
44460             return 1; //right trailed.
44461         }
44462         
44463         if (nodeIsBefore && !nodeIsAfter) {
44464             return 2;  // left trailed.
44465         }
44466         // fully contined.
44467         return 3;
44468     },
44469
44470     // private? - in a new class?
44471     cleanUpPaste :  function()
44472     {
44473         // cleans up the whole document..
44474         Roo.log('cleanuppaste');
44475         
44476         this.cleanUpChildren(this.doc.body);
44477         var clean = this.cleanWordChars(this.doc.body.innerHTML);
44478         if (clean != this.doc.body.innerHTML) {
44479             this.doc.body.innerHTML = clean;
44480         }
44481         
44482     },
44483     
44484     cleanWordChars : function(input) {// change the chars to hex code
44485         var he = Roo.HtmlEditorCore;
44486         
44487         var output = input;
44488         Roo.each(he.swapCodes, function(sw) { 
44489             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
44490             
44491             output = output.replace(swapper, sw[1]);
44492         });
44493         
44494         return output;
44495     },
44496     
44497     
44498     cleanUpChildren : function (n)
44499     {
44500         if (!n.childNodes.length) {
44501             return;
44502         }
44503         for (var i = n.childNodes.length-1; i > -1 ; i--) {
44504            this.cleanUpChild(n.childNodes[i]);
44505         }
44506     },
44507     
44508     
44509         
44510     
44511     cleanUpChild : function (node)
44512     {
44513         var ed = this;
44514         //console.log(node);
44515         if (node.nodeName == "#text") {
44516             // clean up silly Windows -- stuff?
44517             return; 
44518         }
44519         if (node.nodeName == "#comment") {
44520             node.parentNode.removeChild(node);
44521             // clean up silly Windows -- stuff?
44522             return; 
44523         }
44524         var lcname = node.tagName.toLowerCase();
44525         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
44526         // whitelist of tags..
44527         
44528         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
44529             // remove node.
44530             node.parentNode.removeChild(node);
44531             return;
44532             
44533         }
44534         
44535         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
44536         
44537         // spans with no attributes - just remove them..
44538         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
44539             remove_keep_children = true;
44540         }
44541         
44542         // remove <a name=....> as rendering on yahoo mailer is borked with this.
44543         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
44544         
44545         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
44546         //    remove_keep_children = true;
44547         //}
44548         
44549         if (remove_keep_children) {
44550             this.cleanUpChildren(node);
44551             // inserts everything just before this node...
44552             while (node.childNodes.length) {
44553                 var cn = node.childNodes[0];
44554                 node.removeChild(cn);
44555                 node.parentNode.insertBefore(cn, node);
44556             }
44557             node.parentNode.removeChild(node);
44558             return;
44559         }
44560         
44561         if (!node.attributes || !node.attributes.length) {
44562             
44563           
44564             
44565             
44566             this.cleanUpChildren(node);
44567             return;
44568         }
44569         
44570         function cleanAttr(n,v)
44571         {
44572             
44573             if (v.match(/^\./) || v.match(/^\//)) {
44574                 return;
44575             }
44576             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
44577                 return;
44578             }
44579             if (v.match(/^#/)) {
44580                 return;
44581             }
44582             if (v.match(/^\{/)) { // allow template editing.
44583                 return;
44584             }
44585 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
44586             node.removeAttribute(n);
44587             
44588         }
44589         
44590         var cwhite = this.cwhite;
44591         var cblack = this.cblack;
44592             
44593         function cleanStyle(n,v)
44594         {
44595             if (v.match(/expression/)) { //XSS?? should we even bother..
44596                 node.removeAttribute(n);
44597                 return;
44598             }
44599             
44600             var parts = v.split(/;/);
44601             var clean = [];
44602             
44603             Roo.each(parts, function(p) {
44604                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
44605                 if (!p.length) {
44606                     return true;
44607                 }
44608                 var l = p.split(':').shift().replace(/\s+/g,'');
44609                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
44610                 
44611                 if ( cwhite.length && cblack.indexOf(l) > -1) {
44612 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
44613                     //node.removeAttribute(n);
44614                     return true;
44615                 }
44616                 //Roo.log()
44617                 // only allow 'c whitelisted system attributes'
44618                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
44619 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
44620                     //node.removeAttribute(n);
44621                     return true;
44622                 }
44623                 
44624                 
44625                  
44626                 
44627                 clean.push(p);
44628                 return true;
44629             });
44630             if (clean.length) { 
44631                 node.setAttribute(n, clean.join(';'));
44632             } else {
44633                 node.removeAttribute(n);
44634             }
44635             
44636         }
44637         
44638         
44639         for (var i = node.attributes.length-1; i > -1 ; i--) {
44640             var a = node.attributes[i];
44641             //console.log(a);
44642             
44643             if (a.name.toLowerCase().substr(0,2)=='on')  {
44644                 node.removeAttribute(a.name);
44645                 continue;
44646             }
44647             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
44648                 node.removeAttribute(a.name);
44649                 continue;
44650             }
44651             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
44652                 cleanAttr(a.name,a.value); // fixme..
44653                 continue;
44654             }
44655             if (a.name == 'style') {
44656                 cleanStyle(a.name,a.value);
44657                 continue;
44658             }
44659             /// clean up MS crap..
44660             // tecnically this should be a list of valid class'es..
44661             
44662             
44663             if (a.name == 'class') {
44664                 if (a.value.match(/^Mso/)) {
44665                     node.removeAttribute('class');
44666                 }
44667                 
44668                 if (a.value.match(/^body$/)) {
44669                     node.removeAttribute('class');
44670                 }
44671                 continue;
44672             }
44673             
44674             // style cleanup!?
44675             // class cleanup?
44676             
44677         }
44678         
44679         
44680         this.cleanUpChildren(node);
44681         
44682         
44683     },
44684     
44685     /**
44686      * Clean up MS wordisms...
44687      */
44688     cleanWord : function(node)
44689     {
44690         if (!node) {
44691             this.cleanWord(this.doc.body);
44692             return;
44693         }
44694         
44695         if(
44696                 node.nodeName == 'SPAN' &&
44697                 !node.hasAttributes() &&
44698                 node.childNodes.length == 1 &&
44699                 node.firstChild.nodeName == "#text"  
44700         ) {
44701             var textNode = node.firstChild;
44702             node.removeChild(textNode);
44703             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
44704                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
44705             }
44706             node.parentNode.insertBefore(textNode, node);
44707             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
44708                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
44709             }
44710             node.parentNode.removeChild(node);
44711         }
44712         
44713         if (node.nodeName == "#text") {
44714             // clean up silly Windows -- stuff?
44715             return; 
44716         }
44717         if (node.nodeName == "#comment") {
44718             node.parentNode.removeChild(node);
44719             // clean up silly Windows -- stuff?
44720             return; 
44721         }
44722         
44723         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
44724             node.parentNode.removeChild(node);
44725             return;
44726         }
44727         //Roo.log(node.tagName);
44728         // remove - but keep children..
44729         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
44730             //Roo.log('-- removed');
44731             while (node.childNodes.length) {
44732                 var cn = node.childNodes[0];
44733                 node.removeChild(cn);
44734                 node.parentNode.insertBefore(cn, node);
44735                 // move node to parent - and clean it..
44736                 this.cleanWord(cn);
44737             }
44738             node.parentNode.removeChild(node);
44739             /// no need to iterate chidlren = it's got none..
44740             //this.iterateChildren(node, this.cleanWord);
44741             return;
44742         }
44743         // clean styles
44744         if (node.className.length) {
44745             
44746             var cn = node.className.split(/\W+/);
44747             var cna = [];
44748             Roo.each(cn, function(cls) {
44749                 if (cls.match(/Mso[a-zA-Z]+/)) {
44750                     return;
44751                 }
44752                 cna.push(cls);
44753             });
44754             node.className = cna.length ? cna.join(' ') : '';
44755             if (!cna.length) {
44756                 node.removeAttribute("class");
44757             }
44758         }
44759         
44760         if (node.hasAttribute("lang")) {
44761             node.removeAttribute("lang");
44762         }
44763         
44764         if (node.hasAttribute("style")) {
44765             
44766             var styles = node.getAttribute("style").split(";");
44767             var nstyle = [];
44768             Roo.each(styles, function(s) {
44769                 if (!s.match(/:/)) {
44770                     return;
44771                 }
44772                 var kv = s.split(":");
44773                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
44774                     return;
44775                 }
44776                 // what ever is left... we allow.
44777                 nstyle.push(s);
44778             });
44779             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
44780             if (!nstyle.length) {
44781                 node.removeAttribute('style');
44782             }
44783         }
44784         this.iterateChildren(node, this.cleanWord);
44785         
44786         
44787         
44788     },
44789     /**
44790      * iterateChildren of a Node, calling fn each time, using this as the scole..
44791      * @param {DomNode} node node to iterate children of.
44792      * @param {Function} fn method of this class to call on each item.
44793      */
44794     iterateChildren : function(node, fn)
44795     {
44796         if (!node.childNodes.length) {
44797                 return;
44798         }
44799         for (var i = node.childNodes.length-1; i > -1 ; i--) {
44800            fn.call(this, node.childNodes[i])
44801         }
44802     },
44803     
44804     
44805     /**
44806      * cleanTableWidths.
44807      *
44808      * Quite often pasting from word etc.. results in tables with column and widths.
44809      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
44810      *
44811      */
44812     cleanTableWidths : function(node)
44813     {
44814          
44815          
44816         if (!node) {
44817             this.cleanTableWidths(this.doc.body);
44818             return;
44819         }
44820         
44821         // ignore list...
44822         if (node.nodeName == "#text" || node.nodeName == "#comment") {
44823             return; 
44824         }
44825         Roo.log(node.tagName);
44826         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
44827             this.iterateChildren(node, this.cleanTableWidths);
44828             return;
44829         }
44830         if (node.hasAttribute('width')) {
44831             node.removeAttribute('width');
44832         }
44833         
44834          
44835         if (node.hasAttribute("style")) {
44836             // pretty basic...
44837             
44838             var styles = node.getAttribute("style").split(";");
44839             var nstyle = [];
44840             Roo.each(styles, function(s) {
44841                 if (!s.match(/:/)) {
44842                     return;
44843                 }
44844                 var kv = s.split(":");
44845                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
44846                     return;
44847                 }
44848                 // what ever is left... we allow.
44849                 nstyle.push(s);
44850             });
44851             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
44852             if (!nstyle.length) {
44853                 node.removeAttribute('style');
44854             }
44855         }
44856         
44857         this.iterateChildren(node, this.cleanTableWidths);
44858         
44859         
44860     },
44861     
44862     
44863     
44864     
44865     domToHTML : function(currentElement, depth, nopadtext) {
44866         
44867         depth = depth || 0;
44868         nopadtext = nopadtext || false;
44869     
44870         if (!currentElement) {
44871             return this.domToHTML(this.doc.body);
44872         }
44873         
44874         //Roo.log(currentElement);
44875         var j;
44876         var allText = false;
44877         var nodeName = currentElement.nodeName;
44878         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
44879         
44880         if  (nodeName == '#text') {
44881             
44882             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
44883         }
44884         
44885         
44886         var ret = '';
44887         if (nodeName != 'BODY') {
44888              
44889             var i = 0;
44890             // Prints the node tagName, such as <A>, <IMG>, etc
44891             if (tagName) {
44892                 var attr = [];
44893                 for(i = 0; i < currentElement.attributes.length;i++) {
44894                     // quoting?
44895                     var aname = currentElement.attributes.item(i).name;
44896                     if (!currentElement.attributes.item(i).value.length) {
44897                         continue;
44898                     }
44899                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
44900                 }
44901                 
44902                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
44903             } 
44904             else {
44905                 
44906                 // eack
44907             }
44908         } else {
44909             tagName = false;
44910         }
44911         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
44912             return ret;
44913         }
44914         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
44915             nopadtext = true;
44916         }
44917         
44918         
44919         // Traverse the tree
44920         i = 0;
44921         var currentElementChild = currentElement.childNodes.item(i);
44922         var allText = true;
44923         var innerHTML  = '';
44924         lastnode = '';
44925         while (currentElementChild) {
44926             // Formatting code (indent the tree so it looks nice on the screen)
44927             var nopad = nopadtext;
44928             if (lastnode == 'SPAN') {
44929                 nopad  = true;
44930             }
44931             // text
44932             if  (currentElementChild.nodeName == '#text') {
44933                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
44934                 toadd = nopadtext ? toadd : toadd.trim();
44935                 if (!nopad && toadd.length > 80) {
44936                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
44937                 }
44938                 innerHTML  += toadd;
44939                 
44940                 i++;
44941                 currentElementChild = currentElement.childNodes.item(i);
44942                 lastNode = '';
44943                 continue;
44944             }
44945             allText = false;
44946             
44947             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
44948                 
44949             // Recursively traverse the tree structure of the child node
44950             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
44951             lastnode = currentElementChild.nodeName;
44952             i++;
44953             currentElementChild=currentElement.childNodes.item(i);
44954         }
44955         
44956         ret += innerHTML;
44957         
44958         if (!allText) {
44959                 // The remaining code is mostly for formatting the tree
44960             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
44961         }
44962         
44963         
44964         if (tagName) {
44965             ret+= "</"+tagName+">";
44966         }
44967         return ret;
44968         
44969     },
44970         
44971     applyBlacklists : function()
44972     {
44973         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
44974         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
44975         
44976         this.white = [];
44977         this.black = [];
44978         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
44979             if (b.indexOf(tag) > -1) {
44980                 return;
44981             }
44982             this.white.push(tag);
44983             
44984         }, this);
44985         
44986         Roo.each(w, function(tag) {
44987             if (b.indexOf(tag) > -1) {
44988                 return;
44989             }
44990             if (this.white.indexOf(tag) > -1) {
44991                 return;
44992             }
44993             this.white.push(tag);
44994             
44995         }, this);
44996         
44997         
44998         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
44999             if (w.indexOf(tag) > -1) {
45000                 return;
45001             }
45002             this.black.push(tag);
45003             
45004         }, this);
45005         
45006         Roo.each(b, function(tag) {
45007             if (w.indexOf(tag) > -1) {
45008                 return;
45009             }
45010             if (this.black.indexOf(tag) > -1) {
45011                 return;
45012             }
45013             this.black.push(tag);
45014             
45015         }, this);
45016         
45017         
45018         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
45019         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
45020         
45021         this.cwhite = [];
45022         this.cblack = [];
45023         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
45024             if (b.indexOf(tag) > -1) {
45025                 return;
45026             }
45027             this.cwhite.push(tag);
45028             
45029         }, this);
45030         
45031         Roo.each(w, function(tag) {
45032             if (b.indexOf(tag) > -1) {
45033                 return;
45034             }
45035             if (this.cwhite.indexOf(tag) > -1) {
45036                 return;
45037             }
45038             this.cwhite.push(tag);
45039             
45040         }, this);
45041         
45042         
45043         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
45044             if (w.indexOf(tag) > -1) {
45045                 return;
45046             }
45047             this.cblack.push(tag);
45048             
45049         }, this);
45050         
45051         Roo.each(b, function(tag) {
45052             if (w.indexOf(tag) > -1) {
45053                 return;
45054             }
45055             if (this.cblack.indexOf(tag) > -1) {
45056                 return;
45057             }
45058             this.cblack.push(tag);
45059             
45060         }, this);
45061     },
45062     
45063     setStylesheets : function(stylesheets)
45064     {
45065         if(typeof(stylesheets) == 'string'){
45066             Roo.get(this.iframe.contentDocument.head).createChild({
45067                 tag : 'link',
45068                 rel : 'stylesheet',
45069                 type : 'text/css',
45070                 href : stylesheets
45071             });
45072             
45073             return;
45074         }
45075         var _this = this;
45076      
45077         Roo.each(stylesheets, function(s) {
45078             if(!s.length){
45079                 return;
45080             }
45081             
45082             Roo.get(_this.iframe.contentDocument.head).createChild({
45083                 tag : 'link',
45084                 rel : 'stylesheet',
45085                 type : 'text/css',
45086                 href : s
45087             });
45088         });
45089
45090         
45091     },
45092     
45093     removeStylesheets : function()
45094     {
45095         var _this = this;
45096         
45097         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
45098             s.remove();
45099         });
45100     },
45101     
45102     setStyle : function(style)
45103     {
45104         Roo.get(this.iframe.contentDocument.head).createChild({
45105             tag : 'style',
45106             type : 'text/css',
45107             html : style
45108         });
45109
45110         return;
45111     }
45112     
45113     // hide stuff that is not compatible
45114     /**
45115      * @event blur
45116      * @hide
45117      */
45118     /**
45119      * @event change
45120      * @hide
45121      */
45122     /**
45123      * @event focus
45124      * @hide
45125      */
45126     /**
45127      * @event specialkey
45128      * @hide
45129      */
45130     /**
45131      * @cfg {String} fieldClass @hide
45132      */
45133     /**
45134      * @cfg {String} focusClass @hide
45135      */
45136     /**
45137      * @cfg {String} autoCreate @hide
45138      */
45139     /**
45140      * @cfg {String} inputType @hide
45141      */
45142     /**
45143      * @cfg {String} invalidClass @hide
45144      */
45145     /**
45146      * @cfg {String} invalidText @hide
45147      */
45148     /**
45149      * @cfg {String} msgFx @hide
45150      */
45151     /**
45152      * @cfg {String} validateOnBlur @hide
45153      */
45154 });
45155
45156 Roo.HtmlEditorCore.white = [
45157         'area', 'br', 'img', 'input', 'hr', 'wbr',
45158         
45159        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
45160        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
45161        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
45162        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
45163        'table',   'ul',         'xmp', 
45164        
45165        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
45166       'thead',   'tr', 
45167      
45168       'dir', 'menu', 'ol', 'ul', 'dl',
45169        
45170       'embed',  'object'
45171 ];
45172
45173
45174 Roo.HtmlEditorCore.black = [
45175     //    'embed',  'object', // enable - backend responsiblity to clean thiese
45176         'applet', // 
45177         'base',   'basefont', 'bgsound', 'blink',  'body', 
45178         'frame',  'frameset', 'head',    'html',   'ilayer', 
45179         'iframe', 'layer',  'link',     'meta',    'object',   
45180         'script', 'style' ,'title',  'xml' // clean later..
45181 ];
45182 Roo.HtmlEditorCore.clean = [
45183     'script', 'style', 'title', 'xml'
45184 ];
45185 Roo.HtmlEditorCore.remove = [
45186     'font'
45187 ];
45188 // attributes..
45189
45190 Roo.HtmlEditorCore.ablack = [
45191     'on'
45192 ];
45193     
45194 Roo.HtmlEditorCore.aclean = [ 
45195     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
45196 ];
45197
45198 // protocols..
45199 Roo.HtmlEditorCore.pwhite= [
45200         'http',  'https',  'mailto'
45201 ];
45202
45203 // white listed style attributes.
45204 Roo.HtmlEditorCore.cwhite= [
45205       //  'text-align', /// default is to allow most things..
45206       
45207          
45208 //        'font-size'//??
45209 ];
45210
45211 // black listed style attributes.
45212 Roo.HtmlEditorCore.cblack= [
45213       //  'font-size' -- this can be set by the project 
45214 ];
45215
45216
45217 Roo.HtmlEditorCore.swapCodes   =[ 
45218     [    8211, "--" ], 
45219     [    8212, "--" ], 
45220     [    8216,  "'" ],  
45221     [    8217, "'" ],  
45222     [    8220, '"' ],  
45223     [    8221, '"' ],  
45224     [    8226, "*" ],  
45225     [    8230, "..." ]
45226 ]; 
45227
45228     //<script type="text/javascript">
45229
45230 /*
45231  * Ext JS Library 1.1.1
45232  * Copyright(c) 2006-2007, Ext JS, LLC.
45233  * Licence LGPL
45234  * 
45235  */
45236  
45237  
45238 Roo.form.HtmlEditor = function(config){
45239     
45240     
45241     
45242     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
45243     
45244     if (!this.toolbars) {
45245         this.toolbars = [];
45246     }
45247     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
45248     
45249     
45250 };
45251
45252 /**
45253  * @class Roo.form.HtmlEditor
45254  * @extends Roo.form.Field
45255  * Provides a lightweight HTML Editor component.
45256  *
45257  * This has been tested on Fireforx / Chrome.. IE may not be so great..
45258  * 
45259  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
45260  * supported by this editor.</b><br/><br/>
45261  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
45262  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
45263  */
45264 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
45265     /**
45266      * @cfg {Boolean} clearUp
45267      */
45268     clearUp : true,
45269       /**
45270      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
45271      */
45272     toolbars : false,
45273    
45274      /**
45275      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
45276      *                        Roo.resizable.
45277      */
45278     resizable : false,
45279      /**
45280      * @cfg {Number} height (in pixels)
45281      */   
45282     height: 300,
45283    /**
45284      * @cfg {Number} width (in pixels)
45285      */   
45286     width: 500,
45287     
45288     /**
45289      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
45290      * 
45291      */
45292     stylesheets: false,
45293     
45294     
45295      /**
45296      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
45297      * 
45298      */
45299     cblack: false,
45300     /**
45301      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
45302      * 
45303      */
45304     cwhite: false,
45305     
45306      /**
45307      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
45308      * 
45309      */
45310     black: false,
45311     /**
45312      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
45313      * 
45314      */
45315     white: false,
45316     
45317     // id of frame..
45318     frameId: false,
45319     
45320     // private properties
45321     validationEvent : false,
45322     deferHeight: true,
45323     initialized : false,
45324     activated : false,
45325     
45326     onFocus : Roo.emptyFn,
45327     iframePad:3,
45328     hideMode:'offsets',
45329     
45330     actionMode : 'container', // defaults to hiding it...
45331     
45332     defaultAutoCreate : { // modified by initCompnoent..
45333         tag: "textarea",
45334         style:"width:500px;height:300px;",
45335         autocomplete: "new-password"
45336     },
45337
45338     // private
45339     initComponent : function(){
45340         this.addEvents({
45341             /**
45342              * @event initialize
45343              * Fires when the editor is fully initialized (including the iframe)
45344              * @param {HtmlEditor} this
45345              */
45346             initialize: true,
45347             /**
45348              * @event activate
45349              * Fires when the editor is first receives the focus. Any insertion must wait
45350              * until after this event.
45351              * @param {HtmlEditor} this
45352              */
45353             activate: true,
45354              /**
45355              * @event beforesync
45356              * Fires before the textarea is updated with content from the editor iframe. Return false
45357              * to cancel the sync.
45358              * @param {HtmlEditor} this
45359              * @param {String} html
45360              */
45361             beforesync: true,
45362              /**
45363              * @event beforepush
45364              * Fires before the iframe editor is updated with content from the textarea. Return false
45365              * to cancel the push.
45366              * @param {HtmlEditor} this
45367              * @param {String} html
45368              */
45369             beforepush: true,
45370              /**
45371              * @event sync
45372              * Fires when the textarea is updated with content from the editor iframe.
45373              * @param {HtmlEditor} this
45374              * @param {String} html
45375              */
45376             sync: true,
45377              /**
45378              * @event push
45379              * Fires when the iframe editor is updated with content from the textarea.
45380              * @param {HtmlEditor} this
45381              * @param {String} html
45382              */
45383             push: true,
45384              /**
45385              * @event editmodechange
45386              * Fires when the editor switches edit modes
45387              * @param {HtmlEditor} this
45388              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
45389              */
45390             editmodechange: true,
45391             /**
45392              * @event editorevent
45393              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
45394              * @param {HtmlEditor} this
45395              */
45396             editorevent: true,
45397             /**
45398              * @event firstfocus
45399              * Fires when on first focus - needed by toolbars..
45400              * @param {HtmlEditor} this
45401              */
45402             firstfocus: true,
45403             /**
45404              * @event autosave
45405              * Auto save the htmlEditor value as a file into Events
45406              * @param {HtmlEditor} this
45407              */
45408             autosave: true,
45409             /**
45410              * @event savedpreview
45411              * preview the saved version of htmlEditor
45412              * @param {HtmlEditor} this
45413              */
45414             savedpreview: true,
45415             
45416             /**
45417             * @event stylesheetsclick
45418             * Fires when press the Sytlesheets button
45419             * @param {Roo.HtmlEditorCore} this
45420             */
45421             stylesheetsclick: true
45422         });
45423         this.defaultAutoCreate =  {
45424             tag: "textarea",
45425             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
45426             autocomplete: "new-password"
45427         };
45428     },
45429
45430     /**
45431      * Protected method that will not generally be called directly. It
45432      * is called when the editor creates its toolbar. Override this method if you need to
45433      * add custom toolbar buttons.
45434      * @param {HtmlEditor} editor
45435      */
45436     createToolbar : function(editor){
45437         Roo.log("create toolbars");
45438         if (!editor.toolbars || !editor.toolbars.length) {
45439             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
45440         }
45441         
45442         for (var i =0 ; i < editor.toolbars.length;i++) {
45443             editor.toolbars[i] = Roo.factory(
45444                     typeof(editor.toolbars[i]) == 'string' ?
45445                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
45446                 Roo.form.HtmlEditor);
45447             editor.toolbars[i].init(editor);
45448         }
45449          
45450         
45451     },
45452
45453      
45454     // private
45455     onRender : function(ct, position)
45456     {
45457         var _t = this;
45458         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
45459         
45460         this.wrap = this.el.wrap({
45461             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
45462         });
45463         
45464         this.editorcore.onRender(ct, position);
45465          
45466         if (this.resizable) {
45467             this.resizeEl = new Roo.Resizable(this.wrap, {
45468                 pinned : true,
45469                 wrap: true,
45470                 dynamic : true,
45471                 minHeight : this.height,
45472                 height: this.height,
45473                 handles : this.resizable,
45474                 width: this.width,
45475                 listeners : {
45476                     resize : function(r, w, h) {
45477                         _t.onResize(w,h); // -something
45478                     }
45479                 }
45480             });
45481             
45482         }
45483         this.createToolbar(this);
45484        
45485         
45486         if(!this.width){
45487             this.setSize(this.wrap.getSize());
45488         }
45489         if (this.resizeEl) {
45490             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
45491             // should trigger onReize..
45492         }
45493         
45494         this.keyNav = new Roo.KeyNav(this.el, {
45495             
45496             "tab" : function(e){
45497                 e.preventDefault();
45498                 
45499                 var value = this.getValue();
45500                 
45501                 var start = this.el.dom.selectionStart;
45502                 var end = this.el.dom.selectionEnd;
45503                 
45504                 if(!e.shiftKey){
45505                     
45506                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
45507                     this.el.dom.setSelectionRange(end + 1, end + 1);
45508                     return;
45509                 }
45510                 
45511                 var f = value.substring(0, start).split("\t");
45512                 
45513                 if(f.pop().length != 0){
45514                     return;
45515                 }
45516                 
45517                 this.setValue(f.join("\t") + value.substring(end));
45518                 this.el.dom.setSelectionRange(start - 1, start - 1);
45519                 
45520             },
45521             
45522             "home" : function(e){
45523                 e.preventDefault();
45524                 
45525                 var curr = this.el.dom.selectionStart;
45526                 var lines = this.getValue().split("\n");
45527                 
45528                 if(!lines.length){
45529                     return;
45530                 }
45531                 
45532                 if(e.ctrlKey){
45533                     this.el.dom.setSelectionRange(0, 0);
45534                     return;
45535                 }
45536                 
45537                 var pos = 0;
45538                 
45539                 for (var i = 0; i < lines.length;i++) {
45540                     pos += lines[i].length;
45541                     
45542                     if(i != 0){
45543                         pos += 1;
45544                     }
45545                     
45546                     if(pos < curr){
45547                         continue;
45548                     }
45549                     
45550                     pos -= lines[i].length;
45551                     
45552                     break;
45553                 }
45554                 
45555                 if(!e.shiftKey){
45556                     this.el.dom.setSelectionRange(pos, pos);
45557                     return;
45558                 }
45559                 
45560                 this.el.dom.selectionStart = pos;
45561                 this.el.dom.selectionEnd = curr;
45562             },
45563             
45564             "end" : function(e){
45565                 e.preventDefault();
45566                 
45567                 var curr = this.el.dom.selectionStart;
45568                 var lines = this.getValue().split("\n");
45569                 
45570                 if(!lines.length){
45571                     return;
45572                 }
45573                 
45574                 if(e.ctrlKey){
45575                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
45576                     return;
45577                 }
45578                 
45579                 var pos = 0;
45580                 
45581                 for (var i = 0; i < lines.length;i++) {
45582                     
45583                     pos += lines[i].length;
45584                     
45585                     if(i != 0){
45586                         pos += 1;
45587                     }
45588                     
45589                     if(pos < curr){
45590                         continue;
45591                     }
45592                     
45593                     break;
45594                 }
45595                 
45596                 if(!e.shiftKey){
45597                     this.el.dom.setSelectionRange(pos, pos);
45598                     return;
45599                 }
45600                 
45601                 this.el.dom.selectionStart = curr;
45602                 this.el.dom.selectionEnd = pos;
45603             },
45604
45605             scope : this,
45606
45607             doRelay : function(foo, bar, hname){
45608                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
45609             },
45610
45611             forceKeyDown: true
45612         });
45613         
45614 //        if(this.autosave && this.w){
45615 //            this.autoSaveFn = setInterval(this.autosave, 1000);
45616 //        }
45617     },
45618
45619     // private
45620     onResize : function(w, h)
45621     {
45622         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
45623         var ew = false;
45624         var eh = false;
45625         
45626         if(this.el ){
45627             if(typeof w == 'number'){
45628                 var aw = w - this.wrap.getFrameWidth('lr');
45629                 this.el.setWidth(this.adjustWidth('textarea', aw));
45630                 ew = aw;
45631             }
45632             if(typeof h == 'number'){
45633                 var tbh = 0;
45634                 for (var i =0; i < this.toolbars.length;i++) {
45635                     // fixme - ask toolbars for heights?
45636                     tbh += this.toolbars[i].tb.el.getHeight();
45637                     if (this.toolbars[i].footer) {
45638                         tbh += this.toolbars[i].footer.el.getHeight();
45639                     }
45640                 }
45641                 
45642                 
45643                 
45644                 
45645                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
45646                 ah -= 5; // knock a few pixes off for look..
45647 //                Roo.log(ah);
45648                 this.el.setHeight(this.adjustWidth('textarea', ah));
45649                 var eh = ah;
45650             }
45651         }
45652         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
45653         this.editorcore.onResize(ew,eh);
45654         
45655     },
45656
45657     /**
45658      * Toggles the editor between standard and source edit mode.
45659      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
45660      */
45661     toggleSourceEdit : function(sourceEditMode)
45662     {
45663         this.editorcore.toggleSourceEdit(sourceEditMode);
45664         
45665         if(this.editorcore.sourceEditMode){
45666             Roo.log('editor - showing textarea');
45667             
45668 //            Roo.log('in');
45669 //            Roo.log(this.syncValue());
45670             this.editorcore.syncValue();
45671             this.el.removeClass('x-hidden');
45672             this.el.dom.removeAttribute('tabIndex');
45673             this.el.focus();
45674             
45675             for (var i = 0; i < this.toolbars.length; i++) {
45676                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
45677                     this.toolbars[i].tb.hide();
45678                     this.toolbars[i].footer.hide();
45679                 }
45680             }
45681             
45682         }else{
45683             Roo.log('editor - hiding textarea');
45684 //            Roo.log('out')
45685 //            Roo.log(this.pushValue()); 
45686             this.editorcore.pushValue();
45687             
45688             this.el.addClass('x-hidden');
45689             this.el.dom.setAttribute('tabIndex', -1);
45690             
45691             for (var i = 0; i < this.toolbars.length; i++) {
45692                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
45693                     this.toolbars[i].tb.show();
45694                     this.toolbars[i].footer.show();
45695                 }
45696             }
45697             
45698             //this.deferFocus();
45699         }
45700         
45701         this.setSize(this.wrap.getSize());
45702         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
45703         
45704         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
45705     },
45706  
45707     // private (for BoxComponent)
45708     adjustSize : Roo.BoxComponent.prototype.adjustSize,
45709
45710     // private (for BoxComponent)
45711     getResizeEl : function(){
45712         return this.wrap;
45713     },
45714
45715     // private (for BoxComponent)
45716     getPositionEl : function(){
45717         return this.wrap;
45718     },
45719
45720     // private
45721     initEvents : function(){
45722         this.originalValue = this.getValue();
45723     },
45724
45725     /**
45726      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
45727      * @method
45728      */
45729     markInvalid : Roo.emptyFn,
45730     /**
45731      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
45732      * @method
45733      */
45734     clearInvalid : Roo.emptyFn,
45735
45736     setValue : function(v){
45737         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
45738         this.editorcore.pushValue();
45739     },
45740
45741      
45742     // private
45743     deferFocus : function(){
45744         this.focus.defer(10, this);
45745     },
45746
45747     // doc'ed in Field
45748     focus : function(){
45749         this.editorcore.focus();
45750         
45751     },
45752       
45753
45754     // private
45755     onDestroy : function(){
45756         
45757         
45758         
45759         if(this.rendered){
45760             
45761             for (var i =0; i < this.toolbars.length;i++) {
45762                 // fixme - ask toolbars for heights?
45763                 this.toolbars[i].onDestroy();
45764             }
45765             
45766             this.wrap.dom.innerHTML = '';
45767             this.wrap.remove();
45768         }
45769     },
45770
45771     // private
45772     onFirstFocus : function(){
45773         //Roo.log("onFirstFocus");
45774         this.editorcore.onFirstFocus();
45775          for (var i =0; i < this.toolbars.length;i++) {
45776             this.toolbars[i].onFirstFocus();
45777         }
45778         
45779     },
45780     
45781     // private
45782     syncValue : function()
45783     {
45784         this.editorcore.syncValue();
45785     },
45786     
45787     pushValue : function()
45788     {
45789         this.editorcore.pushValue();
45790     },
45791     
45792     setStylesheets : function(stylesheets)
45793     {
45794         this.editorcore.setStylesheets(stylesheets);
45795     },
45796     
45797     removeStylesheets : function()
45798     {
45799         this.editorcore.removeStylesheets();
45800     }
45801      
45802     
45803     // hide stuff that is not compatible
45804     /**
45805      * @event blur
45806      * @hide
45807      */
45808     /**
45809      * @event change
45810      * @hide
45811      */
45812     /**
45813      * @event focus
45814      * @hide
45815      */
45816     /**
45817      * @event specialkey
45818      * @hide
45819      */
45820     /**
45821      * @cfg {String} fieldClass @hide
45822      */
45823     /**
45824      * @cfg {String} focusClass @hide
45825      */
45826     /**
45827      * @cfg {String} autoCreate @hide
45828      */
45829     /**
45830      * @cfg {String} inputType @hide
45831      */
45832     /**
45833      * @cfg {String} invalidClass @hide
45834      */
45835     /**
45836      * @cfg {String} invalidText @hide
45837      */
45838     /**
45839      * @cfg {String} msgFx @hide
45840      */
45841     /**
45842      * @cfg {String} validateOnBlur @hide
45843      */
45844 });
45845  
45846     // <script type="text/javascript">
45847 /*
45848  * Based on
45849  * Ext JS Library 1.1.1
45850  * Copyright(c) 2006-2007, Ext JS, LLC.
45851  *  
45852  
45853  */
45854
45855 /**
45856  * @class Roo.form.HtmlEditorToolbar1
45857  * Basic Toolbar
45858  * 
45859  * Usage:
45860  *
45861  new Roo.form.HtmlEditor({
45862     ....
45863     toolbars : [
45864         new Roo.form.HtmlEditorToolbar1({
45865             disable : { fonts: 1 , format: 1, ..., ... , ...],
45866             btns : [ .... ]
45867         })
45868     }
45869      
45870  * 
45871  * @cfg {Object} disable List of elements to disable..
45872  * @cfg {Array} btns List of additional buttons.
45873  * 
45874  * 
45875  * NEEDS Extra CSS? 
45876  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
45877  */
45878  
45879 Roo.form.HtmlEditor.ToolbarStandard = function(config)
45880 {
45881     
45882     Roo.apply(this, config);
45883     
45884     // default disabled, based on 'good practice'..
45885     this.disable = this.disable || {};
45886     Roo.applyIf(this.disable, {
45887         fontSize : true,
45888         colors : true,
45889         specialElements : true
45890     });
45891     
45892     
45893     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
45894     // dont call parent... till later.
45895 }
45896
45897 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
45898     
45899     tb: false,
45900     
45901     rendered: false,
45902     
45903     editor : false,
45904     editorcore : false,
45905     /**
45906      * @cfg {Object} disable  List of toolbar elements to disable
45907          
45908      */
45909     disable : false,
45910     
45911     
45912      /**
45913      * @cfg {String} createLinkText The default text for the create link prompt
45914      */
45915     createLinkText : 'Please enter the URL for the link:',
45916     /**
45917      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
45918      */
45919     defaultLinkValue : 'http:/'+'/',
45920    
45921     
45922       /**
45923      * @cfg {Array} fontFamilies An array of available font families
45924      */
45925     fontFamilies : [
45926         'Arial',
45927         'Courier New',
45928         'Tahoma',
45929         'Times New Roman',
45930         'Verdana'
45931     ],
45932     
45933     specialChars : [
45934            "&#169;",
45935           "&#174;",     
45936           "&#8482;",    
45937           "&#163;" ,    
45938          // "&#8212;",    
45939           "&#8230;",    
45940           "&#247;" ,    
45941         //  "&#225;" ,     ?? a acute?
45942            "&#8364;"    , //Euro
45943        //   "&#8220;"    ,
45944         //  "&#8221;"    ,
45945         //  "&#8226;"    ,
45946           "&#176;"  //   , // degrees
45947
45948          // "&#233;"     , // e ecute
45949          // "&#250;"     , // u ecute?
45950     ],
45951     
45952     specialElements : [
45953         {
45954             text: "Insert Table",
45955             xtype: 'MenuItem',
45956             xns : Roo.Menu,
45957             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
45958                 
45959         },
45960         {    
45961             text: "Insert Image",
45962             xtype: 'MenuItem',
45963             xns : Roo.Menu,
45964             ihtml : '<img src="about:blank"/>'
45965             
45966         }
45967         
45968          
45969     ],
45970     
45971     
45972     inputElements : [ 
45973             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
45974             "input:submit", "input:button", "select", "textarea", "label" ],
45975     formats : [
45976         ["p"] ,  
45977         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
45978         ["pre"],[ "code"], 
45979         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
45980         ['div'],['span'],
45981         ['sup'],['sub']
45982     ],
45983     
45984     cleanStyles : [
45985         "font-size"
45986     ],
45987      /**
45988      * @cfg {String} defaultFont default font to use.
45989      */
45990     defaultFont: 'tahoma',
45991    
45992     fontSelect : false,
45993     
45994     
45995     formatCombo : false,
45996     
45997     init : function(editor)
45998     {
45999         this.editor = editor;
46000         this.editorcore = editor.editorcore ? editor.editorcore : editor;
46001         var editorcore = this.editorcore;
46002         
46003         var _t = this;
46004         
46005         var fid = editorcore.frameId;
46006         var etb = this;
46007         function btn(id, toggle, handler){
46008             var xid = fid + '-'+ id ;
46009             return {
46010                 id : xid,
46011                 cmd : id,
46012                 cls : 'x-btn-icon x-edit-'+id,
46013                 enableToggle:toggle !== false,
46014                 scope: _t, // was editor...
46015                 handler:handler||_t.relayBtnCmd,
46016                 clickEvent:'mousedown',
46017                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
46018                 tabIndex:-1
46019             };
46020         }
46021         
46022         
46023         
46024         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
46025         this.tb = tb;
46026          // stop form submits
46027         tb.el.on('click', function(e){
46028             e.preventDefault(); // what does this do?
46029         });
46030
46031         if(!this.disable.font) { // && !Roo.isSafari){
46032             /* why no safari for fonts 
46033             editor.fontSelect = tb.el.createChild({
46034                 tag:'select',
46035                 tabIndex: -1,
46036                 cls:'x-font-select',
46037                 html: this.createFontOptions()
46038             });
46039             
46040             editor.fontSelect.on('change', function(){
46041                 var font = editor.fontSelect.dom.value;
46042                 editor.relayCmd('fontname', font);
46043                 editor.deferFocus();
46044             }, editor);
46045             
46046             tb.add(
46047                 editor.fontSelect.dom,
46048                 '-'
46049             );
46050             */
46051             
46052         };
46053         if(!this.disable.formats){
46054             this.formatCombo = new Roo.form.ComboBox({
46055                 store: new Roo.data.SimpleStore({
46056                     id : 'tag',
46057                     fields: ['tag'],
46058                     data : this.formats // from states.js
46059                 }),
46060                 blockFocus : true,
46061                 name : '',
46062                 //autoCreate : {tag: "div",  size: "20"},
46063                 displayField:'tag',
46064                 typeAhead: false,
46065                 mode: 'local',
46066                 editable : false,
46067                 triggerAction: 'all',
46068                 emptyText:'Add tag',
46069                 selectOnFocus:true,
46070                 width:135,
46071                 listeners : {
46072                     'select': function(c, r, i) {
46073                         editorcore.insertTag(r.get('tag'));
46074                         editor.focus();
46075                     }
46076                 }
46077
46078             });
46079             tb.addField(this.formatCombo);
46080             
46081         }
46082         
46083         if(!this.disable.format){
46084             tb.add(
46085                 btn('bold'),
46086                 btn('italic'),
46087                 btn('underline'),
46088                 btn('strikethrough')
46089             );
46090         };
46091         if(!this.disable.fontSize){
46092             tb.add(
46093                 '-',
46094                 
46095                 
46096                 btn('increasefontsize', false, editorcore.adjustFont),
46097                 btn('decreasefontsize', false, editorcore.adjustFont)
46098             );
46099         };
46100         
46101         
46102         if(!this.disable.colors){
46103             tb.add(
46104                 '-', {
46105                     id:editorcore.frameId +'-forecolor',
46106                     cls:'x-btn-icon x-edit-forecolor',
46107                     clickEvent:'mousedown',
46108                     tooltip: this.buttonTips['forecolor'] || undefined,
46109                     tabIndex:-1,
46110                     menu : new Roo.menu.ColorMenu({
46111                         allowReselect: true,
46112                         focus: Roo.emptyFn,
46113                         value:'000000',
46114                         plain:true,
46115                         selectHandler: function(cp, color){
46116                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
46117                             editor.deferFocus();
46118                         },
46119                         scope: editorcore,
46120                         clickEvent:'mousedown'
46121                     })
46122                 }, {
46123                     id:editorcore.frameId +'backcolor',
46124                     cls:'x-btn-icon x-edit-backcolor',
46125                     clickEvent:'mousedown',
46126                     tooltip: this.buttonTips['backcolor'] || undefined,
46127                     tabIndex:-1,
46128                     menu : new Roo.menu.ColorMenu({
46129                         focus: Roo.emptyFn,
46130                         value:'FFFFFF',
46131                         plain:true,
46132                         allowReselect: true,
46133                         selectHandler: function(cp, color){
46134                             if(Roo.isGecko){
46135                                 editorcore.execCmd('useCSS', false);
46136                                 editorcore.execCmd('hilitecolor', color);
46137                                 editorcore.execCmd('useCSS', true);
46138                                 editor.deferFocus();
46139                             }else{
46140                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
46141                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
46142                                 editor.deferFocus();
46143                             }
46144                         },
46145                         scope:editorcore,
46146                         clickEvent:'mousedown'
46147                     })
46148                 }
46149             );
46150         };
46151         // now add all the items...
46152         
46153
46154         if(!this.disable.alignments){
46155             tb.add(
46156                 '-',
46157                 btn('justifyleft'),
46158                 btn('justifycenter'),
46159                 btn('justifyright')
46160             );
46161         };
46162
46163         //if(!Roo.isSafari){
46164             if(!this.disable.links){
46165                 tb.add(
46166                     '-',
46167                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
46168                 );
46169             };
46170
46171             if(!this.disable.lists){
46172                 tb.add(
46173                     '-',
46174                     btn('insertorderedlist'),
46175                     btn('insertunorderedlist')
46176                 );
46177             }
46178             if(!this.disable.sourceEdit){
46179                 tb.add(
46180                     '-',
46181                     btn('sourceedit', true, function(btn){
46182                         this.toggleSourceEdit(btn.pressed);
46183                     })
46184                 );
46185             }
46186         //}
46187         
46188         var smenu = { };
46189         // special menu.. - needs to be tidied up..
46190         if (!this.disable.special) {
46191             smenu = {
46192                 text: "&#169;",
46193                 cls: 'x-edit-none',
46194                 
46195                 menu : {
46196                     items : []
46197                 }
46198             };
46199             for (var i =0; i < this.specialChars.length; i++) {
46200                 smenu.menu.items.push({
46201                     
46202                     html: this.specialChars[i],
46203                     handler: function(a,b) {
46204                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
46205                         //editor.insertAtCursor(a.html);
46206                         
46207                     },
46208                     tabIndex:-1
46209                 });
46210             }
46211             
46212             
46213             tb.add(smenu);
46214             
46215             
46216         }
46217         
46218         var cmenu = { };
46219         if (!this.disable.cleanStyles) {
46220             cmenu = {
46221                 cls: 'x-btn-icon x-btn-clear',
46222                 
46223                 menu : {
46224                     items : []
46225                 }
46226             };
46227             for (var i =0; i < this.cleanStyles.length; i++) {
46228                 cmenu.menu.items.push({
46229                     actiontype : this.cleanStyles[i],
46230                     html: 'Remove ' + this.cleanStyles[i],
46231                     handler: function(a,b) {
46232 //                        Roo.log(a);
46233 //                        Roo.log(b);
46234                         var c = Roo.get(editorcore.doc.body);
46235                         c.select('[style]').each(function(s) {
46236                             s.dom.style.removeProperty(a.actiontype);
46237                         });
46238                         editorcore.syncValue();
46239                     },
46240                     tabIndex:-1
46241                 });
46242             }
46243              cmenu.menu.items.push({
46244                 actiontype : 'tablewidths',
46245                 html: 'Remove Table Widths',
46246                 handler: function(a,b) {
46247                     editorcore.cleanTableWidths();
46248                     editorcore.syncValue();
46249                 },
46250                 tabIndex:-1
46251             });
46252             cmenu.menu.items.push({
46253                 actiontype : 'word',
46254                 html: 'Remove MS Word Formating',
46255                 handler: function(a,b) {
46256                     editorcore.cleanWord();
46257                     editorcore.syncValue();
46258                 },
46259                 tabIndex:-1
46260             });
46261             
46262             cmenu.menu.items.push({
46263                 actiontype : 'all',
46264                 html: 'Remove All Styles',
46265                 handler: function(a,b) {
46266                     
46267                     var c = Roo.get(editorcore.doc.body);
46268                     c.select('[style]').each(function(s) {
46269                         s.dom.removeAttribute('style');
46270                     });
46271                     editorcore.syncValue();
46272                 },
46273                 tabIndex:-1
46274             });
46275             
46276             cmenu.menu.items.push({
46277                 actiontype : 'all',
46278                 html: 'Remove All CSS Classes',
46279                 handler: function(a,b) {
46280                     
46281                     var c = Roo.get(editorcore.doc.body);
46282                     c.select('[class]').each(function(s) {
46283                         s.dom.removeAttribute('class');
46284                     });
46285                     editorcore.cleanWord();
46286                     editorcore.syncValue();
46287                 },
46288                 tabIndex:-1
46289             });
46290             
46291              cmenu.menu.items.push({
46292                 actiontype : 'tidy',
46293                 html: 'Tidy HTML Source',
46294                 handler: function(a,b) {
46295                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
46296                     editorcore.syncValue();
46297                 },
46298                 tabIndex:-1
46299             });
46300             
46301             
46302             tb.add(cmenu);
46303         }
46304          
46305         if (!this.disable.specialElements) {
46306             var semenu = {
46307                 text: "Other;",
46308                 cls: 'x-edit-none',
46309                 menu : {
46310                     items : []
46311                 }
46312             };
46313             for (var i =0; i < this.specialElements.length; i++) {
46314                 semenu.menu.items.push(
46315                     Roo.apply({ 
46316                         handler: function(a,b) {
46317                             editor.insertAtCursor(this.ihtml);
46318                         }
46319                     }, this.specialElements[i])
46320                 );
46321                     
46322             }
46323             
46324             tb.add(semenu);
46325             
46326             
46327         }
46328          
46329         
46330         if (this.btns) {
46331             for(var i =0; i< this.btns.length;i++) {
46332                 var b = Roo.factory(this.btns[i],Roo.form);
46333                 b.cls =  'x-edit-none';
46334                 
46335                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
46336                     b.cls += ' x-init-enable';
46337                 }
46338                 
46339                 b.scope = editorcore;
46340                 tb.add(b);
46341             }
46342         
46343         }
46344         
46345         
46346         
46347         // disable everything...
46348         
46349         this.tb.items.each(function(item){
46350             
46351            if(
46352                 item.id != editorcore.frameId+ '-sourceedit' && 
46353                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
46354             ){
46355                 
46356                 item.disable();
46357             }
46358         });
46359         this.rendered = true;
46360         
46361         // the all the btns;
46362         editor.on('editorevent', this.updateToolbar, this);
46363         // other toolbars need to implement this..
46364         //editor.on('editmodechange', this.updateToolbar, this);
46365     },
46366     
46367     
46368     relayBtnCmd : function(btn) {
46369         this.editorcore.relayCmd(btn.cmd);
46370     },
46371     // private used internally
46372     createLink : function(){
46373         Roo.log("create link?");
46374         var url = prompt(this.createLinkText, this.defaultLinkValue);
46375         if(url && url != 'http:/'+'/'){
46376             this.editorcore.relayCmd('createlink', url);
46377         }
46378     },
46379
46380     
46381     /**
46382      * Protected method that will not generally be called directly. It triggers
46383      * a toolbar update by reading the markup state of the current selection in the editor.
46384      */
46385     updateToolbar: function(){
46386
46387         if(!this.editorcore.activated){
46388             this.editor.onFirstFocus();
46389             return;
46390         }
46391
46392         var btns = this.tb.items.map, 
46393             doc = this.editorcore.doc,
46394             frameId = this.editorcore.frameId;
46395
46396         if(!this.disable.font && !Roo.isSafari){
46397             /*
46398             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
46399             if(name != this.fontSelect.dom.value){
46400                 this.fontSelect.dom.value = name;
46401             }
46402             */
46403         }
46404         if(!this.disable.format){
46405             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
46406             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
46407             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
46408             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
46409         }
46410         if(!this.disable.alignments){
46411             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
46412             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
46413             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
46414         }
46415         if(!Roo.isSafari && !this.disable.lists){
46416             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
46417             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
46418         }
46419         
46420         var ans = this.editorcore.getAllAncestors();
46421         if (this.formatCombo) {
46422             
46423             
46424             var store = this.formatCombo.store;
46425             this.formatCombo.setValue("");
46426             for (var i =0; i < ans.length;i++) {
46427                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
46428                     // select it..
46429                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
46430                     break;
46431                 }
46432             }
46433         }
46434         
46435         
46436         
46437         // hides menus... - so this cant be on a menu...
46438         Roo.menu.MenuMgr.hideAll();
46439
46440         //this.editorsyncValue();
46441     },
46442    
46443     
46444     createFontOptions : function(){
46445         var buf = [], fs = this.fontFamilies, ff, lc;
46446         
46447         
46448         
46449         for(var i = 0, len = fs.length; i< len; i++){
46450             ff = fs[i];
46451             lc = ff.toLowerCase();
46452             buf.push(
46453                 '<option value="',lc,'" style="font-family:',ff,';"',
46454                     (this.defaultFont == lc ? ' selected="true">' : '>'),
46455                     ff,
46456                 '</option>'
46457             );
46458         }
46459         return buf.join('');
46460     },
46461     
46462     toggleSourceEdit : function(sourceEditMode){
46463         
46464         Roo.log("toolbar toogle");
46465         if(sourceEditMode === undefined){
46466             sourceEditMode = !this.sourceEditMode;
46467         }
46468         this.sourceEditMode = sourceEditMode === true;
46469         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
46470         // just toggle the button?
46471         if(btn.pressed !== this.sourceEditMode){
46472             btn.toggle(this.sourceEditMode);
46473             return;
46474         }
46475         
46476         if(sourceEditMode){
46477             Roo.log("disabling buttons");
46478             this.tb.items.each(function(item){
46479                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
46480                     item.disable();
46481                 }
46482             });
46483           
46484         }else{
46485             Roo.log("enabling buttons");
46486             if(this.editorcore.initialized){
46487                 this.tb.items.each(function(item){
46488                     item.enable();
46489                 });
46490             }
46491             
46492         }
46493         Roo.log("calling toggole on editor");
46494         // tell the editor that it's been pressed..
46495         this.editor.toggleSourceEdit(sourceEditMode);
46496        
46497     },
46498      /**
46499      * Object collection of toolbar tooltips for the buttons in the editor. The key
46500      * is the command id associated with that button and the value is a valid QuickTips object.
46501      * For example:
46502 <pre><code>
46503 {
46504     bold : {
46505         title: 'Bold (Ctrl+B)',
46506         text: 'Make the selected text bold.',
46507         cls: 'x-html-editor-tip'
46508     },
46509     italic : {
46510         title: 'Italic (Ctrl+I)',
46511         text: 'Make the selected text italic.',
46512         cls: 'x-html-editor-tip'
46513     },
46514     ...
46515 </code></pre>
46516     * @type Object
46517      */
46518     buttonTips : {
46519         bold : {
46520             title: 'Bold (Ctrl+B)',
46521             text: 'Make the selected text bold.',
46522             cls: 'x-html-editor-tip'
46523         },
46524         italic : {
46525             title: 'Italic (Ctrl+I)',
46526             text: 'Make the selected text italic.',
46527             cls: 'x-html-editor-tip'
46528         },
46529         underline : {
46530             title: 'Underline (Ctrl+U)',
46531             text: 'Underline the selected text.',
46532             cls: 'x-html-editor-tip'
46533         },
46534         strikethrough : {
46535             title: 'Strikethrough',
46536             text: 'Strikethrough the selected text.',
46537             cls: 'x-html-editor-tip'
46538         },
46539         increasefontsize : {
46540             title: 'Grow Text',
46541             text: 'Increase the font size.',
46542             cls: 'x-html-editor-tip'
46543         },
46544         decreasefontsize : {
46545             title: 'Shrink Text',
46546             text: 'Decrease the font size.',
46547             cls: 'x-html-editor-tip'
46548         },
46549         backcolor : {
46550             title: 'Text Highlight Color',
46551             text: 'Change the background color of the selected text.',
46552             cls: 'x-html-editor-tip'
46553         },
46554         forecolor : {
46555             title: 'Font Color',
46556             text: 'Change the color of the selected text.',
46557             cls: 'x-html-editor-tip'
46558         },
46559         justifyleft : {
46560             title: 'Align Text Left',
46561             text: 'Align text to the left.',
46562             cls: 'x-html-editor-tip'
46563         },
46564         justifycenter : {
46565             title: 'Center Text',
46566             text: 'Center text in the editor.',
46567             cls: 'x-html-editor-tip'
46568         },
46569         justifyright : {
46570             title: 'Align Text Right',
46571             text: 'Align text to the right.',
46572             cls: 'x-html-editor-tip'
46573         },
46574         insertunorderedlist : {
46575             title: 'Bullet List',
46576             text: 'Start a bulleted list.',
46577             cls: 'x-html-editor-tip'
46578         },
46579         insertorderedlist : {
46580             title: 'Numbered List',
46581             text: 'Start a numbered list.',
46582             cls: 'x-html-editor-tip'
46583         },
46584         createlink : {
46585             title: 'Hyperlink',
46586             text: 'Make the selected text a hyperlink.',
46587             cls: 'x-html-editor-tip'
46588         },
46589         sourceedit : {
46590             title: 'Source Edit',
46591             text: 'Switch to source editing mode.',
46592             cls: 'x-html-editor-tip'
46593         }
46594     },
46595     // private
46596     onDestroy : function(){
46597         if(this.rendered){
46598             
46599             this.tb.items.each(function(item){
46600                 if(item.menu){
46601                     item.menu.removeAll();
46602                     if(item.menu.el){
46603                         item.menu.el.destroy();
46604                     }
46605                 }
46606                 item.destroy();
46607             });
46608              
46609         }
46610     },
46611     onFirstFocus: function() {
46612         this.tb.items.each(function(item){
46613            item.enable();
46614         });
46615     }
46616 });
46617
46618
46619
46620
46621 // <script type="text/javascript">
46622 /*
46623  * Based on
46624  * Ext JS Library 1.1.1
46625  * Copyright(c) 2006-2007, Ext JS, LLC.
46626  *  
46627  
46628  */
46629
46630  
46631 /**
46632  * @class Roo.form.HtmlEditor.ToolbarContext
46633  * Context Toolbar
46634  * 
46635  * Usage:
46636  *
46637  new Roo.form.HtmlEditor({
46638     ....
46639     toolbars : [
46640         { xtype: 'ToolbarStandard', styles : {} }
46641         { xtype: 'ToolbarContext', disable : {} }
46642     ]
46643 })
46644
46645      
46646  * 
46647  * @config : {Object} disable List of elements to disable.. (not done yet.)
46648  * @config : {Object} styles  Map of styles available.
46649  * 
46650  */
46651
46652 Roo.form.HtmlEditor.ToolbarContext = function(config)
46653 {
46654     
46655     Roo.apply(this, config);
46656     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
46657     // dont call parent... till later.
46658     this.styles = this.styles || {};
46659 }
46660
46661  
46662
46663 Roo.form.HtmlEditor.ToolbarContext.types = {
46664     'IMG' : {
46665         width : {
46666             title: "Width",
46667             width: 40
46668         },
46669         height:  {
46670             title: "Height",
46671             width: 40
46672         },
46673         align: {
46674             title: "Align",
46675             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
46676             width : 80
46677             
46678         },
46679         border: {
46680             title: "Border",
46681             width: 40
46682         },
46683         alt: {
46684             title: "Alt",
46685             width: 120
46686         },
46687         src : {
46688             title: "Src",
46689             width: 220
46690         }
46691         
46692     },
46693     'A' : {
46694         name : {
46695             title: "Name",
46696             width: 50
46697         },
46698         target:  {
46699             title: "Target",
46700             width: 120
46701         },
46702         href:  {
46703             title: "Href",
46704             width: 220
46705         } // border?
46706         
46707     },
46708     'TABLE' : {
46709         rows : {
46710             title: "Rows",
46711             width: 20
46712         },
46713         cols : {
46714             title: "Cols",
46715             width: 20
46716         },
46717         width : {
46718             title: "Width",
46719             width: 40
46720         },
46721         height : {
46722             title: "Height",
46723             width: 40
46724         },
46725         border : {
46726             title: "Border",
46727             width: 20
46728         }
46729     },
46730     'TD' : {
46731         width : {
46732             title: "Width",
46733             width: 40
46734         },
46735         height : {
46736             title: "Height",
46737             width: 40
46738         },   
46739         align: {
46740             title: "Align",
46741             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
46742             width: 80
46743         },
46744         valign: {
46745             title: "Valign",
46746             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
46747             width: 80
46748         },
46749         colspan: {
46750             title: "Colspan",
46751             width: 20
46752             
46753         },
46754          'font-family'  : {
46755             title : "Font",
46756             style : 'fontFamily',
46757             displayField: 'display',
46758             optname : 'font-family',
46759             width: 140
46760         }
46761     },
46762     'INPUT' : {
46763         name : {
46764             title: "name",
46765             width: 120
46766         },
46767         value : {
46768             title: "Value",
46769             width: 120
46770         },
46771         width : {
46772             title: "Width",
46773             width: 40
46774         }
46775     },
46776     'LABEL' : {
46777         'for' : {
46778             title: "For",
46779             width: 120
46780         }
46781     },
46782     'TEXTAREA' : {
46783           name : {
46784             title: "name",
46785             width: 120
46786         },
46787         rows : {
46788             title: "Rows",
46789             width: 20
46790         },
46791         cols : {
46792             title: "Cols",
46793             width: 20
46794         }
46795     },
46796     'SELECT' : {
46797         name : {
46798             title: "name",
46799             width: 120
46800         },
46801         selectoptions : {
46802             title: "Options",
46803             width: 200
46804         }
46805     },
46806     
46807     // should we really allow this??
46808     // should this just be 
46809     'BODY' : {
46810         title : {
46811             title: "Title",
46812             width: 200,
46813             disabled : true
46814         }
46815     },
46816     'SPAN' : {
46817         'font-family'  : {
46818             title : "Font",
46819             style : 'fontFamily',
46820             displayField: 'display',
46821             optname : 'font-family',
46822             width: 140
46823         }
46824     },
46825     'DIV' : {
46826         'font-family'  : {
46827             title : "Font",
46828             style : 'fontFamily',
46829             displayField: 'display',
46830             optname : 'font-family',
46831             width: 140
46832         }
46833     },
46834      'P' : {
46835         'font-family'  : {
46836             title : "Font",
46837             style : 'fontFamily',
46838             displayField: 'display',
46839             optname : 'font-family',
46840             width: 140
46841         }
46842     },
46843     
46844     '*' : {
46845         // empty..
46846     }
46847
46848 };
46849
46850 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
46851 Roo.form.HtmlEditor.ToolbarContext.stores = false;
46852
46853 Roo.form.HtmlEditor.ToolbarContext.options = {
46854         'font-family'  : [ 
46855                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
46856                 [ 'Courier New', 'Courier New'],
46857                 [ 'Tahoma', 'Tahoma'],
46858                 [ 'Times New Roman,serif', 'Times'],
46859                 [ 'Verdana','Verdana' ]
46860         ]
46861 };
46862
46863 // fixme - these need to be configurable..
46864  
46865
46866 //Roo.form.HtmlEditor.ToolbarContext.types
46867
46868
46869 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
46870     
46871     tb: false,
46872     
46873     rendered: false,
46874     
46875     editor : false,
46876     editorcore : false,
46877     /**
46878      * @cfg {Object} disable  List of toolbar elements to disable
46879          
46880      */
46881     disable : false,
46882     /**
46883      * @cfg {Object} styles List of styles 
46884      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
46885      *
46886      * These must be defined in the page, so they get rendered correctly..
46887      * .headline { }
46888      * TD.underline { }
46889      * 
46890      */
46891     styles : false,
46892     
46893     options: false,
46894     
46895     toolbars : false,
46896     
46897     init : function(editor)
46898     {
46899         this.editor = editor;
46900         this.editorcore = editor.editorcore ? editor.editorcore : editor;
46901         var editorcore = this.editorcore;
46902         
46903         var fid = editorcore.frameId;
46904         var etb = this;
46905         function btn(id, toggle, handler){
46906             var xid = fid + '-'+ id ;
46907             return {
46908                 id : xid,
46909                 cmd : id,
46910                 cls : 'x-btn-icon x-edit-'+id,
46911                 enableToggle:toggle !== false,
46912                 scope: editorcore, // was editor...
46913                 handler:handler||editorcore.relayBtnCmd,
46914                 clickEvent:'mousedown',
46915                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
46916                 tabIndex:-1
46917             };
46918         }
46919         // create a new element.
46920         var wdiv = editor.wrap.createChild({
46921                 tag: 'div'
46922             }, editor.wrap.dom.firstChild.nextSibling, true);
46923         
46924         // can we do this more than once??
46925         
46926          // stop form submits
46927       
46928  
46929         // disable everything...
46930         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
46931         this.toolbars = {};
46932            
46933         for (var i in  ty) {
46934           
46935             this.toolbars[i] = this.buildToolbar(ty[i],i);
46936         }
46937         this.tb = this.toolbars.BODY;
46938         this.tb.el.show();
46939         this.buildFooter();
46940         this.footer.show();
46941         editor.on('hide', function( ) { this.footer.hide() }, this);
46942         editor.on('show', function( ) { this.footer.show() }, this);
46943         
46944          
46945         this.rendered = true;
46946         
46947         // the all the btns;
46948         editor.on('editorevent', this.updateToolbar, this);
46949         // other toolbars need to implement this..
46950         //editor.on('editmodechange', this.updateToolbar, this);
46951     },
46952     
46953     
46954     
46955     /**
46956      * Protected method that will not generally be called directly. It triggers
46957      * a toolbar update by reading the markup state of the current selection in the editor.
46958      *
46959      * Note you can force an update by calling on('editorevent', scope, false)
46960      */
46961     updateToolbar: function(editor,ev,sel){
46962
46963         //Roo.log(ev);
46964         // capture mouse up - this is handy for selecting images..
46965         // perhaps should go somewhere else...
46966         if(!this.editorcore.activated){
46967              this.editor.onFirstFocus();
46968             return;
46969         }
46970         
46971         
46972         
46973         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
46974         // selectNode - might want to handle IE?
46975         if (ev &&
46976             (ev.type == 'mouseup' || ev.type == 'click' ) &&
46977             ev.target && ev.target.tagName == 'IMG') {
46978             // they have click on an image...
46979             // let's see if we can change the selection...
46980             sel = ev.target;
46981          
46982               var nodeRange = sel.ownerDocument.createRange();
46983             try {
46984                 nodeRange.selectNode(sel);
46985             } catch (e) {
46986                 nodeRange.selectNodeContents(sel);
46987             }
46988             //nodeRange.collapse(true);
46989             var s = this.editorcore.win.getSelection();
46990             s.removeAllRanges();
46991             s.addRange(nodeRange);
46992         }  
46993         
46994       
46995         var updateFooter = sel ? false : true;
46996         
46997         
46998         var ans = this.editorcore.getAllAncestors();
46999         
47000         // pick
47001         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
47002         
47003         if (!sel) { 
47004             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
47005             sel = sel ? sel : this.editorcore.doc.body;
47006             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
47007             
47008         }
47009         // pick a menu that exists..
47010         var tn = sel.tagName.toUpperCase();
47011         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
47012         
47013         tn = sel.tagName.toUpperCase();
47014         
47015         var lastSel = this.tb.selectedNode;
47016         
47017         this.tb.selectedNode = sel;
47018         
47019         // if current menu does not match..
47020         
47021         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode) || ev === false) {
47022                 
47023             this.tb.el.hide();
47024             ///console.log("show: " + tn);
47025             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
47026             this.tb.el.show();
47027             // update name
47028             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
47029             
47030             
47031             // update attributes
47032             if (this.tb.fields) {
47033                 this.tb.fields.each(function(e) {
47034                     if (e.stylename) {
47035                         e.setValue(sel.style[e.stylename]);
47036                         return;
47037                     } 
47038                    e.setValue(sel.getAttribute(e.attrname));
47039                 });
47040             }
47041             
47042             var hasStyles = false;
47043             for(var i in this.styles) {
47044                 hasStyles = true;
47045                 break;
47046             }
47047             
47048             // update styles
47049             if (hasStyles) { 
47050                 var st = this.tb.fields.item(0);
47051                 
47052                 st.store.removeAll();
47053                
47054                 
47055                 var cn = sel.className.split(/\s+/);
47056                 
47057                 var avs = [];
47058                 if (this.styles['*']) {
47059                     
47060                     Roo.each(this.styles['*'], function(v) {
47061                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
47062                     });
47063                 }
47064                 if (this.styles[tn]) { 
47065                     Roo.each(this.styles[tn], function(v) {
47066                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
47067                     });
47068                 }
47069                 
47070                 st.store.loadData(avs);
47071                 st.collapse();
47072                 st.setValue(cn);
47073             }
47074             // flag our selected Node.
47075             this.tb.selectedNode = sel;
47076            
47077            
47078             Roo.menu.MenuMgr.hideAll();
47079
47080         }
47081         
47082         if (!updateFooter) {
47083             //this.footDisp.dom.innerHTML = ''; 
47084             return;
47085         }
47086         // update the footer
47087         //
47088         var html = '';
47089         
47090         this.footerEls = ans.reverse();
47091         Roo.each(this.footerEls, function(a,i) {
47092             if (!a) { return; }
47093             html += html.length ? ' &gt; '  :  '';
47094             
47095             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
47096             
47097         });
47098        
47099         // 
47100         var sz = this.footDisp.up('td').getSize();
47101         this.footDisp.dom.style.width = (sz.width -10) + 'px';
47102         this.footDisp.dom.style.marginLeft = '5px';
47103         
47104         this.footDisp.dom.style.overflow = 'hidden';
47105         
47106         this.footDisp.dom.innerHTML = html;
47107             
47108         //this.editorsyncValue();
47109     },
47110      
47111     
47112    
47113        
47114     // private
47115     onDestroy : function(){
47116         if(this.rendered){
47117             
47118             this.tb.items.each(function(item){
47119                 if(item.menu){
47120                     item.menu.removeAll();
47121                     if(item.menu.el){
47122                         item.menu.el.destroy();
47123                     }
47124                 }
47125                 item.destroy();
47126             });
47127              
47128         }
47129     },
47130     onFirstFocus: function() {
47131         // need to do this for all the toolbars..
47132         this.tb.items.each(function(item){
47133            item.enable();
47134         });
47135     },
47136     buildToolbar: function(tlist, nm)
47137     {
47138         var editor = this.editor;
47139         var editorcore = this.editorcore;
47140          // create a new element.
47141         var wdiv = editor.wrap.createChild({
47142                 tag: 'div'
47143             }, editor.wrap.dom.firstChild.nextSibling, true);
47144         
47145        
47146         var tb = new Roo.Toolbar(wdiv);
47147         // add the name..
47148         
47149         tb.add(nm+ ":&nbsp;");
47150         
47151         var styles = [];
47152         for(var i in this.styles) {
47153             styles.push(i);
47154         }
47155         
47156         // styles...
47157         if (styles && styles.length) {
47158             
47159             // this needs a multi-select checkbox...
47160             tb.addField( new Roo.form.ComboBox({
47161                 store: new Roo.data.SimpleStore({
47162                     id : 'val',
47163                     fields: ['val', 'selected'],
47164                     data : [] 
47165                 }),
47166                 name : '-roo-edit-className',
47167                 attrname : 'className',
47168                 displayField: 'val',
47169                 typeAhead: false,
47170                 mode: 'local',
47171                 editable : false,
47172                 triggerAction: 'all',
47173                 emptyText:'Select Style',
47174                 selectOnFocus:true,
47175                 width: 130,
47176                 listeners : {
47177                     'select': function(c, r, i) {
47178                         // initial support only for on class per el..
47179                         tb.selectedNode.className =  r ? r.get('val') : '';
47180                         editorcore.syncValue();
47181                     }
47182                 }
47183     
47184             }));
47185         }
47186         
47187         var tbc = Roo.form.HtmlEditor.ToolbarContext;
47188         var tbops = tbc.options;
47189         
47190         for (var i in tlist) {
47191             
47192             var item = tlist[i];
47193             tb.add(item.title + ":&nbsp;");
47194             
47195             
47196             //optname == used so you can configure the options available..
47197             var opts = item.opts ? item.opts : false;
47198             if (item.optname) {
47199                 opts = tbops[item.optname];
47200            
47201             }
47202             
47203             if (opts) {
47204                 // opts == pulldown..
47205                 tb.addField( new Roo.form.ComboBox({
47206                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
47207                         id : 'val',
47208                         fields: ['val', 'display'],
47209                         data : opts  
47210                     }),
47211                     name : '-roo-edit-' + i,
47212                     attrname : i,
47213                     stylename : item.style ? item.style : false,
47214                     displayField: item.displayField ? item.displayField : 'val',
47215                     valueField :  'val',
47216                     typeAhead: false,
47217                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
47218                     editable : false,
47219                     triggerAction: 'all',
47220                     emptyText:'Select',
47221                     selectOnFocus:true,
47222                     width: item.width ? item.width  : 130,
47223                     listeners : {
47224                         'select': function(c, r, i) {
47225                             if (c.stylename) {
47226                                 tb.selectedNode.style[c.stylename] =  r.get('val');
47227                                 return;
47228                             }
47229                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
47230                         }
47231                     }
47232
47233                 }));
47234                 continue;
47235                     
47236                  
47237                 
47238                 tb.addField( new Roo.form.TextField({
47239                     name: i,
47240                     width: 100,
47241                     //allowBlank:false,
47242                     value: ''
47243                 }));
47244                 continue;
47245             }
47246             tb.addField( new Roo.form.TextField({
47247                 name: '-roo-edit-' + i,
47248                 attrname : i,
47249                 
47250                 width: item.width,
47251                 //allowBlank:true,
47252                 value: '',
47253                 listeners: {
47254                     'change' : function(f, nv, ov) {
47255                         tb.selectedNode.setAttribute(f.attrname, nv);
47256                         editorcore.syncValue();
47257                     }
47258                 }
47259             }));
47260              
47261         }
47262         
47263         var _this = this;
47264         
47265         if(nm == 'BODY'){
47266             tb.addSeparator();
47267         
47268             tb.addButton( {
47269                 text: 'Stylesheets',
47270
47271                 listeners : {
47272                     click : function ()
47273                     {
47274                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
47275                     }
47276                 }
47277             });
47278         }
47279         
47280         tb.addFill();
47281         tb.addButton( {
47282             text: 'Remove Tag',
47283     
47284             listeners : {
47285                 click : function ()
47286                 {
47287                     // remove
47288                     // undo does not work.
47289                      
47290                     var sn = tb.selectedNode;
47291                     
47292                     var pn = sn.parentNode;
47293                     
47294                     var stn =  sn.childNodes[0];
47295                     var en = sn.childNodes[sn.childNodes.length - 1 ];
47296                     while (sn.childNodes.length) {
47297                         var node = sn.childNodes[0];
47298                         sn.removeChild(node);
47299                         //Roo.log(node);
47300                         pn.insertBefore(node, sn);
47301                         
47302                     }
47303                     pn.removeChild(sn);
47304                     var range = editorcore.createRange();
47305         
47306                     range.setStart(stn,0);
47307                     range.setEnd(en,0); //????
47308                     //range.selectNode(sel);
47309                     
47310                     
47311                     var selection = editorcore.getSelection();
47312                     selection.removeAllRanges();
47313                     selection.addRange(range);
47314                     
47315                     
47316                     
47317                     //_this.updateToolbar(null, null, pn);
47318                     _this.updateToolbar(null, null, null);
47319                     _this.footDisp.dom.innerHTML = ''; 
47320                 }
47321             }
47322             
47323                     
47324                 
47325             
47326         });
47327         
47328         
47329         tb.el.on('click', function(e){
47330             e.preventDefault(); // what does this do?
47331         });
47332         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
47333         tb.el.hide();
47334         tb.name = nm;
47335         // dont need to disable them... as they will get hidden
47336         return tb;
47337          
47338         
47339     },
47340     buildFooter : function()
47341     {
47342         
47343         var fel = this.editor.wrap.createChild();
47344         this.footer = new Roo.Toolbar(fel);
47345         // toolbar has scrolly on left / right?
47346         var footDisp= new Roo.Toolbar.Fill();
47347         var _t = this;
47348         this.footer.add(
47349             {
47350                 text : '&lt;',
47351                 xtype: 'Button',
47352                 handler : function() {
47353                     _t.footDisp.scrollTo('left',0,true)
47354                 }
47355             }
47356         );
47357         this.footer.add( footDisp );
47358         this.footer.add( 
47359             {
47360                 text : '&gt;',
47361                 xtype: 'Button',
47362                 handler : function() {
47363                     // no animation..
47364                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
47365                 }
47366             }
47367         );
47368         var fel = Roo.get(footDisp.el);
47369         fel.addClass('x-editor-context');
47370         this.footDispWrap = fel; 
47371         this.footDispWrap.overflow  = 'hidden';
47372         
47373         this.footDisp = fel.createChild();
47374         this.footDispWrap.on('click', this.onContextClick, this)
47375         
47376         
47377     },
47378     onContextClick : function (ev,dom)
47379     {
47380         ev.preventDefault();
47381         var  cn = dom.className;
47382         //Roo.log(cn);
47383         if (!cn.match(/x-ed-loc-/)) {
47384             return;
47385         }
47386         var n = cn.split('-').pop();
47387         var ans = this.footerEls;
47388         var sel = ans[n];
47389         
47390          // pick
47391         var range = this.editorcore.createRange();
47392         
47393         range.selectNodeContents(sel);
47394         //range.selectNode(sel);
47395         
47396         
47397         var selection = this.editorcore.getSelection();
47398         selection.removeAllRanges();
47399         selection.addRange(range);
47400         
47401         
47402         
47403         this.updateToolbar(null, null, sel);
47404         
47405         
47406     }
47407     
47408     
47409     
47410     
47411     
47412 });
47413
47414
47415
47416
47417
47418 /*
47419  * Based on:
47420  * Ext JS Library 1.1.1
47421  * Copyright(c) 2006-2007, Ext JS, LLC.
47422  *
47423  * Originally Released Under LGPL - original licence link has changed is not relivant.
47424  *
47425  * Fork - LGPL
47426  * <script type="text/javascript">
47427  */
47428  
47429 /**
47430  * @class Roo.form.BasicForm
47431  * @extends Roo.util.Observable
47432  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
47433  * @constructor
47434  * @param {String/HTMLElement/Roo.Element} el The form element or its id
47435  * @param {Object} config Configuration options
47436  */
47437 Roo.form.BasicForm = function(el, config){
47438     this.allItems = [];
47439     this.childForms = [];
47440     Roo.apply(this, config);
47441     /*
47442      * The Roo.form.Field items in this form.
47443      * @type MixedCollection
47444      */
47445      
47446      
47447     this.items = new Roo.util.MixedCollection(false, function(o){
47448         return o.id || (o.id = Roo.id());
47449     });
47450     this.addEvents({
47451         /**
47452          * @event beforeaction
47453          * Fires before any action is performed. Return false to cancel the action.
47454          * @param {Form} this
47455          * @param {Action} action The action to be performed
47456          */
47457         beforeaction: true,
47458         /**
47459          * @event actionfailed
47460          * Fires when an action fails.
47461          * @param {Form} this
47462          * @param {Action} action The action that failed
47463          */
47464         actionfailed : true,
47465         /**
47466          * @event actioncomplete
47467          * Fires when an action is completed.
47468          * @param {Form} this
47469          * @param {Action} action The action that completed
47470          */
47471         actioncomplete : true
47472     });
47473     if(el){
47474         this.initEl(el);
47475     }
47476     Roo.form.BasicForm.superclass.constructor.call(this);
47477     
47478     Roo.form.BasicForm.popover.apply();
47479 };
47480
47481 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
47482     /**
47483      * @cfg {String} method
47484      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
47485      */
47486     /**
47487      * @cfg {DataReader} reader
47488      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
47489      * This is optional as there is built-in support for processing JSON.
47490      */
47491     /**
47492      * @cfg {DataReader} errorReader
47493      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
47494      * This is completely optional as there is built-in support for processing JSON.
47495      */
47496     /**
47497      * @cfg {String} url
47498      * The URL to use for form actions if one isn't supplied in the action options.
47499      */
47500     /**
47501      * @cfg {Boolean} fileUpload
47502      * Set to true if this form is a file upload.
47503      */
47504      
47505     /**
47506      * @cfg {Object} baseParams
47507      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
47508      */
47509      /**
47510      
47511     /**
47512      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
47513      */
47514     timeout: 30,
47515
47516     // private
47517     activeAction : null,
47518
47519     /**
47520      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
47521      * or setValues() data instead of when the form was first created.
47522      */
47523     trackResetOnLoad : false,
47524     
47525     
47526     /**
47527      * childForms - used for multi-tab forms
47528      * @type {Array}
47529      */
47530     childForms : false,
47531     
47532     /**
47533      * allItems - full list of fields.
47534      * @type {Array}
47535      */
47536     allItems : false,
47537     
47538     /**
47539      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
47540      * element by passing it or its id or mask the form itself by passing in true.
47541      * @type Mixed
47542      */
47543     waitMsgTarget : false,
47544     
47545     /**
47546      * @type Boolean
47547      */
47548     disableMask : false,
47549     
47550     /**
47551      * @cfg {Boolean} errorMask (true|false) default false
47552      */
47553     errorMask : false,
47554     
47555     /**
47556      * @cfg {Number} maskOffset Default 100
47557      */
47558     maskOffset : 100,
47559
47560     // private
47561     initEl : function(el){
47562         this.el = Roo.get(el);
47563         this.id = this.el.id || Roo.id();
47564         this.el.on('submit', this.onSubmit, this);
47565         this.el.addClass('x-form');
47566     },
47567
47568     // private
47569     onSubmit : function(e){
47570         e.stopEvent();
47571     },
47572
47573     /**
47574      * Returns true if client-side validation on the form is successful.
47575      * @return Boolean
47576      */
47577     isValid : function(){
47578         var valid = true;
47579         var target = false;
47580         this.items.each(function(f){
47581             if(f.validate()){
47582                 return;
47583             }
47584             
47585             valid = false;
47586                 
47587             if(!target && f.el.isVisible(true)){
47588                 target = f;
47589             }
47590         });
47591         
47592         if(this.errorMask && !valid){
47593             Roo.form.BasicForm.popover.mask(this, target);
47594         }
47595         
47596         return valid;
47597     },
47598     /**
47599      * Returns array of invalid form fields.
47600      * @return Array
47601      */
47602     
47603     invalidFields : function()
47604     {
47605         var ret = [];
47606         this.items.each(function(f){
47607             if(f.validate()){
47608                 return;
47609             }
47610             ret.push(f);
47611             
47612         });
47613         
47614         return ret;
47615     },
47616     
47617     
47618     /**
47619      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
47620      * @return Boolean
47621      */
47622     isDirty : function(){
47623         var dirty = false;
47624         this.items.each(function(f){
47625            if(f.isDirty()){
47626                dirty = true;
47627                return false;
47628            }
47629         });
47630         return dirty;
47631     },
47632     
47633     /**
47634      * Returns true if any fields in this form have changed since their original load. (New version)
47635      * @return Boolean
47636      */
47637     
47638     hasChanged : function()
47639     {
47640         var dirty = false;
47641         this.items.each(function(f){
47642            if(f.hasChanged()){
47643                dirty = true;
47644                return false;
47645            }
47646         });
47647         return dirty;
47648         
47649     },
47650     /**
47651      * Resets all hasChanged to 'false' -
47652      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
47653      * So hasChanged storage is only to be used for this purpose
47654      * @return Boolean
47655      */
47656     resetHasChanged : function()
47657     {
47658         this.items.each(function(f){
47659            f.resetHasChanged();
47660         });
47661         
47662     },
47663     
47664     
47665     /**
47666      * Performs a predefined action (submit or load) or custom actions you define on this form.
47667      * @param {String} actionName The name of the action type
47668      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
47669      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
47670      * accept other config options):
47671      * <pre>
47672 Property          Type             Description
47673 ----------------  ---------------  ----------------------------------------------------------------------------------
47674 url               String           The url for the action (defaults to the form's url)
47675 method            String           The form method to use (defaults to the form's method, or POST if not defined)
47676 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
47677 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
47678                                    validate the form on the client (defaults to false)
47679      * </pre>
47680      * @return {BasicForm} this
47681      */
47682     doAction : function(action, options){
47683         if(typeof action == 'string'){
47684             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
47685         }
47686         if(this.fireEvent('beforeaction', this, action) !== false){
47687             this.beforeAction(action);
47688             action.run.defer(100, action);
47689         }
47690         return this;
47691     },
47692
47693     /**
47694      * Shortcut to do a submit action.
47695      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
47696      * @return {BasicForm} this
47697      */
47698     submit : function(options){
47699         this.doAction('submit', options);
47700         return this;
47701     },
47702
47703     /**
47704      * Shortcut to do a load action.
47705      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
47706      * @return {BasicForm} this
47707      */
47708     load : function(options){
47709         this.doAction('load', options);
47710         return this;
47711     },
47712
47713     /**
47714      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
47715      * @param {Record} record The record to edit
47716      * @return {BasicForm} this
47717      */
47718     updateRecord : function(record){
47719         record.beginEdit();
47720         var fs = record.fields;
47721         fs.each(function(f){
47722             var field = this.findField(f.name);
47723             if(field){
47724                 record.set(f.name, field.getValue());
47725             }
47726         }, this);
47727         record.endEdit();
47728         return this;
47729     },
47730
47731     /**
47732      * Loads an Roo.data.Record into this form.
47733      * @param {Record} record The record to load
47734      * @return {BasicForm} this
47735      */
47736     loadRecord : function(record){
47737         this.setValues(record.data);
47738         return this;
47739     },
47740
47741     // private
47742     beforeAction : function(action){
47743         var o = action.options;
47744         
47745         if(!this.disableMask) {
47746             if(this.waitMsgTarget === true){
47747                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
47748             }else if(this.waitMsgTarget){
47749                 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
47750                 this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
47751             }else {
47752                 Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
47753             }
47754         }
47755         
47756          
47757     },
47758
47759     // private
47760     afterAction : function(action, success){
47761         this.activeAction = null;
47762         var o = action.options;
47763         
47764         if(!this.disableMask) {
47765             if(this.waitMsgTarget === true){
47766                 this.el.unmask();
47767             }else if(this.waitMsgTarget){
47768                 this.waitMsgTarget.unmask();
47769             }else{
47770                 Roo.MessageBox.updateProgress(1);
47771                 Roo.MessageBox.hide();
47772             }
47773         }
47774         
47775         if(success){
47776             if(o.reset){
47777                 this.reset();
47778             }
47779             Roo.callback(o.success, o.scope, [this, action]);
47780             this.fireEvent('actioncomplete', this, action);
47781             
47782         }else{
47783             
47784             // failure condition..
47785             // we have a scenario where updates need confirming.
47786             // eg. if a locking scenario exists..
47787             // we look for { errors : { needs_confirm : true }} in the response.
47788             if (
47789                 (typeof(action.result) != 'undefined')  &&
47790                 (typeof(action.result.errors) != 'undefined')  &&
47791                 (typeof(action.result.errors.needs_confirm) != 'undefined')
47792            ){
47793                 var _t = this;
47794                 Roo.MessageBox.confirm(
47795                     "Change requires confirmation",
47796                     action.result.errorMsg,
47797                     function(r) {
47798                         if (r != 'yes') {
47799                             return;
47800                         }
47801                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
47802                     }
47803                     
47804                 );
47805                 
47806                 
47807                 
47808                 return;
47809             }
47810             
47811             Roo.callback(o.failure, o.scope, [this, action]);
47812             // show an error message if no failed handler is set..
47813             if (!this.hasListener('actionfailed')) {
47814                 Roo.MessageBox.alert("Error",
47815                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
47816                         action.result.errorMsg :
47817                         "Saving Failed, please check your entries or try again"
47818                 );
47819             }
47820             
47821             this.fireEvent('actionfailed', this, action);
47822         }
47823         
47824     },
47825
47826     /**
47827      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
47828      * @param {String} id The value to search for
47829      * @return Field
47830      */
47831     findField : function(id){
47832         var field = this.items.get(id);
47833         if(!field){
47834             this.items.each(function(f){
47835                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
47836                     field = f;
47837                     return false;
47838                 }
47839             });
47840         }
47841         return field || null;
47842     },
47843
47844     /**
47845      * Add a secondary form to this one, 
47846      * Used to provide tabbed forms. One form is primary, with hidden values 
47847      * which mirror the elements from the other forms.
47848      * 
47849      * @param {Roo.form.Form} form to add.
47850      * 
47851      */
47852     addForm : function(form)
47853     {
47854        
47855         if (this.childForms.indexOf(form) > -1) {
47856             // already added..
47857             return;
47858         }
47859         this.childForms.push(form);
47860         var n = '';
47861         Roo.each(form.allItems, function (fe) {
47862             
47863             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
47864             if (this.findField(n)) { // already added..
47865                 return;
47866             }
47867             var add = new Roo.form.Hidden({
47868                 name : n
47869             });
47870             add.render(this.el);
47871             
47872             this.add( add );
47873         }, this);
47874         
47875     },
47876     /**
47877      * Mark fields in this form invalid in bulk.
47878      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
47879      * @return {BasicForm} this
47880      */
47881     markInvalid : function(errors){
47882         if(errors instanceof Array){
47883             for(var i = 0, len = errors.length; i < len; i++){
47884                 var fieldError = errors[i];
47885                 var f = this.findField(fieldError.id);
47886                 if(f){
47887                     f.markInvalid(fieldError.msg);
47888                 }
47889             }
47890         }else{
47891             var field, id;
47892             for(id in errors){
47893                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
47894                     field.markInvalid(errors[id]);
47895                 }
47896             }
47897         }
47898         Roo.each(this.childForms || [], function (f) {
47899             f.markInvalid(errors);
47900         });
47901         
47902         return this;
47903     },
47904
47905     /**
47906      * Set values for fields in this form in bulk.
47907      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
47908      * @return {BasicForm} this
47909      */
47910     setValues : function(values){
47911         if(values instanceof Array){ // array of objects
47912             for(var i = 0, len = values.length; i < len; i++){
47913                 var v = values[i];
47914                 var f = this.findField(v.id);
47915                 if(f){
47916                     f.setValue(v.value);
47917                     if(this.trackResetOnLoad){
47918                         f.originalValue = f.getValue();
47919                     }
47920                 }
47921             }
47922         }else{ // object hash
47923             var field, id;
47924             for(id in values){
47925                 if(typeof values[id] != 'function' && (field = this.findField(id))){
47926                     
47927                     if (field.setFromData && 
47928                         field.valueField && 
47929                         field.displayField &&
47930                         // combos' with local stores can 
47931                         // be queried via setValue()
47932                         // to set their value..
47933                         (field.store && !field.store.isLocal)
47934                         ) {
47935                         // it's a combo
47936                         var sd = { };
47937                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
47938                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
47939                         field.setFromData(sd);
47940                         
47941                     } else {
47942                         field.setValue(values[id]);
47943                     }
47944                     
47945                     
47946                     if(this.trackResetOnLoad){
47947                         field.originalValue = field.getValue();
47948                     }
47949                 }
47950             }
47951         }
47952         this.resetHasChanged();
47953         
47954         
47955         Roo.each(this.childForms || [], function (f) {
47956             f.setValues(values);
47957             f.resetHasChanged();
47958         });
47959                 
47960         return this;
47961     },
47962  
47963     /**
47964      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
47965      * they are returned as an array.
47966      * @param {Boolean} asString
47967      * @return {Object}
47968      */
47969     getValues : function(asString){
47970         if (this.childForms) {
47971             // copy values from the child forms
47972             Roo.each(this.childForms, function (f) {
47973                 this.setValues(f.getValues());
47974             }, this);
47975         }
47976         
47977         // use formdata
47978         if (typeof(FormData) != 'undefined' && asString !== true) {
47979             // this relies on a 'recent' version of chrome apparently...
47980             try {
47981                 var fd = (new FormData(this.el.dom)).entries();
47982                 var ret = {};
47983                 var ent = fd.next();
47984                 while (!ent.done) {
47985                     ret[ent.value[0]] = ent.value[1]; // not sure how this will handle duplicates..
47986                     ent = fd.next();
47987                 };
47988                 return ret;
47989             } catch(e) {
47990                 
47991             }
47992             
47993         }
47994         
47995         
47996         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
47997         if(asString === true){
47998             return fs;
47999         }
48000         return Roo.urlDecode(fs);
48001     },
48002     
48003     /**
48004      * Returns the fields in this form as an object with key/value pairs. 
48005      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
48006      * @return {Object}
48007      */
48008     getFieldValues : function(with_hidden)
48009     {
48010         if (this.childForms) {
48011             // copy values from the child forms
48012             // should this call getFieldValues - probably not as we do not currently copy
48013             // hidden fields when we generate..
48014             Roo.each(this.childForms, function (f) {
48015                 this.setValues(f.getValues());
48016             }, this);
48017         }
48018         
48019         var ret = {};
48020         this.items.each(function(f){
48021             if (!f.getName()) {
48022                 return;
48023             }
48024             var v = f.getValue();
48025             if (f.inputType =='radio') {
48026                 if (typeof(ret[f.getName()]) == 'undefined') {
48027                     ret[f.getName()] = ''; // empty..
48028                 }
48029                 
48030                 if (!f.el.dom.checked) {
48031                     return;
48032                     
48033                 }
48034                 v = f.el.dom.value;
48035                 
48036             }
48037             
48038             // not sure if this supported any more..
48039             if ((typeof(v) == 'object') && f.getRawValue) {
48040                 v = f.getRawValue() ; // dates..
48041             }
48042             // combo boxes where name != hiddenName...
48043             if (f.name != f.getName()) {
48044                 ret[f.name] = f.getRawValue();
48045             }
48046             ret[f.getName()] = v;
48047         });
48048         
48049         return ret;
48050     },
48051
48052     /**
48053      * Clears all invalid messages in this form.
48054      * @return {BasicForm} this
48055      */
48056     clearInvalid : function(){
48057         this.items.each(function(f){
48058            f.clearInvalid();
48059         });
48060         
48061         Roo.each(this.childForms || [], function (f) {
48062             f.clearInvalid();
48063         });
48064         
48065         
48066         return this;
48067     },
48068
48069     /**
48070      * Resets this form.
48071      * @return {BasicForm} this
48072      */
48073     reset : function(){
48074         this.items.each(function(f){
48075             f.reset();
48076         });
48077         
48078         Roo.each(this.childForms || [], function (f) {
48079             f.reset();
48080         });
48081         this.resetHasChanged();
48082         
48083         return this;
48084     },
48085
48086     /**
48087      * Add Roo.form components to this form.
48088      * @param {Field} field1
48089      * @param {Field} field2 (optional)
48090      * @param {Field} etc (optional)
48091      * @return {BasicForm} this
48092      */
48093     add : function(){
48094         this.items.addAll(Array.prototype.slice.call(arguments, 0));
48095         return this;
48096     },
48097
48098
48099     /**
48100      * Removes a field from the items collection (does NOT remove its markup).
48101      * @param {Field} field
48102      * @return {BasicForm} this
48103      */
48104     remove : function(field){
48105         this.items.remove(field);
48106         return this;
48107     },
48108
48109     /**
48110      * Looks at the fields in this form, checks them for an id attribute,
48111      * and calls applyTo on the existing dom element with that id.
48112      * @return {BasicForm} this
48113      */
48114     render : function(){
48115         this.items.each(function(f){
48116             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
48117                 f.applyTo(f.id);
48118             }
48119         });
48120         return this;
48121     },
48122
48123     /**
48124      * Calls {@link Ext#apply} for all fields in this form with the passed object.
48125      * @param {Object} values
48126      * @return {BasicForm} this
48127      */
48128     applyToFields : function(o){
48129         this.items.each(function(f){
48130            Roo.apply(f, o);
48131         });
48132         return this;
48133     },
48134
48135     /**
48136      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
48137      * @param {Object} values
48138      * @return {BasicForm} this
48139      */
48140     applyIfToFields : function(o){
48141         this.items.each(function(f){
48142            Roo.applyIf(f, o);
48143         });
48144         return this;
48145     }
48146 });
48147
48148 // back compat
48149 Roo.BasicForm = Roo.form.BasicForm;
48150
48151 Roo.apply(Roo.form.BasicForm, {
48152     
48153     popover : {
48154         
48155         padding : 5,
48156         
48157         isApplied : false,
48158         
48159         isMasked : false,
48160         
48161         form : false,
48162         
48163         target : false,
48164         
48165         intervalID : false,
48166         
48167         maskEl : false,
48168         
48169         apply : function()
48170         {
48171             if(this.isApplied){
48172                 return;
48173             }
48174             
48175             this.maskEl = {
48176                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
48177                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
48178                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
48179                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
48180             };
48181             
48182             this.maskEl.top.enableDisplayMode("block");
48183             this.maskEl.left.enableDisplayMode("block");
48184             this.maskEl.bottom.enableDisplayMode("block");
48185             this.maskEl.right.enableDisplayMode("block");
48186             
48187             Roo.get(document.body).on('click', function(){
48188                 this.unmask();
48189             }, this);
48190             
48191             Roo.get(document.body).on('touchstart', function(){
48192                 this.unmask();
48193             }, this);
48194             
48195             this.isApplied = true
48196         },
48197         
48198         mask : function(form, target)
48199         {
48200             this.form = form;
48201             
48202             this.target = target;
48203             
48204             if(!this.form.errorMask || !target.el){
48205                 return;
48206             }
48207             
48208             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.x-layout-active-content', 100, true) || Roo.get(document.body);
48209             
48210             var ot = this.target.el.calcOffsetsTo(scrollable);
48211             
48212             var scrollTo = ot[1] - this.form.maskOffset;
48213             
48214             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
48215             
48216             scrollable.scrollTo('top', scrollTo);
48217             
48218             var el = this.target.wrap || this.target.el;
48219             
48220             var box = el.getBox();
48221             
48222             this.maskEl.top.setStyle('position', 'absolute');
48223             this.maskEl.top.setStyle('z-index', 10000);
48224             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
48225             this.maskEl.top.setLeft(0);
48226             this.maskEl.top.setTop(0);
48227             this.maskEl.top.show();
48228             
48229             this.maskEl.left.setStyle('position', 'absolute');
48230             this.maskEl.left.setStyle('z-index', 10000);
48231             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
48232             this.maskEl.left.setLeft(0);
48233             this.maskEl.left.setTop(box.y - this.padding);
48234             this.maskEl.left.show();
48235
48236             this.maskEl.bottom.setStyle('position', 'absolute');
48237             this.maskEl.bottom.setStyle('z-index', 10000);
48238             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
48239             this.maskEl.bottom.setLeft(0);
48240             this.maskEl.bottom.setTop(box.bottom + this.padding);
48241             this.maskEl.bottom.show();
48242
48243             this.maskEl.right.setStyle('position', 'absolute');
48244             this.maskEl.right.setStyle('z-index', 10000);
48245             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
48246             this.maskEl.right.setLeft(box.right + this.padding);
48247             this.maskEl.right.setTop(box.y - this.padding);
48248             this.maskEl.right.show();
48249
48250             this.intervalID = window.setInterval(function() {
48251                 Roo.form.BasicForm.popover.unmask();
48252             }, 10000);
48253
48254             window.onwheel = function(){ return false;};
48255             
48256             (function(){ this.isMasked = true; }).defer(500, this);
48257             
48258         },
48259         
48260         unmask : function()
48261         {
48262             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
48263                 return;
48264             }
48265             
48266             this.maskEl.top.setStyle('position', 'absolute');
48267             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
48268             this.maskEl.top.hide();
48269
48270             this.maskEl.left.setStyle('position', 'absolute');
48271             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
48272             this.maskEl.left.hide();
48273
48274             this.maskEl.bottom.setStyle('position', 'absolute');
48275             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
48276             this.maskEl.bottom.hide();
48277
48278             this.maskEl.right.setStyle('position', 'absolute');
48279             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
48280             this.maskEl.right.hide();
48281             
48282             window.onwheel = function(){ return true;};
48283             
48284             if(this.intervalID){
48285                 window.clearInterval(this.intervalID);
48286                 this.intervalID = false;
48287             }
48288             
48289             this.isMasked = false;
48290             
48291         }
48292         
48293     }
48294     
48295 });/*
48296  * Based on:
48297  * Ext JS Library 1.1.1
48298  * Copyright(c) 2006-2007, Ext JS, LLC.
48299  *
48300  * Originally Released Under LGPL - original licence link has changed is not relivant.
48301  *
48302  * Fork - LGPL
48303  * <script type="text/javascript">
48304  */
48305
48306 /**
48307  * @class Roo.form.Form
48308  * @extends Roo.form.BasicForm
48309  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
48310  * @constructor
48311  * @param {Object} config Configuration options
48312  */
48313 Roo.form.Form = function(config){
48314     var xitems =  [];
48315     if (config.items) {
48316         xitems = config.items;
48317         delete config.items;
48318     }
48319    
48320     
48321     Roo.form.Form.superclass.constructor.call(this, null, config);
48322     this.url = this.url || this.action;
48323     if(!this.root){
48324         this.root = new Roo.form.Layout(Roo.applyIf({
48325             id: Roo.id()
48326         }, config));
48327     }
48328     this.active = this.root;
48329     /**
48330      * Array of all the buttons that have been added to this form via {@link addButton}
48331      * @type Array
48332      */
48333     this.buttons = [];
48334     this.allItems = [];
48335     this.addEvents({
48336         /**
48337          * @event clientvalidation
48338          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
48339          * @param {Form} this
48340          * @param {Boolean} valid true if the form has passed client-side validation
48341          */
48342         clientvalidation: true,
48343         /**
48344          * @event rendered
48345          * Fires when the form is rendered
48346          * @param {Roo.form.Form} form
48347          */
48348         rendered : true
48349     });
48350     
48351     if (this.progressUrl) {
48352             // push a hidden field onto the list of fields..
48353             this.addxtype( {
48354                     xns: Roo.form, 
48355                     xtype : 'Hidden', 
48356                     name : 'UPLOAD_IDENTIFIER' 
48357             });
48358         }
48359         
48360     
48361     Roo.each(xitems, this.addxtype, this);
48362     
48363 };
48364
48365 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
48366     /**
48367      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
48368      */
48369     /**
48370      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
48371      */
48372     /**
48373      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
48374      */
48375     buttonAlign:'center',
48376
48377     /**
48378      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
48379      */
48380     minButtonWidth:75,
48381
48382     /**
48383      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
48384      * This property cascades to child containers if not set.
48385      */
48386     labelAlign:'left',
48387
48388     /**
48389      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
48390      * fires a looping event with that state. This is required to bind buttons to the valid
48391      * state using the config value formBind:true on the button.
48392      */
48393     monitorValid : false,
48394
48395     /**
48396      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
48397      */
48398     monitorPoll : 200,
48399     
48400     /**
48401      * @cfg {String} progressUrl - Url to return progress data 
48402      */
48403     
48404     progressUrl : false,
48405     /**
48406      * @cfg {boolean|FormData} formData - true to use new 'FormData' post, or set to a new FormData({dom form}) Object, if
48407      * sending a formdata with extra parameters - eg uploaded elements.
48408      */
48409     
48410     formData : false,
48411     
48412     /**
48413      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
48414      * fields are added and the column is closed. If no fields are passed the column remains open
48415      * until end() is called.
48416      * @param {Object} config The config to pass to the column
48417      * @param {Field} field1 (optional)
48418      * @param {Field} field2 (optional)
48419      * @param {Field} etc (optional)
48420      * @return Column The column container object
48421      */
48422     column : function(c){
48423         var col = new Roo.form.Column(c);
48424         this.start(col);
48425         if(arguments.length > 1){ // duplicate code required because of Opera
48426             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
48427             this.end();
48428         }
48429         return col;
48430     },
48431
48432     /**
48433      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
48434      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
48435      * until end() is called.
48436      * @param {Object} config The config to pass to the fieldset
48437      * @param {Field} field1 (optional)
48438      * @param {Field} field2 (optional)
48439      * @param {Field} etc (optional)
48440      * @return FieldSet The fieldset container object
48441      */
48442     fieldset : function(c){
48443         var fs = new Roo.form.FieldSet(c);
48444         this.start(fs);
48445         if(arguments.length > 1){ // duplicate code required because of Opera
48446             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
48447             this.end();
48448         }
48449         return fs;
48450     },
48451
48452     /**
48453      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
48454      * fields are added and the container is closed. If no fields are passed the container remains open
48455      * until end() is called.
48456      * @param {Object} config The config to pass to the Layout
48457      * @param {Field} field1 (optional)
48458      * @param {Field} field2 (optional)
48459      * @param {Field} etc (optional)
48460      * @return Layout The container object
48461      */
48462     container : function(c){
48463         var l = new Roo.form.Layout(c);
48464         this.start(l);
48465         if(arguments.length > 1){ // duplicate code required because of Opera
48466             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
48467             this.end();
48468         }
48469         return l;
48470     },
48471
48472     /**
48473      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
48474      * @param {Object} container A Roo.form.Layout or subclass of Layout
48475      * @return {Form} this
48476      */
48477     start : function(c){
48478         // cascade label info
48479         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
48480         this.active.stack.push(c);
48481         c.ownerCt = this.active;
48482         this.active = c;
48483         return this;
48484     },
48485
48486     /**
48487      * Closes the current open container
48488      * @return {Form} this
48489      */
48490     end : function(){
48491         if(this.active == this.root){
48492             return this;
48493         }
48494         this.active = this.active.ownerCt;
48495         return this;
48496     },
48497
48498     /**
48499      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
48500      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
48501      * as the label of the field.
48502      * @param {Field} field1
48503      * @param {Field} field2 (optional)
48504      * @param {Field} etc. (optional)
48505      * @return {Form} this
48506      */
48507     add : function(){
48508         this.active.stack.push.apply(this.active.stack, arguments);
48509         this.allItems.push.apply(this.allItems,arguments);
48510         var r = [];
48511         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
48512             if(a[i].isFormField){
48513                 r.push(a[i]);
48514             }
48515         }
48516         if(r.length > 0){
48517             Roo.form.Form.superclass.add.apply(this, r);
48518         }
48519         return this;
48520     },
48521     
48522
48523     
48524     
48525     
48526      /**
48527      * Find any element that has been added to a form, using it's ID or name
48528      * This can include framesets, columns etc. along with regular fields..
48529      * @param {String} id - id or name to find.
48530      
48531      * @return {Element} e - or false if nothing found.
48532      */
48533     findbyId : function(id)
48534     {
48535         var ret = false;
48536         if (!id) {
48537             return ret;
48538         }
48539         Roo.each(this.allItems, function(f){
48540             if (f.id == id || f.name == id ){
48541                 ret = f;
48542                 return false;
48543             }
48544         });
48545         return ret;
48546     },
48547
48548     
48549     
48550     /**
48551      * Render this form into the passed container. This should only be called once!
48552      * @param {String/HTMLElement/Element} container The element this component should be rendered into
48553      * @return {Form} this
48554      */
48555     render : function(ct)
48556     {
48557         
48558         
48559         
48560         ct = Roo.get(ct);
48561         var o = this.autoCreate || {
48562             tag: 'form',
48563             method : this.method || 'POST',
48564             id : this.id || Roo.id()
48565         };
48566         this.initEl(ct.createChild(o));
48567
48568         this.root.render(this.el);
48569         
48570        
48571              
48572         this.items.each(function(f){
48573             f.render('x-form-el-'+f.id);
48574         });
48575
48576         if(this.buttons.length > 0){
48577             // tables are required to maintain order and for correct IE layout
48578             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
48579                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
48580                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
48581             }}, null, true);
48582             var tr = tb.getElementsByTagName('tr')[0];
48583             for(var i = 0, len = this.buttons.length; i < len; i++) {
48584                 var b = this.buttons[i];
48585                 var td = document.createElement('td');
48586                 td.className = 'x-form-btn-td';
48587                 b.render(tr.appendChild(td));
48588             }
48589         }
48590         if(this.monitorValid){ // initialize after render
48591             this.startMonitoring();
48592         }
48593         this.fireEvent('rendered', this);
48594         return this;
48595     },
48596
48597     /**
48598      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
48599      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
48600      * object or a valid Roo.DomHelper element config
48601      * @param {Function} handler The function called when the button is clicked
48602      * @param {Object} scope (optional) The scope of the handler function
48603      * @return {Roo.Button}
48604      */
48605     addButton : function(config, handler, scope){
48606         var bc = {
48607             handler: handler,
48608             scope: scope,
48609             minWidth: this.minButtonWidth,
48610             hideParent:true
48611         };
48612         if(typeof config == "string"){
48613             bc.text = config;
48614         }else{
48615             Roo.apply(bc, config);
48616         }
48617         var btn = new Roo.Button(null, bc);
48618         this.buttons.push(btn);
48619         return btn;
48620     },
48621
48622      /**
48623      * Adds a series of form elements (using the xtype property as the factory method.
48624      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
48625      * @param {Object} config 
48626      */
48627     
48628     addxtype : function()
48629     {
48630         var ar = Array.prototype.slice.call(arguments, 0);
48631         var ret = false;
48632         for(var i = 0; i < ar.length; i++) {
48633             if (!ar[i]) {
48634                 continue; // skip -- if this happends something invalid got sent, we 
48635                 // should ignore it, as basically that interface element will not show up
48636                 // and that should be pretty obvious!!
48637             }
48638             
48639             if (Roo.form[ar[i].xtype]) {
48640                 ar[i].form = this;
48641                 var fe = Roo.factory(ar[i], Roo.form);
48642                 if (!ret) {
48643                     ret = fe;
48644                 }
48645                 fe.form = this;
48646                 if (fe.store) {
48647                     fe.store.form = this;
48648                 }
48649                 if (fe.isLayout) {  
48650                          
48651                     this.start(fe);
48652                     this.allItems.push(fe);
48653                     if (fe.items && fe.addxtype) {
48654                         fe.addxtype.apply(fe, fe.items);
48655                         delete fe.items;
48656                     }
48657                      this.end();
48658                     continue;
48659                 }
48660                 
48661                 
48662                  
48663                 this.add(fe);
48664               //  console.log('adding ' + ar[i].xtype);
48665             }
48666             if (ar[i].xtype == 'Button') {  
48667                 //console.log('adding button');
48668                 //console.log(ar[i]);
48669                 this.addButton(ar[i]);
48670                 this.allItems.push(fe);
48671                 continue;
48672             }
48673             
48674             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
48675                 alert('end is not supported on xtype any more, use items');
48676             //    this.end();
48677             //    //console.log('adding end');
48678             }
48679             
48680         }
48681         return ret;
48682     },
48683     
48684     /**
48685      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
48686      * option "monitorValid"
48687      */
48688     startMonitoring : function(){
48689         if(!this.bound){
48690             this.bound = true;
48691             Roo.TaskMgr.start({
48692                 run : this.bindHandler,
48693                 interval : this.monitorPoll || 200,
48694                 scope: this
48695             });
48696         }
48697     },
48698
48699     /**
48700      * Stops monitoring of the valid state of this form
48701      */
48702     stopMonitoring : function(){
48703         this.bound = false;
48704     },
48705
48706     // private
48707     bindHandler : function(){
48708         if(!this.bound){
48709             return false; // stops binding
48710         }
48711         var valid = true;
48712         this.items.each(function(f){
48713             if(!f.isValid(true)){
48714                 valid = false;
48715                 return false;
48716             }
48717         });
48718         for(var i = 0, len = this.buttons.length; i < len; i++){
48719             var btn = this.buttons[i];
48720             if(btn.formBind === true && btn.disabled === valid){
48721                 btn.setDisabled(!valid);
48722             }
48723         }
48724         this.fireEvent('clientvalidation', this, valid);
48725     }
48726     
48727     
48728     
48729     
48730     
48731     
48732     
48733     
48734 });
48735
48736
48737 // back compat
48738 Roo.Form = Roo.form.Form;
48739 /*
48740  * Based on:
48741  * Ext JS Library 1.1.1
48742  * Copyright(c) 2006-2007, Ext JS, LLC.
48743  *
48744  * Originally Released Under LGPL - original licence link has changed is not relivant.
48745  *
48746  * Fork - LGPL
48747  * <script type="text/javascript">
48748  */
48749
48750 // as we use this in bootstrap.
48751 Roo.namespace('Roo.form');
48752  /**
48753  * @class Roo.form.Action
48754  * Internal Class used to handle form actions
48755  * @constructor
48756  * @param {Roo.form.BasicForm} el The form element or its id
48757  * @param {Object} config Configuration options
48758  */
48759
48760  
48761  
48762 // define the action interface
48763 Roo.form.Action = function(form, options){
48764     this.form = form;
48765     this.options = options || {};
48766 };
48767 /**
48768  * Client Validation Failed
48769  * @const 
48770  */
48771 Roo.form.Action.CLIENT_INVALID = 'client';
48772 /**
48773  * Server Validation Failed
48774  * @const 
48775  */
48776 Roo.form.Action.SERVER_INVALID = 'server';
48777  /**
48778  * Connect to Server Failed
48779  * @const 
48780  */
48781 Roo.form.Action.CONNECT_FAILURE = 'connect';
48782 /**
48783  * Reading Data from Server Failed
48784  * @const 
48785  */
48786 Roo.form.Action.LOAD_FAILURE = 'load';
48787
48788 Roo.form.Action.prototype = {
48789     type : 'default',
48790     failureType : undefined,
48791     response : undefined,
48792     result : undefined,
48793
48794     // interface method
48795     run : function(options){
48796
48797     },
48798
48799     // interface method
48800     success : function(response){
48801
48802     },
48803
48804     // interface method
48805     handleResponse : function(response){
48806
48807     },
48808
48809     // default connection failure
48810     failure : function(response){
48811         
48812         this.response = response;
48813         this.failureType = Roo.form.Action.CONNECT_FAILURE;
48814         this.form.afterAction(this, false);
48815     },
48816
48817     processResponse : function(response){
48818         this.response = response;
48819         if(!response.responseText){
48820             return true;
48821         }
48822         this.result = this.handleResponse(response);
48823         return this.result;
48824     },
48825
48826     // utility functions used internally
48827     getUrl : function(appendParams){
48828         var url = this.options.url || this.form.url || this.form.el.dom.action;
48829         if(appendParams){
48830             var p = this.getParams();
48831             if(p){
48832                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
48833             }
48834         }
48835         return url;
48836     },
48837
48838     getMethod : function(){
48839         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
48840     },
48841
48842     getParams : function(){
48843         var bp = this.form.baseParams;
48844         var p = this.options.params;
48845         if(p){
48846             if(typeof p == "object"){
48847                 p = Roo.urlEncode(Roo.applyIf(p, bp));
48848             }else if(typeof p == 'string' && bp){
48849                 p += '&' + Roo.urlEncode(bp);
48850             }
48851         }else if(bp){
48852             p = Roo.urlEncode(bp);
48853         }
48854         return p;
48855     },
48856
48857     createCallback : function(){
48858         return {
48859             success: this.success,
48860             failure: this.failure,
48861             scope: this,
48862             timeout: (this.form.timeout*1000),
48863             upload: this.form.fileUpload ? this.success : undefined
48864         };
48865     }
48866 };
48867
48868 Roo.form.Action.Submit = function(form, options){
48869     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
48870 };
48871
48872 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
48873     type : 'submit',
48874
48875     haveProgress : false,
48876     uploadComplete : false,
48877     
48878     // uploadProgress indicator.
48879     uploadProgress : function()
48880     {
48881         if (!this.form.progressUrl) {
48882             return;
48883         }
48884         
48885         if (!this.haveProgress) {
48886             Roo.MessageBox.progress("Uploading", "Uploading");
48887         }
48888         if (this.uploadComplete) {
48889            Roo.MessageBox.hide();
48890            return;
48891         }
48892         
48893         this.haveProgress = true;
48894    
48895         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
48896         
48897         var c = new Roo.data.Connection();
48898         c.request({
48899             url : this.form.progressUrl,
48900             params: {
48901                 id : uid
48902             },
48903             method: 'GET',
48904             success : function(req){
48905                //console.log(data);
48906                 var rdata = false;
48907                 var edata;
48908                 try  {
48909                    rdata = Roo.decode(req.responseText)
48910                 } catch (e) {
48911                     Roo.log("Invalid data from server..");
48912                     Roo.log(edata);
48913                     return;
48914                 }
48915                 if (!rdata || !rdata.success) {
48916                     Roo.log(rdata);
48917                     Roo.MessageBox.alert(Roo.encode(rdata));
48918                     return;
48919                 }
48920                 var data = rdata.data;
48921                 
48922                 if (this.uploadComplete) {
48923                    Roo.MessageBox.hide();
48924                    return;
48925                 }
48926                    
48927                 if (data){
48928                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
48929                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
48930                     );
48931                 }
48932                 this.uploadProgress.defer(2000,this);
48933             },
48934        
48935             failure: function(data) {
48936                 Roo.log('progress url failed ');
48937                 Roo.log(data);
48938             },
48939             scope : this
48940         });
48941            
48942     },
48943     
48944     
48945     run : function()
48946     {
48947         // run get Values on the form, so it syncs any secondary forms.
48948         this.form.getValues();
48949         
48950         var o = this.options;
48951         var method = this.getMethod();
48952         var isPost = method == 'POST';
48953         if(o.clientValidation === false || this.form.isValid()){
48954             
48955             if (this.form.progressUrl) {
48956                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
48957                     (new Date() * 1) + '' + Math.random());
48958                     
48959             } 
48960             
48961             
48962             Roo.Ajax.request(Roo.apply(this.createCallback(), {
48963                 form:this.form.el.dom,
48964                 url:this.getUrl(!isPost),
48965                 method: method,
48966                 params:isPost ? this.getParams() : null,
48967                 isUpload: this.form.fileUpload,
48968                 formData : this.form.formData
48969             }));
48970             
48971             this.uploadProgress();
48972
48973         }else if (o.clientValidation !== false){ // client validation failed
48974             this.failureType = Roo.form.Action.CLIENT_INVALID;
48975             this.form.afterAction(this, false);
48976         }
48977     },
48978
48979     success : function(response)
48980     {
48981         this.uploadComplete= true;
48982         if (this.haveProgress) {
48983             Roo.MessageBox.hide();
48984         }
48985         
48986         
48987         var result = this.processResponse(response);
48988         if(result === true || result.success){
48989             this.form.afterAction(this, true);
48990             return;
48991         }
48992         if(result.errors){
48993             this.form.markInvalid(result.errors);
48994             this.failureType = Roo.form.Action.SERVER_INVALID;
48995         }
48996         this.form.afterAction(this, false);
48997     },
48998     failure : function(response)
48999     {
49000         this.uploadComplete= true;
49001         if (this.haveProgress) {
49002             Roo.MessageBox.hide();
49003         }
49004         
49005         this.response = response;
49006         this.failureType = Roo.form.Action.CONNECT_FAILURE;
49007         this.form.afterAction(this, false);
49008     },
49009     
49010     handleResponse : function(response){
49011         if(this.form.errorReader){
49012             var rs = this.form.errorReader.read(response);
49013             var errors = [];
49014             if(rs.records){
49015                 for(var i = 0, len = rs.records.length; i < len; i++) {
49016                     var r = rs.records[i];
49017                     errors[i] = r.data;
49018                 }
49019             }
49020             if(errors.length < 1){
49021                 errors = null;
49022             }
49023             return {
49024                 success : rs.success,
49025                 errors : errors
49026             };
49027         }
49028         var ret = false;
49029         try {
49030             ret = Roo.decode(response.responseText);
49031         } catch (e) {
49032             ret = {
49033                 success: false,
49034                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
49035                 errors : []
49036             };
49037         }
49038         return ret;
49039         
49040     }
49041 });
49042
49043
49044 Roo.form.Action.Load = function(form, options){
49045     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
49046     this.reader = this.form.reader;
49047 };
49048
49049 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
49050     type : 'load',
49051
49052     run : function(){
49053         
49054         Roo.Ajax.request(Roo.apply(
49055                 this.createCallback(), {
49056                     method:this.getMethod(),
49057                     url:this.getUrl(false),
49058                     params:this.getParams()
49059         }));
49060     },
49061
49062     success : function(response){
49063         
49064         var result = this.processResponse(response);
49065         if(result === true || !result.success || !result.data){
49066             this.failureType = Roo.form.Action.LOAD_FAILURE;
49067             this.form.afterAction(this, false);
49068             return;
49069         }
49070         this.form.clearInvalid();
49071         this.form.setValues(result.data);
49072         this.form.afterAction(this, true);
49073     },
49074
49075     handleResponse : function(response){
49076         if(this.form.reader){
49077             var rs = this.form.reader.read(response);
49078             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
49079             return {
49080                 success : rs.success,
49081                 data : data
49082             };
49083         }
49084         return Roo.decode(response.responseText);
49085     }
49086 });
49087
49088 Roo.form.Action.ACTION_TYPES = {
49089     'load' : Roo.form.Action.Load,
49090     'submit' : Roo.form.Action.Submit
49091 };/*
49092  * Based on:
49093  * Ext JS Library 1.1.1
49094  * Copyright(c) 2006-2007, Ext JS, LLC.
49095  *
49096  * Originally Released Under LGPL - original licence link has changed is not relivant.
49097  *
49098  * Fork - LGPL
49099  * <script type="text/javascript">
49100  */
49101  
49102 /**
49103  * @class Roo.form.Layout
49104  * @extends Roo.Component
49105  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
49106  * @constructor
49107  * @param {Object} config Configuration options
49108  */
49109 Roo.form.Layout = function(config){
49110     var xitems = [];
49111     if (config.items) {
49112         xitems = config.items;
49113         delete config.items;
49114     }
49115     Roo.form.Layout.superclass.constructor.call(this, config);
49116     this.stack = [];
49117     Roo.each(xitems, this.addxtype, this);
49118      
49119 };
49120
49121 Roo.extend(Roo.form.Layout, Roo.Component, {
49122     /**
49123      * @cfg {String/Object} autoCreate
49124      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
49125      */
49126     /**
49127      * @cfg {String/Object/Function} style
49128      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
49129      * a function which returns such a specification.
49130      */
49131     /**
49132      * @cfg {String} labelAlign
49133      * Valid values are "left," "top" and "right" (defaults to "left")
49134      */
49135     /**
49136      * @cfg {Number} labelWidth
49137      * Fixed width in pixels of all field labels (defaults to undefined)
49138      */
49139     /**
49140      * @cfg {Boolean} clear
49141      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
49142      */
49143     clear : true,
49144     /**
49145      * @cfg {String} labelSeparator
49146      * The separator to use after field labels (defaults to ':')
49147      */
49148     labelSeparator : ':',
49149     /**
49150      * @cfg {Boolean} hideLabels
49151      * True to suppress the display of field labels in this layout (defaults to false)
49152      */
49153     hideLabels : false,
49154
49155     // private
49156     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
49157     
49158     isLayout : true,
49159     
49160     // private
49161     onRender : function(ct, position){
49162         if(this.el){ // from markup
49163             this.el = Roo.get(this.el);
49164         }else {  // generate
49165             var cfg = this.getAutoCreate();
49166             this.el = ct.createChild(cfg, position);
49167         }
49168         if(this.style){
49169             this.el.applyStyles(this.style);
49170         }
49171         if(this.labelAlign){
49172             this.el.addClass('x-form-label-'+this.labelAlign);
49173         }
49174         if(this.hideLabels){
49175             this.labelStyle = "display:none";
49176             this.elementStyle = "padding-left:0;";
49177         }else{
49178             if(typeof this.labelWidth == 'number'){
49179                 this.labelStyle = "width:"+this.labelWidth+"px;";
49180                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
49181             }
49182             if(this.labelAlign == 'top'){
49183                 this.labelStyle = "width:auto;";
49184                 this.elementStyle = "padding-left:0;";
49185             }
49186         }
49187         var stack = this.stack;
49188         var slen = stack.length;
49189         if(slen > 0){
49190             if(!this.fieldTpl){
49191                 var t = new Roo.Template(
49192                     '<div class="x-form-item {5}">',
49193                         '<label for="{0}" style="{2}">{1}{4}</label>',
49194                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
49195                         '</div>',
49196                     '</div><div class="x-form-clear-left"></div>'
49197                 );
49198                 t.disableFormats = true;
49199                 t.compile();
49200                 Roo.form.Layout.prototype.fieldTpl = t;
49201             }
49202             for(var i = 0; i < slen; i++) {
49203                 if(stack[i].isFormField){
49204                     this.renderField(stack[i]);
49205                 }else{
49206                     this.renderComponent(stack[i]);
49207                 }
49208             }
49209         }
49210         if(this.clear){
49211             this.el.createChild({cls:'x-form-clear'});
49212         }
49213     },
49214
49215     // private
49216     renderField : function(f){
49217         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
49218                f.id, //0
49219                f.fieldLabel, //1
49220                f.labelStyle||this.labelStyle||'', //2
49221                this.elementStyle||'', //3
49222                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
49223                f.itemCls||this.itemCls||''  //5
49224        ], true).getPrevSibling());
49225     },
49226
49227     // private
49228     renderComponent : function(c){
49229         c.render(c.isLayout ? this.el : this.el.createChild());    
49230     },
49231     /**
49232      * Adds a object form elements (using the xtype property as the factory method.)
49233      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
49234      * @param {Object} config 
49235      */
49236     addxtype : function(o)
49237     {
49238         // create the lement.
49239         o.form = this.form;
49240         var fe = Roo.factory(o, Roo.form);
49241         this.form.allItems.push(fe);
49242         this.stack.push(fe);
49243         
49244         if (fe.isFormField) {
49245             this.form.items.add(fe);
49246         }
49247          
49248         return fe;
49249     }
49250 });
49251
49252 /**
49253  * @class Roo.form.Column
49254  * @extends Roo.form.Layout
49255  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
49256  * @constructor
49257  * @param {Object} config Configuration options
49258  */
49259 Roo.form.Column = function(config){
49260     Roo.form.Column.superclass.constructor.call(this, config);
49261 };
49262
49263 Roo.extend(Roo.form.Column, Roo.form.Layout, {
49264     /**
49265      * @cfg {Number/String} width
49266      * The fixed width of the column in pixels or CSS value (defaults to "auto")
49267      */
49268     /**
49269      * @cfg {String/Object} autoCreate
49270      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
49271      */
49272
49273     // private
49274     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
49275
49276     // private
49277     onRender : function(ct, position){
49278         Roo.form.Column.superclass.onRender.call(this, ct, position);
49279         if(this.width){
49280             this.el.setWidth(this.width);
49281         }
49282     }
49283 });
49284
49285
49286 /**
49287  * @class Roo.form.Row
49288  * @extends Roo.form.Layout
49289  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
49290  * @constructor
49291  * @param {Object} config Configuration options
49292  */
49293
49294  
49295 Roo.form.Row = function(config){
49296     Roo.form.Row.superclass.constructor.call(this, config);
49297 };
49298  
49299 Roo.extend(Roo.form.Row, Roo.form.Layout, {
49300       /**
49301      * @cfg {Number/String} width
49302      * The fixed width of the column in pixels or CSS value (defaults to "auto")
49303      */
49304     /**
49305      * @cfg {Number/String} height
49306      * The fixed height of the column in pixels or CSS value (defaults to "auto")
49307      */
49308     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
49309     
49310     padWidth : 20,
49311     // private
49312     onRender : function(ct, position){
49313         //console.log('row render');
49314         if(!this.rowTpl){
49315             var t = new Roo.Template(
49316                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
49317                     '<label for="{0}" style="{2}">{1}{4}</label>',
49318                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
49319                     '</div>',
49320                 '</div>'
49321             );
49322             t.disableFormats = true;
49323             t.compile();
49324             Roo.form.Layout.prototype.rowTpl = t;
49325         }
49326         this.fieldTpl = this.rowTpl;
49327         
49328         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
49329         var labelWidth = 100;
49330         
49331         if ((this.labelAlign != 'top')) {
49332             if (typeof this.labelWidth == 'number') {
49333                 labelWidth = this.labelWidth
49334             }
49335             this.padWidth =  20 + labelWidth;
49336             
49337         }
49338         
49339         Roo.form.Column.superclass.onRender.call(this, ct, position);
49340         if(this.width){
49341             this.el.setWidth(this.width);
49342         }
49343         if(this.height){
49344             this.el.setHeight(this.height);
49345         }
49346     },
49347     
49348     // private
49349     renderField : function(f){
49350         f.fieldEl = this.fieldTpl.append(this.el, [
49351                f.id, f.fieldLabel,
49352                f.labelStyle||this.labelStyle||'',
49353                this.elementStyle||'',
49354                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
49355                f.itemCls||this.itemCls||'',
49356                f.width ? f.width + this.padWidth : 160 + this.padWidth
49357        ],true);
49358     }
49359 });
49360  
49361
49362 /**
49363  * @class Roo.form.FieldSet
49364  * @extends Roo.form.Layout
49365  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
49366  * @constructor
49367  * @param {Object} config Configuration options
49368  */
49369 Roo.form.FieldSet = function(config){
49370     Roo.form.FieldSet.superclass.constructor.call(this, config);
49371 };
49372
49373 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
49374     /**
49375      * @cfg {String} legend
49376      * The text to display as the legend for the FieldSet (defaults to '')
49377      */
49378     /**
49379      * @cfg {String/Object} autoCreate
49380      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
49381      */
49382
49383     // private
49384     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
49385
49386     // private
49387     onRender : function(ct, position){
49388         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
49389         if(this.legend){
49390             this.setLegend(this.legend);
49391         }
49392     },
49393
49394     // private
49395     setLegend : function(text){
49396         if(this.rendered){
49397             this.el.child('legend').update(text);
49398         }
49399     }
49400 });/*
49401  * Based on:
49402  * Ext JS Library 1.1.1
49403  * Copyright(c) 2006-2007, Ext JS, LLC.
49404  *
49405  * Originally Released Under LGPL - original licence link has changed is not relivant.
49406  *
49407  * Fork - LGPL
49408  * <script type="text/javascript">
49409  */
49410 /**
49411  * @class Roo.form.VTypes
49412  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
49413  * @singleton
49414  */
49415 Roo.form.VTypes = function(){
49416     // closure these in so they are only created once.
49417     var alpha = /^[a-zA-Z_]+$/;
49418     var alphanum = /^[a-zA-Z0-9_]+$/;
49419     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
49420     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
49421
49422     // All these messages and functions are configurable
49423     return {
49424         /**
49425          * The function used to validate email addresses
49426          * @param {String} value The email address
49427          */
49428         'email' : function(v){
49429             return email.test(v);
49430         },
49431         /**
49432          * The error text to display when the email validation function returns false
49433          * @type String
49434          */
49435         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
49436         /**
49437          * The keystroke filter mask to be applied on email input
49438          * @type RegExp
49439          */
49440         'emailMask' : /[a-z0-9_\.\-@]/i,
49441
49442         /**
49443          * The function used to validate URLs
49444          * @param {String} value The URL
49445          */
49446         'url' : function(v){
49447             return url.test(v);
49448         },
49449         /**
49450          * The error text to display when the url validation function returns false
49451          * @type String
49452          */
49453         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
49454         
49455         /**
49456          * The function used to validate alpha values
49457          * @param {String} value The value
49458          */
49459         'alpha' : function(v){
49460             return alpha.test(v);
49461         },
49462         /**
49463          * The error text to display when the alpha validation function returns false
49464          * @type String
49465          */
49466         'alphaText' : 'This field should only contain letters and _',
49467         /**
49468          * The keystroke filter mask to be applied on alpha input
49469          * @type RegExp
49470          */
49471         'alphaMask' : /[a-z_]/i,
49472
49473         /**
49474          * The function used to validate alphanumeric values
49475          * @param {String} value The value
49476          */
49477         'alphanum' : function(v){
49478             return alphanum.test(v);
49479         },
49480         /**
49481          * The error text to display when the alphanumeric validation function returns false
49482          * @type String
49483          */
49484         'alphanumText' : 'This field should only contain letters, numbers and _',
49485         /**
49486          * The keystroke filter mask to be applied on alphanumeric input
49487          * @type RegExp
49488          */
49489         'alphanumMask' : /[a-z0-9_]/i
49490     };
49491 }();//<script type="text/javascript">
49492
49493 /**
49494  * @class Roo.form.FCKeditor
49495  * @extends Roo.form.TextArea
49496  * Wrapper around the FCKEditor http://www.fckeditor.net
49497  * @constructor
49498  * Creates a new FCKeditor
49499  * @param {Object} config Configuration options
49500  */
49501 Roo.form.FCKeditor = function(config){
49502     Roo.form.FCKeditor.superclass.constructor.call(this, config);
49503     this.addEvents({
49504          /**
49505          * @event editorinit
49506          * Fired when the editor is initialized - you can add extra handlers here..
49507          * @param {FCKeditor} this
49508          * @param {Object} the FCK object.
49509          */
49510         editorinit : true
49511     });
49512     
49513     
49514 };
49515 Roo.form.FCKeditor.editors = { };
49516 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
49517 {
49518     //defaultAutoCreate : {
49519     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
49520     //},
49521     // private
49522     /**
49523      * @cfg {Object} fck options - see fck manual for details.
49524      */
49525     fckconfig : false,
49526     
49527     /**
49528      * @cfg {Object} fck toolbar set (Basic or Default)
49529      */
49530     toolbarSet : 'Basic',
49531     /**
49532      * @cfg {Object} fck BasePath
49533      */ 
49534     basePath : '/fckeditor/',
49535     
49536     
49537     frame : false,
49538     
49539     value : '',
49540     
49541    
49542     onRender : function(ct, position)
49543     {
49544         if(!this.el){
49545             this.defaultAutoCreate = {
49546                 tag: "textarea",
49547                 style:"width:300px;height:60px;",
49548                 autocomplete: "new-password"
49549             };
49550         }
49551         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
49552         /*
49553         if(this.grow){
49554             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
49555             if(this.preventScrollbars){
49556                 this.el.setStyle("overflow", "hidden");
49557             }
49558             this.el.setHeight(this.growMin);
49559         }
49560         */
49561         //console.log('onrender' + this.getId() );
49562         Roo.form.FCKeditor.editors[this.getId()] = this;
49563          
49564
49565         this.replaceTextarea() ;
49566         
49567     },
49568     
49569     getEditor : function() {
49570         return this.fckEditor;
49571     },
49572     /**
49573      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
49574      * @param {Mixed} value The value to set
49575      */
49576     
49577     
49578     setValue : function(value)
49579     {
49580         //console.log('setValue: ' + value);
49581         
49582         if(typeof(value) == 'undefined') { // not sure why this is happending...
49583             return;
49584         }
49585         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
49586         
49587         //if(!this.el || !this.getEditor()) {
49588         //    this.value = value;
49589             //this.setValue.defer(100,this,[value]);    
49590         //    return;
49591         //} 
49592         
49593         if(!this.getEditor()) {
49594             return;
49595         }
49596         
49597         this.getEditor().SetData(value);
49598         
49599         //
49600
49601     },
49602
49603     /**
49604      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
49605      * @return {Mixed} value The field value
49606      */
49607     getValue : function()
49608     {
49609         
49610         if (this.frame && this.frame.dom.style.display == 'none') {
49611             return Roo.form.FCKeditor.superclass.getValue.call(this);
49612         }
49613         
49614         if(!this.el || !this.getEditor()) {
49615            
49616            // this.getValue.defer(100,this); 
49617             return this.value;
49618         }
49619        
49620         
49621         var value=this.getEditor().GetData();
49622         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
49623         return Roo.form.FCKeditor.superclass.getValue.call(this);
49624         
49625
49626     },
49627
49628     /**
49629      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
49630      * @return {Mixed} value The field value
49631      */
49632     getRawValue : function()
49633     {
49634         if (this.frame && this.frame.dom.style.display == 'none') {
49635             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
49636         }
49637         
49638         if(!this.el || !this.getEditor()) {
49639             //this.getRawValue.defer(100,this); 
49640             return this.value;
49641             return;
49642         }
49643         
49644         
49645         
49646         var value=this.getEditor().GetData();
49647         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
49648         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
49649          
49650     },
49651     
49652     setSize : function(w,h) {
49653         
49654         
49655         
49656         //if (this.frame && this.frame.dom.style.display == 'none') {
49657         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
49658         //    return;
49659         //}
49660         //if(!this.el || !this.getEditor()) {
49661         //    this.setSize.defer(100,this, [w,h]); 
49662         //    return;
49663         //}
49664         
49665         
49666         
49667         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
49668         
49669         this.frame.dom.setAttribute('width', w);
49670         this.frame.dom.setAttribute('height', h);
49671         this.frame.setSize(w,h);
49672         
49673     },
49674     
49675     toggleSourceEdit : function(value) {
49676         
49677       
49678          
49679         this.el.dom.style.display = value ? '' : 'none';
49680         this.frame.dom.style.display = value ?  'none' : '';
49681         
49682     },
49683     
49684     
49685     focus: function(tag)
49686     {
49687         if (this.frame.dom.style.display == 'none') {
49688             return Roo.form.FCKeditor.superclass.focus.call(this);
49689         }
49690         if(!this.el || !this.getEditor()) {
49691             this.focus.defer(100,this, [tag]); 
49692             return;
49693         }
49694         
49695         
49696         
49697         
49698         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
49699         this.getEditor().Focus();
49700         if (tgs.length) {
49701             if (!this.getEditor().Selection.GetSelection()) {
49702                 this.focus.defer(100,this, [tag]); 
49703                 return;
49704             }
49705             
49706             
49707             var r = this.getEditor().EditorDocument.createRange();
49708             r.setStart(tgs[0],0);
49709             r.setEnd(tgs[0],0);
49710             this.getEditor().Selection.GetSelection().removeAllRanges();
49711             this.getEditor().Selection.GetSelection().addRange(r);
49712             this.getEditor().Focus();
49713         }
49714         
49715     },
49716     
49717     
49718     
49719     replaceTextarea : function()
49720     {
49721         if ( document.getElementById( this.getId() + '___Frame' ) ) {
49722             return ;
49723         }
49724         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
49725         //{
49726             // We must check the elements firstly using the Id and then the name.
49727         var oTextarea = document.getElementById( this.getId() );
49728         
49729         var colElementsByName = document.getElementsByName( this.getId() ) ;
49730          
49731         oTextarea.style.display = 'none' ;
49732
49733         if ( oTextarea.tabIndex ) {            
49734             this.TabIndex = oTextarea.tabIndex ;
49735         }
49736         
49737         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
49738         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
49739         this.frame = Roo.get(this.getId() + '___Frame')
49740     },
49741     
49742     _getConfigHtml : function()
49743     {
49744         var sConfig = '' ;
49745
49746         for ( var o in this.fckconfig ) {
49747             sConfig += sConfig.length > 0  ? '&amp;' : '';
49748             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
49749         }
49750
49751         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
49752     },
49753     
49754     
49755     _getIFrameHtml : function()
49756     {
49757         var sFile = 'fckeditor.html' ;
49758         /* no idea what this is about..
49759         try
49760         {
49761             if ( (/fcksource=true/i).test( window.top.location.search ) )
49762                 sFile = 'fckeditor.original.html' ;
49763         }
49764         catch (e) { 
49765         */
49766
49767         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
49768         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
49769         
49770         
49771         var html = '<iframe id="' + this.getId() +
49772             '___Frame" src="' + sLink +
49773             '" width="' + this.width +
49774             '" height="' + this.height + '"' +
49775             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
49776             ' frameborder="0" scrolling="no"></iframe>' ;
49777
49778         return html ;
49779     },
49780     
49781     _insertHtmlBefore : function( html, element )
49782     {
49783         if ( element.insertAdjacentHTML )       {
49784             // IE
49785             element.insertAdjacentHTML( 'beforeBegin', html ) ;
49786         } else { // Gecko
49787             var oRange = document.createRange() ;
49788             oRange.setStartBefore( element ) ;
49789             var oFragment = oRange.createContextualFragment( html );
49790             element.parentNode.insertBefore( oFragment, element ) ;
49791         }
49792     }
49793     
49794     
49795   
49796     
49797     
49798     
49799     
49800
49801 });
49802
49803 //Roo.reg('fckeditor', Roo.form.FCKeditor);
49804
49805 function FCKeditor_OnComplete(editorInstance){
49806     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
49807     f.fckEditor = editorInstance;
49808     //console.log("loaded");
49809     f.fireEvent('editorinit', f, editorInstance);
49810
49811   
49812
49813  
49814
49815
49816
49817
49818
49819
49820
49821
49822
49823
49824
49825
49826
49827
49828
49829 //<script type="text/javascript">
49830 /**
49831  * @class Roo.form.GridField
49832  * @extends Roo.form.Field
49833  * Embed a grid (or editable grid into a form)
49834  * STATUS ALPHA
49835  * 
49836  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
49837  * it needs 
49838  * xgrid.store = Roo.data.Store
49839  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
49840  * xgrid.store.reader = Roo.data.JsonReader 
49841  * 
49842  * 
49843  * @constructor
49844  * Creates a new GridField
49845  * @param {Object} config Configuration options
49846  */
49847 Roo.form.GridField = function(config){
49848     Roo.form.GridField.superclass.constructor.call(this, config);
49849      
49850 };
49851
49852 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
49853     /**
49854      * @cfg {Number} width  - used to restrict width of grid..
49855      */
49856     width : 100,
49857     /**
49858      * @cfg {Number} height - used to restrict height of grid..
49859      */
49860     height : 50,
49861      /**
49862      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
49863          * 
49864          *}
49865      */
49866     xgrid : false, 
49867     /**
49868      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
49869      * {tag: "input", type: "checkbox", autocomplete: "off"})
49870      */
49871    // defaultAutoCreate : { tag: 'div' },
49872     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
49873     /**
49874      * @cfg {String} addTitle Text to include for adding a title.
49875      */
49876     addTitle : false,
49877     //
49878     onResize : function(){
49879         Roo.form.Field.superclass.onResize.apply(this, arguments);
49880     },
49881
49882     initEvents : function(){
49883         // Roo.form.Checkbox.superclass.initEvents.call(this);
49884         // has no events...
49885        
49886     },
49887
49888
49889     getResizeEl : function(){
49890         return this.wrap;
49891     },
49892
49893     getPositionEl : function(){
49894         return this.wrap;
49895     },
49896
49897     // private
49898     onRender : function(ct, position){
49899         
49900         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
49901         var style = this.style;
49902         delete this.style;
49903         
49904         Roo.form.GridField.superclass.onRender.call(this, ct, position);
49905         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
49906         this.viewEl = this.wrap.createChild({ tag: 'div' });
49907         if (style) {
49908             this.viewEl.applyStyles(style);
49909         }
49910         if (this.width) {
49911             this.viewEl.setWidth(this.width);
49912         }
49913         if (this.height) {
49914             this.viewEl.setHeight(this.height);
49915         }
49916         //if(this.inputValue !== undefined){
49917         //this.setValue(this.value);
49918         
49919         
49920         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
49921         
49922         
49923         this.grid.render();
49924         this.grid.getDataSource().on('remove', this.refreshValue, this);
49925         this.grid.getDataSource().on('update', this.refreshValue, this);
49926         this.grid.on('afteredit', this.refreshValue, this);
49927  
49928     },
49929      
49930     
49931     /**
49932      * Sets the value of the item. 
49933      * @param {String} either an object  or a string..
49934      */
49935     setValue : function(v){
49936         //this.value = v;
49937         v = v || []; // empty set..
49938         // this does not seem smart - it really only affects memoryproxy grids..
49939         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
49940             var ds = this.grid.getDataSource();
49941             // assumes a json reader..
49942             var data = {}
49943             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
49944             ds.loadData( data);
49945         }
49946         // clear selection so it does not get stale.
49947         if (this.grid.sm) { 
49948             this.grid.sm.clearSelections();
49949         }
49950         
49951         Roo.form.GridField.superclass.setValue.call(this, v);
49952         this.refreshValue();
49953         // should load data in the grid really....
49954     },
49955     
49956     // private
49957     refreshValue: function() {
49958          var val = [];
49959         this.grid.getDataSource().each(function(r) {
49960             val.push(r.data);
49961         });
49962         this.el.dom.value = Roo.encode(val);
49963     }
49964     
49965      
49966     
49967     
49968 });/*
49969  * Based on:
49970  * Ext JS Library 1.1.1
49971  * Copyright(c) 2006-2007, Ext JS, LLC.
49972  *
49973  * Originally Released Under LGPL - original licence link has changed is not relivant.
49974  *
49975  * Fork - LGPL
49976  * <script type="text/javascript">
49977  */
49978 /**
49979  * @class Roo.form.DisplayField
49980  * @extends Roo.form.Field
49981  * A generic Field to display non-editable data.
49982  * @cfg {Boolean} closable (true|false) default false
49983  * @constructor
49984  * Creates a new Display Field item.
49985  * @param {Object} config Configuration options
49986  */
49987 Roo.form.DisplayField = function(config){
49988     Roo.form.DisplayField.superclass.constructor.call(this, config);
49989     
49990     this.addEvents({
49991         /**
49992          * @event close
49993          * Fires after the click the close btn
49994              * @param {Roo.form.DisplayField} this
49995              */
49996         close : true
49997     });
49998 };
49999
50000 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
50001     inputType:      'hidden',
50002     allowBlank:     true,
50003     readOnly:         true,
50004     
50005  
50006     /**
50007      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
50008      */
50009     focusClass : undefined,
50010     /**
50011      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
50012      */
50013     fieldClass: 'x-form-field',
50014     
50015      /**
50016      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
50017      */
50018     valueRenderer: undefined,
50019     
50020     width: 100,
50021     /**
50022      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
50023      * {tag: "input", type: "checkbox", autocomplete: "off"})
50024      */
50025      
50026  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
50027  
50028     closable : false,
50029     
50030     onResize : function(){
50031         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
50032         
50033     },
50034
50035     initEvents : function(){
50036         // Roo.form.Checkbox.superclass.initEvents.call(this);
50037         // has no events...
50038         
50039         if(this.closable){
50040             this.closeEl.on('click', this.onClose, this);
50041         }
50042        
50043     },
50044
50045
50046     getResizeEl : function(){
50047         return this.wrap;
50048     },
50049
50050     getPositionEl : function(){
50051         return this.wrap;
50052     },
50053
50054     // private
50055     onRender : function(ct, position){
50056         
50057         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
50058         //if(this.inputValue !== undefined){
50059         this.wrap = this.el.wrap();
50060         
50061         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
50062         
50063         if(this.closable){
50064             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
50065         }
50066         
50067         if (this.bodyStyle) {
50068             this.viewEl.applyStyles(this.bodyStyle);
50069         }
50070         //this.viewEl.setStyle('padding', '2px');
50071         
50072         this.setValue(this.value);
50073         
50074     },
50075 /*
50076     // private
50077     initValue : Roo.emptyFn,
50078
50079   */
50080
50081         // private
50082     onClick : function(){
50083         
50084     },
50085
50086     /**
50087      * Sets the checked state of the checkbox.
50088      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
50089      */
50090     setValue : function(v){
50091         this.value = v;
50092         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
50093         // this might be called before we have a dom element..
50094         if (!this.viewEl) {
50095             return;
50096         }
50097         this.viewEl.dom.innerHTML = html;
50098         Roo.form.DisplayField.superclass.setValue.call(this, v);
50099
50100     },
50101     
50102     onClose : function(e)
50103     {
50104         e.preventDefault();
50105         
50106         this.fireEvent('close', this);
50107     }
50108 });/*
50109  * 
50110  * Licence- LGPL
50111  * 
50112  */
50113
50114 /**
50115  * @class Roo.form.DayPicker
50116  * @extends Roo.form.Field
50117  * A Day picker show [M] [T] [W] ....
50118  * @constructor
50119  * Creates a new Day Picker
50120  * @param {Object} config Configuration options
50121  */
50122 Roo.form.DayPicker= function(config){
50123     Roo.form.DayPicker.superclass.constructor.call(this, config);
50124      
50125 };
50126
50127 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
50128     /**
50129      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
50130      */
50131     focusClass : undefined,
50132     /**
50133      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
50134      */
50135     fieldClass: "x-form-field",
50136    
50137     /**
50138      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
50139      * {tag: "input", type: "checkbox", autocomplete: "off"})
50140      */
50141     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
50142     
50143    
50144     actionMode : 'viewEl', 
50145     //
50146     // private
50147  
50148     inputType : 'hidden',
50149     
50150      
50151     inputElement: false, // real input element?
50152     basedOn: false, // ????
50153     
50154     isFormField: true, // not sure where this is needed!!!!
50155
50156     onResize : function(){
50157         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
50158         if(!this.boxLabel){
50159             this.el.alignTo(this.wrap, 'c-c');
50160         }
50161     },
50162
50163     initEvents : function(){
50164         Roo.form.Checkbox.superclass.initEvents.call(this);
50165         this.el.on("click", this.onClick,  this);
50166         this.el.on("change", this.onClick,  this);
50167     },
50168
50169
50170     getResizeEl : function(){
50171         return this.wrap;
50172     },
50173
50174     getPositionEl : function(){
50175         return this.wrap;
50176     },
50177
50178     
50179     // private
50180     onRender : function(ct, position){
50181         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
50182        
50183         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
50184         
50185         var r1 = '<table><tr>';
50186         var r2 = '<tr class="x-form-daypick-icons">';
50187         for (var i=0; i < 7; i++) {
50188             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
50189             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
50190         }
50191         
50192         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
50193         viewEl.select('img').on('click', this.onClick, this);
50194         this.viewEl = viewEl;   
50195         
50196         
50197         // this will not work on Chrome!!!
50198         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
50199         this.el.on('propertychange', this.setFromHidden,  this);  //ie
50200         
50201         
50202           
50203
50204     },
50205
50206     // private
50207     initValue : Roo.emptyFn,
50208
50209     /**
50210      * Returns the checked state of the checkbox.
50211      * @return {Boolean} True if checked, else false
50212      */
50213     getValue : function(){
50214         return this.el.dom.value;
50215         
50216     },
50217
50218         // private
50219     onClick : function(e){ 
50220         //this.setChecked(!this.checked);
50221         Roo.get(e.target).toggleClass('x-menu-item-checked');
50222         this.refreshValue();
50223         //if(this.el.dom.checked != this.checked){
50224         //    this.setValue(this.el.dom.checked);
50225        // }
50226     },
50227     
50228     // private
50229     refreshValue : function()
50230     {
50231         var val = '';
50232         this.viewEl.select('img',true).each(function(e,i,n)  {
50233             val += e.is(".x-menu-item-checked") ? String(n) : '';
50234         });
50235         this.setValue(val, true);
50236     },
50237
50238     /**
50239      * Sets the checked state of the checkbox.
50240      * On is always based on a string comparison between inputValue and the param.
50241      * @param {Boolean/String} value - the value to set 
50242      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
50243      */
50244     setValue : function(v,suppressEvent){
50245         if (!this.el.dom) {
50246             return;
50247         }
50248         var old = this.el.dom.value ;
50249         this.el.dom.value = v;
50250         if (suppressEvent) {
50251             return ;
50252         }
50253          
50254         // update display..
50255         this.viewEl.select('img',true).each(function(e,i,n)  {
50256             
50257             var on = e.is(".x-menu-item-checked");
50258             var newv = v.indexOf(String(n)) > -1;
50259             if (on != newv) {
50260                 e.toggleClass('x-menu-item-checked');
50261             }
50262             
50263         });
50264         
50265         
50266         this.fireEvent('change', this, v, old);
50267         
50268         
50269     },
50270    
50271     // handle setting of hidden value by some other method!!?!?
50272     setFromHidden: function()
50273     {
50274         if(!this.el){
50275             return;
50276         }
50277         //console.log("SET FROM HIDDEN");
50278         //alert('setFrom hidden');
50279         this.setValue(this.el.dom.value);
50280     },
50281     
50282     onDestroy : function()
50283     {
50284         if(this.viewEl){
50285             Roo.get(this.viewEl).remove();
50286         }
50287          
50288         Roo.form.DayPicker.superclass.onDestroy.call(this);
50289     }
50290
50291 });/*
50292  * RooJS Library 1.1.1
50293  * Copyright(c) 2008-2011  Alan Knowles
50294  *
50295  * License - LGPL
50296  */
50297  
50298
50299 /**
50300  * @class Roo.form.ComboCheck
50301  * @extends Roo.form.ComboBox
50302  * A combobox for multiple select items.
50303  *
50304  * FIXME - could do with a reset button..
50305  * 
50306  * @constructor
50307  * Create a new ComboCheck
50308  * @param {Object} config Configuration options
50309  */
50310 Roo.form.ComboCheck = function(config){
50311     Roo.form.ComboCheck.superclass.constructor.call(this, config);
50312     // should verify some data...
50313     // like
50314     // hiddenName = required..
50315     // displayField = required
50316     // valudField == required
50317     var req= [ 'hiddenName', 'displayField', 'valueField' ];
50318     var _t = this;
50319     Roo.each(req, function(e) {
50320         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
50321             throw "Roo.form.ComboCheck : missing value for: " + e;
50322         }
50323     });
50324     
50325     
50326 };
50327
50328 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
50329      
50330      
50331     editable : false,
50332      
50333     selectedClass: 'x-menu-item-checked', 
50334     
50335     // private
50336     onRender : function(ct, position){
50337         var _t = this;
50338         
50339         
50340         
50341         if(!this.tpl){
50342             var cls = 'x-combo-list';
50343
50344             
50345             this.tpl =  new Roo.Template({
50346                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
50347                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
50348                    '<span>{' + this.displayField + '}</span>' +
50349                     '</div>' 
50350                 
50351             });
50352         }
50353  
50354         
50355         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
50356         this.view.singleSelect = false;
50357         this.view.multiSelect = true;
50358         this.view.toggleSelect = true;
50359         this.pageTb.add(new Roo.Toolbar.Fill(), {
50360             
50361             text: 'Done',
50362             handler: function()
50363             {
50364                 _t.collapse();
50365             }
50366         });
50367     },
50368     
50369     onViewOver : function(e, t){
50370         // do nothing...
50371         return;
50372         
50373     },
50374     
50375     onViewClick : function(doFocus,index){
50376         return;
50377         
50378     },
50379     select: function () {
50380         //Roo.log("SELECT CALLED");
50381     },
50382      
50383     selectByValue : function(xv, scrollIntoView){
50384         var ar = this.getValueArray();
50385         var sels = [];
50386         
50387         Roo.each(ar, function(v) {
50388             if(v === undefined || v === null){
50389                 return;
50390             }
50391             var r = this.findRecord(this.valueField, v);
50392             if(r){
50393                 sels.push(this.store.indexOf(r))
50394                 
50395             }
50396         },this);
50397         this.view.select(sels);
50398         return false;
50399     },
50400     
50401     
50402     
50403     onSelect : function(record, index){
50404        // Roo.log("onselect Called");
50405        // this is only called by the clear button now..
50406         this.view.clearSelections();
50407         this.setValue('[]');
50408         if (this.value != this.valueBefore) {
50409             this.fireEvent('change', this, this.value, this.valueBefore);
50410             this.valueBefore = this.value;
50411         }
50412     },
50413     getValueArray : function()
50414     {
50415         var ar = [] ;
50416         
50417         try {
50418             //Roo.log(this.value);
50419             if (typeof(this.value) == 'undefined') {
50420                 return [];
50421             }
50422             var ar = Roo.decode(this.value);
50423             return  ar instanceof Array ? ar : []; //?? valid?
50424             
50425         } catch(e) {
50426             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
50427             return [];
50428         }
50429          
50430     },
50431     expand : function ()
50432     {
50433         
50434         Roo.form.ComboCheck.superclass.expand.call(this);
50435         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
50436         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
50437         
50438
50439     },
50440     
50441     collapse : function(){
50442         Roo.form.ComboCheck.superclass.collapse.call(this);
50443         var sl = this.view.getSelectedIndexes();
50444         var st = this.store;
50445         var nv = [];
50446         var tv = [];
50447         var r;
50448         Roo.each(sl, function(i) {
50449             r = st.getAt(i);
50450             nv.push(r.get(this.valueField));
50451         },this);
50452         this.setValue(Roo.encode(nv));
50453         if (this.value != this.valueBefore) {
50454
50455             this.fireEvent('change', this, this.value, this.valueBefore);
50456             this.valueBefore = this.value;
50457         }
50458         
50459     },
50460     
50461     setValue : function(v){
50462         // Roo.log(v);
50463         this.value = v;
50464         
50465         var vals = this.getValueArray();
50466         var tv = [];
50467         Roo.each(vals, function(k) {
50468             var r = this.findRecord(this.valueField, k);
50469             if(r){
50470                 tv.push(r.data[this.displayField]);
50471             }else if(this.valueNotFoundText !== undefined){
50472                 tv.push( this.valueNotFoundText );
50473             }
50474         },this);
50475        // Roo.log(tv);
50476         
50477         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
50478         this.hiddenField.value = v;
50479         this.value = v;
50480     }
50481     
50482 });/*
50483  * Based on:
50484  * Ext JS Library 1.1.1
50485  * Copyright(c) 2006-2007, Ext JS, LLC.
50486  *
50487  * Originally Released Under LGPL - original licence link has changed is not relivant.
50488  *
50489  * Fork - LGPL
50490  * <script type="text/javascript">
50491  */
50492  
50493 /**
50494  * @class Roo.form.Signature
50495  * @extends Roo.form.Field
50496  * Signature field.  
50497  * @constructor
50498  * 
50499  * @param {Object} config Configuration options
50500  */
50501
50502 Roo.form.Signature = function(config){
50503     Roo.form.Signature.superclass.constructor.call(this, config);
50504     
50505     this.addEvents({// not in used??
50506          /**
50507          * @event confirm
50508          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
50509              * @param {Roo.form.Signature} combo This combo box
50510              */
50511         'confirm' : true,
50512         /**
50513          * @event reset
50514          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
50515              * @param {Roo.form.ComboBox} combo This combo box
50516              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
50517              */
50518         'reset' : true
50519     });
50520 };
50521
50522 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
50523     /**
50524      * @cfg {Object} labels Label to use when rendering a form.
50525      * defaults to 
50526      * labels : { 
50527      *      clear : "Clear",
50528      *      confirm : "Confirm"
50529      *  }
50530      */
50531     labels : { 
50532         clear : "Clear",
50533         confirm : "Confirm"
50534     },
50535     /**
50536      * @cfg {Number} width The signature panel width (defaults to 300)
50537      */
50538     width: 300,
50539     /**
50540      * @cfg {Number} height The signature panel height (defaults to 100)
50541      */
50542     height : 100,
50543     /**
50544      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
50545      */
50546     allowBlank : false,
50547     
50548     //private
50549     // {Object} signPanel The signature SVG panel element (defaults to {})
50550     signPanel : {},
50551     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
50552     isMouseDown : false,
50553     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
50554     isConfirmed : false,
50555     // {String} signatureTmp SVG mapping string (defaults to empty string)
50556     signatureTmp : '',
50557     
50558     
50559     defaultAutoCreate : { // modified by initCompnoent..
50560         tag: "input",
50561         type:"hidden"
50562     },
50563
50564     // private
50565     onRender : function(ct, position){
50566         
50567         Roo.form.Signature.superclass.onRender.call(this, ct, position);
50568         
50569         this.wrap = this.el.wrap({
50570             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
50571         });
50572         
50573         this.createToolbar(this);
50574         this.signPanel = this.wrap.createChild({
50575                 tag: 'div',
50576                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
50577             }, this.el
50578         );
50579             
50580         this.svgID = Roo.id();
50581         this.svgEl = this.signPanel.createChild({
50582               xmlns : 'http://www.w3.org/2000/svg',
50583               tag : 'svg',
50584               id : this.svgID + "-svg",
50585               width: this.width,
50586               height: this.height,
50587               viewBox: '0 0 '+this.width+' '+this.height,
50588               cn : [
50589                 {
50590                     tag: "rect",
50591                     id: this.svgID + "-svg-r",
50592                     width: this.width,
50593                     height: this.height,
50594                     fill: "#ffa"
50595                 },
50596                 {
50597                     tag: "line",
50598                     id: this.svgID + "-svg-l",
50599                     x1: "0", // start
50600                     y1: (this.height*0.8), // start set the line in 80% of height
50601                     x2: this.width, // end
50602                     y2: (this.height*0.8), // end set the line in 80% of height
50603                     'stroke': "#666",
50604                     'stroke-width': "1",
50605                     'stroke-dasharray': "3",
50606                     'shape-rendering': "crispEdges",
50607                     'pointer-events': "none"
50608                 },
50609                 {
50610                     tag: "path",
50611                     id: this.svgID + "-svg-p",
50612                     'stroke': "navy",
50613                     'stroke-width': "3",
50614                     'fill': "none",
50615                     'pointer-events': 'none'
50616                 }
50617               ]
50618         });
50619         this.createSVG();
50620         this.svgBox = this.svgEl.dom.getScreenCTM();
50621     },
50622     createSVG : function(){ 
50623         var svg = this.signPanel;
50624         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
50625         var t = this;
50626
50627         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
50628         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
50629         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
50630         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
50631         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
50632         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
50633         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
50634         
50635     },
50636     isTouchEvent : function(e){
50637         return e.type.match(/^touch/);
50638     },
50639     getCoords : function (e) {
50640         var pt    = this.svgEl.dom.createSVGPoint();
50641         pt.x = e.clientX; 
50642         pt.y = e.clientY;
50643         if (this.isTouchEvent(e)) {
50644             pt.x =  e.targetTouches[0].clientX;
50645             pt.y = e.targetTouches[0].clientY;
50646         }
50647         var a = this.svgEl.dom.getScreenCTM();
50648         var b = a.inverse();
50649         var mx = pt.matrixTransform(b);
50650         return mx.x + ',' + mx.y;
50651     },
50652     //mouse event headler 
50653     down : function (e) {
50654         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
50655         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
50656         
50657         this.isMouseDown = true;
50658         
50659         e.preventDefault();
50660     },
50661     move : function (e) {
50662         if (this.isMouseDown) {
50663             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
50664             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
50665         }
50666         
50667         e.preventDefault();
50668     },
50669     up : function (e) {
50670         this.isMouseDown = false;
50671         var sp = this.signatureTmp.split(' ');
50672         
50673         if(sp.length > 1){
50674             if(!sp[sp.length-2].match(/^L/)){
50675                 sp.pop();
50676                 sp.pop();
50677                 sp.push("");
50678                 this.signatureTmp = sp.join(" ");
50679             }
50680         }
50681         if(this.getValue() != this.signatureTmp){
50682             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
50683             this.isConfirmed = false;
50684         }
50685         e.preventDefault();
50686     },
50687     
50688     /**
50689      * Protected method that will not generally be called directly. It
50690      * is called when the editor creates its toolbar. Override this method if you need to
50691      * add custom toolbar buttons.
50692      * @param {HtmlEditor} editor
50693      */
50694     createToolbar : function(editor){
50695          function btn(id, toggle, handler){
50696             var xid = fid + '-'+ id ;
50697             return {
50698                 id : xid,
50699                 cmd : id,
50700                 cls : 'x-btn-icon x-edit-'+id,
50701                 enableToggle:toggle !== false,
50702                 scope: editor, // was editor...
50703                 handler:handler||editor.relayBtnCmd,
50704                 clickEvent:'mousedown',
50705                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
50706                 tabIndex:-1
50707             };
50708         }
50709         
50710         
50711         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
50712         this.tb = tb;
50713         this.tb.add(
50714            {
50715                 cls : ' x-signature-btn x-signature-'+id,
50716                 scope: editor, // was editor...
50717                 handler: this.reset,
50718                 clickEvent:'mousedown',
50719                 text: this.labels.clear
50720             },
50721             {
50722                  xtype : 'Fill',
50723                  xns: Roo.Toolbar
50724             }, 
50725             {
50726                 cls : '  x-signature-btn x-signature-'+id,
50727                 scope: editor, // was editor...
50728                 handler: this.confirmHandler,
50729                 clickEvent:'mousedown',
50730                 text: this.labels.confirm
50731             }
50732         );
50733     
50734     },
50735     //public
50736     /**
50737      * when user is clicked confirm then show this image.....
50738      * 
50739      * @return {String} Image Data URI
50740      */
50741     getImageDataURI : function(){
50742         var svg = this.svgEl.dom.parentNode.innerHTML;
50743         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
50744         return src; 
50745     },
50746     /**
50747      * 
50748      * @return {Boolean} this.isConfirmed
50749      */
50750     getConfirmed : function(){
50751         return this.isConfirmed;
50752     },
50753     /**
50754      * 
50755      * @return {Number} this.width
50756      */
50757     getWidth : function(){
50758         return this.width;
50759     },
50760     /**
50761      * 
50762      * @return {Number} this.height
50763      */
50764     getHeight : function(){
50765         return this.height;
50766     },
50767     // private
50768     getSignature : function(){
50769         return this.signatureTmp;
50770     },
50771     // private
50772     reset : function(){
50773         this.signatureTmp = '';
50774         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
50775         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
50776         this.isConfirmed = false;
50777         Roo.form.Signature.superclass.reset.call(this);
50778     },
50779     setSignature : function(s){
50780         this.signatureTmp = s;
50781         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
50782         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
50783         this.setValue(s);
50784         this.isConfirmed = false;
50785         Roo.form.Signature.superclass.reset.call(this);
50786     }, 
50787     test : function(){
50788 //        Roo.log(this.signPanel.dom.contentWindow.up())
50789     },
50790     //private
50791     setConfirmed : function(){
50792         
50793         
50794         
50795 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
50796     },
50797     // private
50798     confirmHandler : function(){
50799         if(!this.getSignature()){
50800             return;
50801         }
50802         
50803         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
50804         this.setValue(this.getSignature());
50805         this.isConfirmed = true;
50806         
50807         this.fireEvent('confirm', this);
50808     },
50809     // private
50810     // Subclasses should provide the validation implementation by overriding this
50811     validateValue : function(value){
50812         if(this.allowBlank){
50813             return true;
50814         }
50815         
50816         if(this.isConfirmed){
50817             return true;
50818         }
50819         return false;
50820     }
50821 });/*
50822  * Based on:
50823  * Ext JS Library 1.1.1
50824  * Copyright(c) 2006-2007, Ext JS, LLC.
50825  *
50826  * Originally Released Under LGPL - original licence link has changed is not relivant.
50827  *
50828  * Fork - LGPL
50829  * <script type="text/javascript">
50830  */
50831  
50832
50833 /**
50834  * @class Roo.form.ComboBox
50835  * @extends Roo.form.TriggerField
50836  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
50837  * @constructor
50838  * Create a new ComboBox.
50839  * @param {Object} config Configuration options
50840  */
50841 Roo.form.Select = function(config){
50842     Roo.form.Select.superclass.constructor.call(this, config);
50843      
50844 };
50845
50846 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
50847     /**
50848      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
50849      */
50850     /**
50851      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
50852      * rendering into an Roo.Editor, defaults to false)
50853      */
50854     /**
50855      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
50856      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
50857      */
50858     /**
50859      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
50860      */
50861     /**
50862      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
50863      * the dropdown list (defaults to undefined, with no header element)
50864      */
50865
50866      /**
50867      * @cfg {String/Roo.Template} tpl The template to use to render the output
50868      */
50869      
50870     // private
50871     defaultAutoCreate : {tag: "select"  },
50872     /**
50873      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
50874      */
50875     listWidth: undefined,
50876     /**
50877      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
50878      * mode = 'remote' or 'text' if mode = 'local')
50879      */
50880     displayField: undefined,
50881     /**
50882      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
50883      * mode = 'remote' or 'value' if mode = 'local'). 
50884      * Note: use of a valueField requires the user make a selection
50885      * in order for a value to be mapped.
50886      */
50887     valueField: undefined,
50888     
50889     
50890     /**
50891      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
50892      * field's data value (defaults to the underlying DOM element's name)
50893      */
50894     hiddenName: undefined,
50895     /**
50896      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
50897      */
50898     listClass: '',
50899     /**
50900      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
50901      */
50902     selectedClass: 'x-combo-selected',
50903     /**
50904      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
50905      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
50906      * which displays a downward arrow icon).
50907      */
50908     triggerClass : 'x-form-arrow-trigger',
50909     /**
50910      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
50911      */
50912     shadow:'sides',
50913     /**
50914      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
50915      * anchor positions (defaults to 'tl-bl')
50916      */
50917     listAlign: 'tl-bl?',
50918     /**
50919      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
50920      */
50921     maxHeight: 300,
50922     /**
50923      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
50924      * query specified by the allQuery config option (defaults to 'query')
50925      */
50926     triggerAction: 'query',
50927     /**
50928      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
50929      * (defaults to 4, does not apply if editable = false)
50930      */
50931     minChars : 4,
50932     /**
50933      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
50934      * delay (typeAheadDelay) if it matches a known value (defaults to false)
50935      */
50936     typeAhead: false,
50937     /**
50938      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
50939      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
50940      */
50941     queryDelay: 500,
50942     /**
50943      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
50944      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
50945      */
50946     pageSize: 0,
50947     /**
50948      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
50949      * when editable = true (defaults to false)
50950      */
50951     selectOnFocus:false,
50952     /**
50953      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
50954      */
50955     queryParam: 'query',
50956     /**
50957      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
50958      * when mode = 'remote' (defaults to 'Loading...')
50959      */
50960     loadingText: 'Loading...',
50961     /**
50962      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
50963      */
50964     resizable: false,
50965     /**
50966      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
50967      */
50968     handleHeight : 8,
50969     /**
50970      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
50971      * traditional select (defaults to true)
50972      */
50973     editable: true,
50974     /**
50975      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
50976      */
50977     allQuery: '',
50978     /**
50979      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
50980      */
50981     mode: 'remote',
50982     /**
50983      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
50984      * listWidth has a higher value)
50985      */
50986     minListWidth : 70,
50987     /**
50988      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
50989      * allow the user to set arbitrary text into the field (defaults to false)
50990      */
50991     forceSelection:false,
50992     /**
50993      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
50994      * if typeAhead = true (defaults to 250)
50995      */
50996     typeAheadDelay : 250,
50997     /**
50998      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
50999      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
51000      */
51001     valueNotFoundText : undefined,
51002     
51003     /**
51004      * @cfg {String} defaultValue The value displayed after loading the store.
51005      */
51006     defaultValue: '',
51007     
51008     /**
51009      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
51010      */
51011     blockFocus : false,
51012     
51013     /**
51014      * @cfg {Boolean} disableClear Disable showing of clear button.
51015      */
51016     disableClear : false,
51017     /**
51018      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
51019      */
51020     alwaysQuery : false,
51021     
51022     //private
51023     addicon : false,
51024     editicon: false,
51025     
51026     // element that contains real text value.. (when hidden is used..)
51027      
51028     // private
51029     onRender : function(ct, position){
51030         Roo.form.Field.prototype.onRender.call(this, ct, position);
51031         
51032         if(this.store){
51033             this.store.on('beforeload', this.onBeforeLoad, this);
51034             this.store.on('load', this.onLoad, this);
51035             this.store.on('loadexception', this.onLoadException, this);
51036             this.store.load({});
51037         }
51038         
51039         
51040         
51041     },
51042
51043     // private
51044     initEvents : function(){
51045         //Roo.form.ComboBox.superclass.initEvents.call(this);
51046  
51047     },
51048
51049     onDestroy : function(){
51050        
51051         if(this.store){
51052             this.store.un('beforeload', this.onBeforeLoad, this);
51053             this.store.un('load', this.onLoad, this);
51054             this.store.un('loadexception', this.onLoadException, this);
51055         }
51056         //Roo.form.ComboBox.superclass.onDestroy.call(this);
51057     },
51058
51059     // private
51060     fireKey : function(e){
51061         if(e.isNavKeyPress() && !this.list.isVisible()){
51062             this.fireEvent("specialkey", this, e);
51063         }
51064     },
51065
51066     // private
51067     onResize: function(w, h){
51068         
51069         return; 
51070     
51071         
51072     },
51073
51074     /**
51075      * Allow or prevent the user from directly editing the field text.  If false is passed,
51076      * the user will only be able to select from the items defined in the dropdown list.  This method
51077      * is the runtime equivalent of setting the 'editable' config option at config time.
51078      * @param {Boolean} value True to allow the user to directly edit the field text
51079      */
51080     setEditable : function(value){
51081          
51082     },
51083
51084     // private
51085     onBeforeLoad : function(){
51086         
51087         Roo.log("Select before load");
51088         return;
51089     
51090         this.innerList.update(this.loadingText ?
51091                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
51092         //this.restrictHeight();
51093         this.selectedIndex = -1;
51094     },
51095
51096     // private
51097     onLoad : function(){
51098
51099     
51100         var dom = this.el.dom;
51101         dom.innerHTML = '';
51102          var od = dom.ownerDocument;
51103          
51104         if (this.emptyText) {
51105             var op = od.createElement('option');
51106             op.setAttribute('value', '');
51107             op.innerHTML = String.format('{0}', this.emptyText);
51108             dom.appendChild(op);
51109         }
51110         if(this.store.getCount() > 0){
51111            
51112             var vf = this.valueField;
51113             var df = this.displayField;
51114             this.store.data.each(function(r) {
51115                 // which colmsn to use... testing - cdoe / title..
51116                 var op = od.createElement('option');
51117                 op.setAttribute('value', r.data[vf]);
51118                 op.innerHTML = String.format('{0}', r.data[df]);
51119                 dom.appendChild(op);
51120             });
51121             if (typeof(this.defaultValue != 'undefined')) {
51122                 this.setValue(this.defaultValue);
51123             }
51124             
51125              
51126         }else{
51127             //this.onEmptyResults();
51128         }
51129         //this.el.focus();
51130     },
51131     // private
51132     onLoadException : function()
51133     {
51134         dom.innerHTML = '';
51135             
51136         Roo.log("Select on load exception");
51137         return;
51138     
51139         this.collapse();
51140         Roo.log(this.store.reader.jsonData);
51141         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
51142             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
51143         }
51144         
51145         
51146     },
51147     // private
51148     onTypeAhead : function(){
51149          
51150     },
51151
51152     // private
51153     onSelect : function(record, index){
51154         Roo.log('on select?');
51155         return;
51156         if(this.fireEvent('beforeselect', this, record, index) !== false){
51157             this.setFromData(index > -1 ? record.data : false);
51158             this.collapse();
51159             this.fireEvent('select', this, record, index);
51160         }
51161     },
51162
51163     /**
51164      * Returns the currently selected field value or empty string if no value is set.
51165      * @return {String} value The selected value
51166      */
51167     getValue : function(){
51168         var dom = this.el.dom;
51169         this.value = dom.options[dom.selectedIndex].value;
51170         return this.value;
51171         
51172     },
51173
51174     /**
51175      * Clears any text/value currently set in the field
51176      */
51177     clearValue : function(){
51178         this.value = '';
51179         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
51180         
51181     },
51182
51183     /**
51184      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
51185      * will be displayed in the field.  If the value does not match the data value of an existing item,
51186      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
51187      * Otherwise the field will be blank (although the value will still be set).
51188      * @param {String} value The value to match
51189      */
51190     setValue : function(v){
51191         var d = this.el.dom;
51192         for (var i =0; i < d.options.length;i++) {
51193             if (v == d.options[i].value) {
51194                 d.selectedIndex = i;
51195                 this.value = v;
51196                 return;
51197             }
51198         }
51199         this.clearValue();
51200     },
51201     /**
51202      * @property {Object} the last set data for the element
51203      */
51204     
51205     lastData : false,
51206     /**
51207      * Sets the value of the field based on a object which is related to the record format for the store.
51208      * @param {Object} value the value to set as. or false on reset?
51209      */
51210     setFromData : function(o){
51211         Roo.log('setfrom data?');
51212          
51213         
51214         
51215     },
51216     // private
51217     reset : function(){
51218         this.clearValue();
51219     },
51220     // private
51221     findRecord : function(prop, value){
51222         
51223         return false;
51224     
51225         var record;
51226         if(this.store.getCount() > 0){
51227             this.store.each(function(r){
51228                 if(r.data[prop] == value){
51229                     record = r;
51230                     return false;
51231                 }
51232                 return true;
51233             });
51234         }
51235         return record;
51236     },
51237     
51238     getName: function()
51239     {
51240         // returns hidden if it's set..
51241         if (!this.rendered) {return ''};
51242         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
51243         
51244     },
51245      
51246
51247     
51248
51249     // private
51250     onEmptyResults : function(){
51251         Roo.log('empty results');
51252         //this.collapse();
51253     },
51254
51255     /**
51256      * Returns true if the dropdown list is expanded, else false.
51257      */
51258     isExpanded : function(){
51259         return false;
51260     },
51261
51262     /**
51263      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
51264      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
51265      * @param {String} value The data value of the item to select
51266      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
51267      * selected item if it is not currently in view (defaults to true)
51268      * @return {Boolean} True if the value matched an item in the list, else false
51269      */
51270     selectByValue : function(v, scrollIntoView){
51271         Roo.log('select By Value');
51272         return false;
51273     
51274         if(v !== undefined && v !== null){
51275             var r = this.findRecord(this.valueField || this.displayField, v);
51276             if(r){
51277                 this.select(this.store.indexOf(r), scrollIntoView);
51278                 return true;
51279             }
51280         }
51281         return false;
51282     },
51283
51284     /**
51285      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
51286      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
51287      * @param {Number} index The zero-based index of the list item to select
51288      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
51289      * selected item if it is not currently in view (defaults to true)
51290      */
51291     select : function(index, scrollIntoView){
51292         Roo.log('select ');
51293         return  ;
51294         
51295         this.selectedIndex = index;
51296         this.view.select(index);
51297         if(scrollIntoView !== false){
51298             var el = this.view.getNode(index);
51299             if(el){
51300                 this.innerList.scrollChildIntoView(el, false);
51301             }
51302         }
51303     },
51304
51305       
51306
51307     // private
51308     validateBlur : function(){
51309         
51310         return;
51311         
51312     },
51313
51314     // private
51315     initQuery : function(){
51316         this.doQuery(this.getRawValue());
51317     },
51318
51319     // private
51320     doForce : function(){
51321         if(this.el.dom.value.length > 0){
51322             this.el.dom.value =
51323                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
51324              
51325         }
51326     },
51327
51328     /**
51329      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
51330      * query allowing the query action to be canceled if needed.
51331      * @param {String} query The SQL query to execute
51332      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
51333      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
51334      * saved in the current store (defaults to false)
51335      */
51336     doQuery : function(q, forceAll){
51337         
51338         Roo.log('doQuery?');
51339         if(q === undefined || q === null){
51340             q = '';
51341         }
51342         var qe = {
51343             query: q,
51344             forceAll: forceAll,
51345             combo: this,
51346             cancel:false
51347         };
51348         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
51349             return false;
51350         }
51351         q = qe.query;
51352         forceAll = qe.forceAll;
51353         if(forceAll === true || (q.length >= this.minChars)){
51354             if(this.lastQuery != q || this.alwaysQuery){
51355                 this.lastQuery = q;
51356                 if(this.mode == 'local'){
51357                     this.selectedIndex = -1;
51358                     if(forceAll){
51359                         this.store.clearFilter();
51360                     }else{
51361                         this.store.filter(this.displayField, q);
51362                     }
51363                     this.onLoad();
51364                 }else{
51365                     this.store.baseParams[this.queryParam] = q;
51366                     this.store.load({
51367                         params: this.getParams(q)
51368                     });
51369                     this.expand();
51370                 }
51371             }else{
51372                 this.selectedIndex = -1;
51373                 this.onLoad();   
51374             }
51375         }
51376     },
51377
51378     // private
51379     getParams : function(q){
51380         var p = {};
51381         //p[this.queryParam] = q;
51382         if(this.pageSize){
51383             p.start = 0;
51384             p.limit = this.pageSize;
51385         }
51386         return p;
51387     },
51388
51389     /**
51390      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
51391      */
51392     collapse : function(){
51393         
51394     },
51395
51396     // private
51397     collapseIf : function(e){
51398         
51399     },
51400
51401     /**
51402      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
51403      */
51404     expand : function(){
51405         
51406     } ,
51407
51408     // private
51409      
51410
51411     /** 
51412     * @cfg {Boolean} grow 
51413     * @hide 
51414     */
51415     /** 
51416     * @cfg {Number} growMin 
51417     * @hide 
51418     */
51419     /** 
51420     * @cfg {Number} growMax 
51421     * @hide 
51422     */
51423     /**
51424      * @hide
51425      * @method autoSize
51426      */
51427     
51428     setWidth : function()
51429     {
51430         
51431     },
51432     getResizeEl : function(){
51433         return this.el;
51434     }
51435 });//<script type="text/javasscript">
51436  
51437
51438 /**
51439  * @class Roo.DDView
51440  * A DnD enabled version of Roo.View.
51441  * @param {Element/String} container The Element in which to create the View.
51442  * @param {String} tpl The template string used to create the markup for each element of the View
51443  * @param {Object} config The configuration properties. These include all the config options of
51444  * {@link Roo.View} plus some specific to this class.<br>
51445  * <p>
51446  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
51447  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
51448  * <p>
51449  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
51450 .x-view-drag-insert-above {
51451         border-top:1px dotted #3366cc;
51452 }
51453 .x-view-drag-insert-below {
51454         border-bottom:1px dotted #3366cc;
51455 }
51456 </code></pre>
51457  * 
51458  */
51459  
51460 Roo.DDView = function(container, tpl, config) {
51461     Roo.DDView.superclass.constructor.apply(this, arguments);
51462     this.getEl().setStyle("outline", "0px none");
51463     this.getEl().unselectable();
51464     if (this.dragGroup) {
51465                 this.setDraggable(this.dragGroup.split(","));
51466     }
51467     if (this.dropGroup) {
51468                 this.setDroppable(this.dropGroup.split(","));
51469     }
51470     if (this.deletable) {
51471         this.setDeletable();
51472     }
51473     this.isDirtyFlag = false;
51474         this.addEvents({
51475                 "drop" : true
51476         });
51477 };
51478
51479 Roo.extend(Roo.DDView, Roo.View, {
51480 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
51481 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
51482 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
51483 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
51484
51485         isFormField: true,
51486
51487         reset: Roo.emptyFn,
51488         
51489         clearInvalid: Roo.form.Field.prototype.clearInvalid,
51490
51491         validate: function() {
51492                 return true;
51493         },
51494         
51495         destroy: function() {
51496                 this.purgeListeners();
51497                 this.getEl.removeAllListeners();
51498                 this.getEl().remove();
51499                 if (this.dragZone) {
51500                         if (this.dragZone.destroy) {
51501                                 this.dragZone.destroy();
51502                         }
51503                 }
51504                 if (this.dropZone) {
51505                         if (this.dropZone.destroy) {
51506                                 this.dropZone.destroy();
51507                         }
51508                 }
51509         },
51510
51511 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
51512         getName: function() {
51513                 return this.name;
51514         },
51515
51516 /**     Loads the View from a JSON string representing the Records to put into the Store. */
51517         setValue: function(v) {
51518                 if (!this.store) {
51519                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
51520                 }
51521                 var data = {};
51522                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
51523                 this.store.proxy = new Roo.data.MemoryProxy(data);
51524                 this.store.load();
51525         },
51526
51527 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
51528         getValue: function() {
51529                 var result = '(';
51530                 this.store.each(function(rec) {
51531                         result += rec.id + ',';
51532                 });
51533                 return result.substr(0, result.length - 1) + ')';
51534         },
51535         
51536         getIds: function() {
51537                 var i = 0, result = new Array(this.store.getCount());
51538                 this.store.each(function(rec) {
51539                         result[i++] = rec.id;
51540                 });
51541                 return result;
51542         },
51543         
51544         isDirty: function() {
51545                 return this.isDirtyFlag;
51546         },
51547
51548 /**
51549  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
51550  *      whole Element becomes the target, and this causes the drop gesture to append.
51551  */
51552     getTargetFromEvent : function(e) {
51553                 var target = e.getTarget();
51554                 while ((target !== null) && (target.parentNode != this.el.dom)) {
51555                 target = target.parentNode;
51556                 }
51557                 if (!target) {
51558                         target = this.el.dom.lastChild || this.el.dom;
51559                 }
51560                 return target;
51561     },
51562
51563 /**
51564  *      Create the drag data which consists of an object which has the property "ddel" as
51565  *      the drag proxy element. 
51566  */
51567     getDragData : function(e) {
51568         var target = this.findItemFromChild(e.getTarget());
51569                 if(target) {
51570                         this.handleSelection(e);
51571                         var selNodes = this.getSelectedNodes();
51572             var dragData = {
51573                 source: this,
51574                 copy: this.copy || (this.allowCopy && e.ctrlKey),
51575                 nodes: selNodes,
51576                 records: []
51577                         };
51578                         var selectedIndices = this.getSelectedIndexes();
51579                         for (var i = 0; i < selectedIndices.length; i++) {
51580                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
51581                         }
51582                         if (selNodes.length == 1) {
51583                                 dragData.ddel = target.cloneNode(true); // the div element
51584                         } else {
51585                                 var div = document.createElement('div'); // create the multi element drag "ghost"
51586                                 div.className = 'multi-proxy';
51587                                 for (var i = 0, len = selNodes.length; i < len; i++) {
51588                                         div.appendChild(selNodes[i].cloneNode(true));
51589                                 }
51590                                 dragData.ddel = div;
51591                         }
51592             //console.log(dragData)
51593             //console.log(dragData.ddel.innerHTML)
51594                         return dragData;
51595                 }
51596         //console.log('nodragData')
51597                 return false;
51598     },
51599     
51600 /**     Specify to which ddGroup items in this DDView may be dragged. */
51601     setDraggable: function(ddGroup) {
51602         if (ddGroup instanceof Array) {
51603                 Roo.each(ddGroup, this.setDraggable, this);
51604                 return;
51605         }
51606         if (this.dragZone) {
51607                 this.dragZone.addToGroup(ddGroup);
51608         } else {
51609                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
51610                                 containerScroll: true,
51611                                 ddGroup: ddGroup 
51612
51613                         });
51614 //                      Draggability implies selection. DragZone's mousedown selects the element.
51615                         if (!this.multiSelect) { this.singleSelect = true; }
51616
51617 //                      Wire the DragZone's handlers up to methods in *this*
51618                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
51619                 }
51620     },
51621
51622 /**     Specify from which ddGroup this DDView accepts drops. */
51623     setDroppable: function(ddGroup) {
51624         if (ddGroup instanceof Array) {
51625                 Roo.each(ddGroup, this.setDroppable, this);
51626                 return;
51627         }
51628         if (this.dropZone) {
51629                 this.dropZone.addToGroup(ddGroup);
51630         } else {
51631                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
51632                                 containerScroll: true,
51633                                 ddGroup: ddGroup
51634                         });
51635
51636 //                      Wire the DropZone's handlers up to methods in *this*
51637                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
51638                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
51639                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
51640                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
51641                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
51642                 }
51643     },
51644
51645 /**     Decide whether to drop above or below a View node. */
51646     getDropPoint : function(e, n, dd){
51647         if (n == this.el.dom) { return "above"; }
51648                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
51649                 var c = t + (b - t) / 2;
51650                 var y = Roo.lib.Event.getPageY(e);
51651                 if(y <= c) {
51652                         return "above";
51653                 }else{
51654                         return "below";
51655                 }
51656     },
51657
51658     onNodeEnter : function(n, dd, e, data){
51659                 return false;
51660     },
51661     
51662     onNodeOver : function(n, dd, e, data){
51663                 var pt = this.getDropPoint(e, n, dd);
51664                 // set the insert point style on the target node
51665                 var dragElClass = this.dropNotAllowed;
51666                 if (pt) {
51667                         var targetElClass;
51668                         if (pt == "above"){
51669                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
51670                                 targetElClass = "x-view-drag-insert-above";
51671                         } else {
51672                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
51673                                 targetElClass = "x-view-drag-insert-below";
51674                         }
51675                         if (this.lastInsertClass != targetElClass){
51676                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
51677                                 this.lastInsertClass = targetElClass;
51678                         }
51679                 }
51680                 return dragElClass;
51681         },
51682
51683     onNodeOut : function(n, dd, e, data){
51684                 this.removeDropIndicators(n);
51685     },
51686
51687     onNodeDrop : function(n, dd, e, data){
51688         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
51689                 return false;
51690         }
51691         var pt = this.getDropPoint(e, n, dd);
51692                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
51693                 if (pt == "below") { insertAt++; }
51694                 for (var i = 0; i < data.records.length; i++) {
51695                         var r = data.records[i];
51696                         var dup = this.store.getById(r.id);
51697                         if (dup && (dd != this.dragZone)) {
51698                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
51699                         } else {
51700                                 if (data.copy) {
51701                                         this.store.insert(insertAt++, r.copy());
51702                                 } else {
51703                                         data.source.isDirtyFlag = true;
51704                                         r.store.remove(r);
51705                                         this.store.insert(insertAt++, r);
51706                                 }
51707                                 this.isDirtyFlag = true;
51708                         }
51709                 }
51710                 this.dragZone.cachedTarget = null;
51711                 return true;
51712     },
51713
51714     removeDropIndicators : function(n){
51715                 if(n){
51716                         Roo.fly(n).removeClass([
51717                                 "x-view-drag-insert-above",
51718                                 "x-view-drag-insert-below"]);
51719                         this.lastInsertClass = "_noclass";
51720                 }
51721     },
51722
51723 /**
51724  *      Utility method. Add a delete option to the DDView's context menu.
51725  *      @param {String} imageUrl The URL of the "delete" icon image.
51726  */
51727         setDeletable: function(imageUrl) {
51728                 if (!this.singleSelect && !this.multiSelect) {
51729                         this.singleSelect = true;
51730                 }
51731                 var c = this.getContextMenu();
51732                 this.contextMenu.on("itemclick", function(item) {
51733                         switch (item.id) {
51734                                 case "delete":
51735                                         this.remove(this.getSelectedIndexes());
51736                                         break;
51737                         }
51738                 }, this);
51739                 this.contextMenu.add({
51740                         icon: imageUrl,
51741                         id: "delete",
51742                         text: 'Delete'
51743                 });
51744         },
51745         
51746 /**     Return the context menu for this DDView. */
51747         getContextMenu: function() {
51748                 if (!this.contextMenu) {
51749 //                      Create the View's context menu
51750                         this.contextMenu = new Roo.menu.Menu({
51751                                 id: this.id + "-contextmenu"
51752                         });
51753                         this.el.on("contextmenu", this.showContextMenu, this);
51754                 }
51755                 return this.contextMenu;
51756         },
51757         
51758         disableContextMenu: function() {
51759                 if (this.contextMenu) {
51760                         this.el.un("contextmenu", this.showContextMenu, this);
51761                 }
51762         },
51763
51764         showContextMenu: function(e, item) {
51765         item = this.findItemFromChild(e.getTarget());
51766                 if (item) {
51767                         e.stopEvent();
51768                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
51769                         this.contextMenu.showAt(e.getXY());
51770             }
51771     },
51772
51773 /**
51774  *      Remove {@link Roo.data.Record}s at the specified indices.
51775  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
51776  */
51777     remove: function(selectedIndices) {
51778                 selectedIndices = [].concat(selectedIndices);
51779                 for (var i = 0; i < selectedIndices.length; i++) {
51780                         var rec = this.store.getAt(selectedIndices[i]);
51781                         this.store.remove(rec);
51782                 }
51783     },
51784
51785 /**
51786  *      Double click fires the event, but also, if this is draggable, and there is only one other
51787  *      related DropZone, it transfers the selected node.
51788  */
51789     onDblClick : function(e){
51790         var item = this.findItemFromChild(e.getTarget());
51791         if(item){
51792             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
51793                 return false;
51794             }
51795             if (this.dragGroup) {
51796                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
51797                     while (targets.indexOf(this.dropZone) > -1) {
51798                             targets.remove(this.dropZone);
51799                                 }
51800                     if (targets.length == 1) {
51801                                         this.dragZone.cachedTarget = null;
51802                         var el = Roo.get(targets[0].getEl());
51803                         var box = el.getBox(true);
51804                         targets[0].onNodeDrop(el.dom, {
51805                                 target: el.dom,
51806                                 xy: [box.x, box.y + box.height - 1]
51807                         }, null, this.getDragData(e));
51808                     }
51809                 }
51810         }
51811     },
51812     
51813     handleSelection: function(e) {
51814                 this.dragZone.cachedTarget = null;
51815         var item = this.findItemFromChild(e.getTarget());
51816         if (!item) {
51817                 this.clearSelections(true);
51818                 return;
51819         }
51820                 if (item && (this.multiSelect || this.singleSelect)){
51821                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
51822                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
51823                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
51824                                 this.unselect(item);
51825                         } else {
51826                                 this.select(item, this.multiSelect && e.ctrlKey);
51827                                 this.lastSelection = item;
51828                         }
51829                 }
51830     },
51831
51832     onItemClick : function(item, index, e){
51833                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
51834                         return false;
51835                 }
51836                 return true;
51837     },
51838
51839     unselect : function(nodeInfo, suppressEvent){
51840                 var node = this.getNode(nodeInfo);
51841                 if(node && this.isSelected(node)){
51842                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
51843                                 Roo.fly(node).removeClass(this.selectedClass);
51844                                 this.selections.remove(node);
51845                                 if(!suppressEvent){
51846                                         this.fireEvent("selectionchange", this, this.selections);
51847                                 }
51848                         }
51849                 }
51850     }
51851 });
51852 /*
51853  * Based on:
51854  * Ext JS Library 1.1.1
51855  * Copyright(c) 2006-2007, Ext JS, LLC.
51856  *
51857  * Originally Released Under LGPL - original licence link has changed is not relivant.
51858  *
51859  * Fork - LGPL
51860  * <script type="text/javascript">
51861  */
51862  
51863 /**
51864  * @class Roo.LayoutManager
51865  * @extends Roo.util.Observable
51866  * Base class for layout managers.
51867  */
51868 Roo.LayoutManager = function(container, config){
51869     Roo.LayoutManager.superclass.constructor.call(this);
51870     this.el = Roo.get(container);
51871     // ie scrollbar fix
51872     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
51873         document.body.scroll = "no";
51874     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
51875         this.el.position('relative');
51876     }
51877     this.id = this.el.id;
51878     this.el.addClass("x-layout-container");
51879     /** false to disable window resize monitoring @type Boolean */
51880     this.monitorWindowResize = true;
51881     this.regions = {};
51882     this.addEvents({
51883         /**
51884          * @event layout
51885          * Fires when a layout is performed. 
51886          * @param {Roo.LayoutManager} this
51887          */
51888         "layout" : true,
51889         /**
51890          * @event regionresized
51891          * Fires when the user resizes a region. 
51892          * @param {Roo.LayoutRegion} region The resized region
51893          * @param {Number} newSize The new size (width for east/west, height for north/south)
51894          */
51895         "regionresized" : true,
51896         /**
51897          * @event regioncollapsed
51898          * Fires when a region is collapsed. 
51899          * @param {Roo.LayoutRegion} region The collapsed region
51900          */
51901         "regioncollapsed" : true,
51902         /**
51903          * @event regionexpanded
51904          * Fires when a region is expanded.  
51905          * @param {Roo.LayoutRegion} region The expanded region
51906          */
51907         "regionexpanded" : true
51908     });
51909     this.updating = false;
51910     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
51911 };
51912
51913 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
51914     /**
51915      * Returns true if this layout is currently being updated
51916      * @return {Boolean}
51917      */
51918     isUpdating : function(){
51919         return this.updating; 
51920     },
51921     
51922     /**
51923      * Suspend the LayoutManager from doing auto-layouts while
51924      * making multiple add or remove calls
51925      */
51926     beginUpdate : function(){
51927         this.updating = true;    
51928     },
51929     
51930     /**
51931      * Restore auto-layouts and optionally disable the manager from performing a layout
51932      * @param {Boolean} noLayout true to disable a layout update 
51933      */
51934     endUpdate : function(noLayout){
51935         this.updating = false;
51936         if(!noLayout){
51937             this.layout();
51938         }    
51939     },
51940     
51941     layout: function(){
51942         
51943     },
51944     
51945     onRegionResized : function(region, newSize){
51946         this.fireEvent("regionresized", region, newSize);
51947         this.layout();
51948     },
51949     
51950     onRegionCollapsed : function(region){
51951         this.fireEvent("regioncollapsed", region);
51952     },
51953     
51954     onRegionExpanded : function(region){
51955         this.fireEvent("regionexpanded", region);
51956     },
51957         
51958     /**
51959      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
51960      * performs box-model adjustments.
51961      * @return {Object} The size as an object {width: (the width), height: (the height)}
51962      */
51963     getViewSize : function(){
51964         var size;
51965         if(this.el.dom != document.body){
51966             size = this.el.getSize();
51967         }else{
51968             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
51969         }
51970         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
51971         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
51972         return size;
51973     },
51974     
51975     /**
51976      * Returns the Element this layout is bound to.
51977      * @return {Roo.Element}
51978      */
51979     getEl : function(){
51980         return this.el;
51981     },
51982     
51983     /**
51984      * Returns the specified region.
51985      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
51986      * @return {Roo.LayoutRegion}
51987      */
51988     getRegion : function(target){
51989         return this.regions[target.toLowerCase()];
51990     },
51991     
51992     onWindowResize : function(){
51993         if(this.monitorWindowResize){
51994             this.layout();
51995         }
51996     }
51997 });/*
51998  * Based on:
51999  * Ext JS Library 1.1.1
52000  * Copyright(c) 2006-2007, Ext JS, LLC.
52001  *
52002  * Originally Released Under LGPL - original licence link has changed is not relivant.
52003  *
52004  * Fork - LGPL
52005  * <script type="text/javascript">
52006  */
52007 /**
52008  * @class Roo.BorderLayout
52009  * @extends Roo.LayoutManager
52010  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
52011  * please see: <br><br>
52012  * <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>
52013  * <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>
52014  * Example:
52015  <pre><code>
52016  var layout = new Roo.BorderLayout(document.body, {
52017     north: {
52018         initialSize: 25,
52019         titlebar: false
52020     },
52021     west: {
52022         split:true,
52023         initialSize: 200,
52024         minSize: 175,
52025         maxSize: 400,
52026         titlebar: true,
52027         collapsible: true
52028     },
52029     east: {
52030         split:true,
52031         initialSize: 202,
52032         minSize: 175,
52033         maxSize: 400,
52034         titlebar: true,
52035         collapsible: true
52036     },
52037     south: {
52038         split:true,
52039         initialSize: 100,
52040         minSize: 100,
52041         maxSize: 200,
52042         titlebar: true,
52043         collapsible: true
52044     },
52045     center: {
52046         titlebar: true,
52047         autoScroll:true,
52048         resizeTabs: true,
52049         minTabWidth: 50,
52050         preferredTabWidth: 150
52051     }
52052 });
52053
52054 // shorthand
52055 var CP = Roo.ContentPanel;
52056
52057 layout.beginUpdate();
52058 layout.add("north", new CP("north", "North"));
52059 layout.add("south", new CP("south", {title: "South", closable: true}));
52060 layout.add("west", new CP("west", {title: "West"}));
52061 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
52062 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
52063 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
52064 layout.getRegion("center").showPanel("center1");
52065 layout.endUpdate();
52066 </code></pre>
52067
52068 <b>The container the layout is rendered into can be either the body element or any other element.
52069 If it is not the body element, the container needs to either be an absolute positioned element,
52070 or you will need to add "position:relative" to the css of the container.  You will also need to specify
52071 the container size if it is not the body element.</b>
52072
52073 * @constructor
52074 * Create a new BorderLayout
52075 * @param {String/HTMLElement/Element} container The container this layout is bound to
52076 * @param {Object} config Configuration options
52077  */
52078 Roo.BorderLayout = function(container, config){
52079     config = config || {};
52080     Roo.BorderLayout.superclass.constructor.call(this, container, config);
52081     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
52082     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
52083         var target = this.factory.validRegions[i];
52084         if(config[target]){
52085             this.addRegion(target, config[target]);
52086         }
52087     }
52088 };
52089
52090 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
52091     /**
52092      * Creates and adds a new region if it doesn't already exist.
52093      * @param {String} target The target region key (north, south, east, west or center).
52094      * @param {Object} config The regions config object
52095      * @return {BorderLayoutRegion} The new region
52096      */
52097     addRegion : function(target, config){
52098         if(!this.regions[target]){
52099             var r = this.factory.create(target, this, config);
52100             this.bindRegion(target, r);
52101         }
52102         return this.regions[target];
52103     },
52104
52105     // private (kinda)
52106     bindRegion : function(name, r){
52107         this.regions[name] = r;
52108         r.on("visibilitychange", this.layout, this);
52109         r.on("paneladded", this.layout, this);
52110         r.on("panelremoved", this.layout, this);
52111         r.on("invalidated", this.layout, this);
52112         r.on("resized", this.onRegionResized, this);
52113         r.on("collapsed", this.onRegionCollapsed, this);
52114         r.on("expanded", this.onRegionExpanded, this);
52115     },
52116
52117     /**
52118      * Performs a layout update.
52119      */
52120     layout : function(){
52121         if(this.updating) {
52122             return;
52123         }
52124         var size = this.getViewSize();
52125         var w = size.width;
52126         var h = size.height;
52127         var centerW = w;
52128         var centerH = h;
52129         var centerY = 0;
52130         var centerX = 0;
52131         //var x = 0, y = 0;
52132
52133         var rs = this.regions;
52134         var north = rs["north"];
52135         var south = rs["south"]; 
52136         var west = rs["west"];
52137         var east = rs["east"];
52138         var center = rs["center"];
52139         //if(this.hideOnLayout){ // not supported anymore
52140             //c.el.setStyle("display", "none");
52141         //}
52142         if(north && north.isVisible()){
52143             var b = north.getBox();
52144             var m = north.getMargins();
52145             b.width = w - (m.left+m.right);
52146             b.x = m.left;
52147             b.y = m.top;
52148             centerY = b.height + b.y + m.bottom;
52149             centerH -= centerY;
52150             north.updateBox(this.safeBox(b));
52151         }
52152         if(south && south.isVisible()){
52153             var b = south.getBox();
52154             var m = south.getMargins();
52155             b.width = w - (m.left+m.right);
52156             b.x = m.left;
52157             var totalHeight = (b.height + m.top + m.bottom);
52158             b.y = h - totalHeight + m.top;
52159             centerH -= totalHeight;
52160             south.updateBox(this.safeBox(b));
52161         }
52162         if(west && west.isVisible()){
52163             var b = west.getBox();
52164             var m = west.getMargins();
52165             b.height = centerH - (m.top+m.bottom);
52166             b.x = m.left;
52167             b.y = centerY + m.top;
52168             var totalWidth = (b.width + m.left + m.right);
52169             centerX += totalWidth;
52170             centerW -= totalWidth;
52171             west.updateBox(this.safeBox(b));
52172         }
52173         if(east && east.isVisible()){
52174             var b = east.getBox();
52175             var m = east.getMargins();
52176             b.height = centerH - (m.top+m.bottom);
52177             var totalWidth = (b.width + m.left + m.right);
52178             b.x = w - totalWidth + m.left;
52179             b.y = centerY + m.top;
52180             centerW -= totalWidth;
52181             east.updateBox(this.safeBox(b));
52182         }
52183         if(center){
52184             var m = center.getMargins();
52185             var centerBox = {
52186                 x: centerX + m.left,
52187                 y: centerY + m.top,
52188                 width: centerW - (m.left+m.right),
52189                 height: centerH - (m.top+m.bottom)
52190             };
52191             //if(this.hideOnLayout){
52192                 //center.el.setStyle("display", "block");
52193             //}
52194             center.updateBox(this.safeBox(centerBox));
52195         }
52196         this.el.repaint();
52197         this.fireEvent("layout", this);
52198     },
52199
52200     // private
52201     safeBox : function(box){
52202         box.width = Math.max(0, box.width);
52203         box.height = Math.max(0, box.height);
52204         return box;
52205     },
52206
52207     /**
52208      * Adds a ContentPanel (or subclass) to this layout.
52209      * @param {String} target The target region key (north, south, east, west or center).
52210      * @param {Roo.ContentPanel} panel The panel to add
52211      * @return {Roo.ContentPanel} The added panel
52212      */
52213     add : function(target, panel){
52214          
52215         target = target.toLowerCase();
52216         return this.regions[target].add(panel);
52217     },
52218
52219     /**
52220      * Remove a ContentPanel (or subclass) to this layout.
52221      * @param {String} target The target region key (north, south, east, west or center).
52222      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
52223      * @return {Roo.ContentPanel} The removed panel
52224      */
52225     remove : function(target, panel){
52226         target = target.toLowerCase();
52227         return this.regions[target].remove(panel);
52228     },
52229
52230     /**
52231      * Searches all regions for a panel with the specified id
52232      * @param {String} panelId
52233      * @return {Roo.ContentPanel} The panel or null if it wasn't found
52234      */
52235     findPanel : function(panelId){
52236         var rs = this.regions;
52237         for(var target in rs){
52238             if(typeof rs[target] != "function"){
52239                 var p = rs[target].getPanel(panelId);
52240                 if(p){
52241                     return p;
52242                 }
52243             }
52244         }
52245         return null;
52246     },
52247
52248     /**
52249      * Searches all regions for a panel with the specified id and activates (shows) it.
52250      * @param {String/ContentPanel} panelId The panels id or the panel itself
52251      * @return {Roo.ContentPanel} The shown panel or null
52252      */
52253     showPanel : function(panelId) {
52254       var rs = this.regions;
52255       for(var target in rs){
52256          var r = rs[target];
52257          if(typeof r != "function"){
52258             if(r.hasPanel(panelId)){
52259                return r.showPanel(panelId);
52260             }
52261          }
52262       }
52263       return null;
52264    },
52265
52266    /**
52267      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
52268      * @param {Roo.state.Provider} provider (optional) An alternate state provider
52269      */
52270     restoreState : function(provider){
52271         if(!provider){
52272             provider = Roo.state.Manager;
52273         }
52274         var sm = new Roo.LayoutStateManager();
52275         sm.init(this, provider);
52276     },
52277
52278     /**
52279      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
52280      * object should contain properties for each region to add ContentPanels to, and each property's value should be
52281      * a valid ContentPanel config object.  Example:
52282      * <pre><code>
52283 // Create the main layout
52284 var layout = new Roo.BorderLayout('main-ct', {
52285     west: {
52286         split:true,
52287         minSize: 175,
52288         titlebar: true
52289     },
52290     center: {
52291         title:'Components'
52292     }
52293 }, 'main-ct');
52294
52295 // Create and add multiple ContentPanels at once via configs
52296 layout.batchAdd({
52297    west: {
52298        id: 'source-files',
52299        autoCreate:true,
52300        title:'Ext Source Files',
52301        autoScroll:true,
52302        fitToFrame:true
52303    },
52304    center : {
52305        el: cview,
52306        autoScroll:true,
52307        fitToFrame:true,
52308        toolbar: tb,
52309        resizeEl:'cbody'
52310    }
52311 });
52312 </code></pre>
52313      * @param {Object} regions An object containing ContentPanel configs by region name
52314      */
52315     batchAdd : function(regions){
52316         this.beginUpdate();
52317         for(var rname in regions){
52318             var lr = this.regions[rname];
52319             if(lr){
52320                 this.addTypedPanels(lr, regions[rname]);
52321             }
52322         }
52323         this.endUpdate();
52324     },
52325
52326     // private
52327     addTypedPanels : function(lr, ps){
52328         if(typeof ps == 'string'){
52329             lr.add(new Roo.ContentPanel(ps));
52330         }
52331         else if(ps instanceof Array){
52332             for(var i =0, len = ps.length; i < len; i++){
52333                 this.addTypedPanels(lr, ps[i]);
52334             }
52335         }
52336         else if(!ps.events){ // raw config?
52337             var el = ps.el;
52338             delete ps.el; // prevent conflict
52339             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
52340         }
52341         else {  // panel object assumed!
52342             lr.add(ps);
52343         }
52344     },
52345     /**
52346      * Adds a xtype elements to the layout.
52347      * <pre><code>
52348
52349 layout.addxtype({
52350        xtype : 'ContentPanel',
52351        region: 'west',
52352        items: [ .... ]
52353    }
52354 );
52355
52356 layout.addxtype({
52357         xtype : 'NestedLayoutPanel',
52358         region: 'west',
52359         layout: {
52360            center: { },
52361            west: { }   
52362         },
52363         items : [ ... list of content panels or nested layout panels.. ]
52364    }
52365 );
52366 </code></pre>
52367      * @param {Object} cfg Xtype definition of item to add.
52368      */
52369     addxtype : function(cfg)
52370     {
52371         // basically accepts a pannel...
52372         // can accept a layout region..!?!?
52373         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
52374         
52375         if (!cfg.xtype.match(/Panel$/)) {
52376             return false;
52377         }
52378         var ret = false;
52379         
52380         if (typeof(cfg.region) == 'undefined') {
52381             Roo.log("Failed to add Panel, region was not set");
52382             Roo.log(cfg);
52383             return false;
52384         }
52385         var region = cfg.region;
52386         delete cfg.region;
52387         
52388           
52389         var xitems = [];
52390         if (cfg.items) {
52391             xitems = cfg.items;
52392             delete cfg.items;
52393         }
52394         var nb = false;
52395         
52396         switch(cfg.xtype) 
52397         {
52398             case 'ContentPanel':  // ContentPanel (el, cfg)
52399             case 'ScrollPanel':  // ContentPanel (el, cfg)
52400             case 'ViewPanel': 
52401                 if(cfg.autoCreate) {
52402                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
52403                 } else {
52404                     var el = this.el.createChild();
52405                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
52406                 }
52407                 
52408                 this.add(region, ret);
52409                 break;
52410             
52411             
52412             case 'TreePanel': // our new panel!
52413                 cfg.el = this.el.createChild();
52414                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
52415                 this.add(region, ret);
52416                 break;
52417             
52418             case 'NestedLayoutPanel': 
52419                 // create a new Layout (which is  a Border Layout...
52420                 var el = this.el.createChild();
52421                 var clayout = cfg.layout;
52422                 delete cfg.layout;
52423                 clayout.items   = clayout.items  || [];
52424                 // replace this exitems with the clayout ones..
52425                 xitems = clayout.items;
52426                  
52427                 
52428                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
52429                     cfg.background = false;
52430                 }
52431                 var layout = new Roo.BorderLayout(el, clayout);
52432                 
52433                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
52434                 //console.log('adding nested layout panel '  + cfg.toSource());
52435                 this.add(region, ret);
52436                 nb = {}; /// find first...
52437                 break;
52438                 
52439             case 'GridPanel': 
52440             
52441                 // needs grid and region
52442                 
52443                 //var el = this.getRegion(region).el.createChild();
52444                 var el = this.el.createChild();
52445                 // create the grid first...
52446                 
52447                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
52448                 delete cfg.grid;
52449                 if (region == 'center' && this.active ) {
52450                     cfg.background = false;
52451                 }
52452                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
52453                 
52454                 this.add(region, ret);
52455                 if (cfg.background) {
52456                     ret.on('activate', function(gp) {
52457                         if (!gp.grid.rendered) {
52458                             gp.grid.render();
52459                         }
52460                     });
52461                 } else {
52462                     grid.render();
52463                 }
52464                 break;
52465            
52466            
52467            
52468                 
52469                 
52470                 
52471             default:
52472                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
52473                     
52474                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
52475                     this.add(region, ret);
52476                 } else {
52477                 
52478                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
52479                     return null;
52480                 }
52481                 
52482              // GridPanel (grid, cfg)
52483             
52484         }
52485         this.beginUpdate();
52486         // add children..
52487         var region = '';
52488         var abn = {};
52489         Roo.each(xitems, function(i)  {
52490             region = nb && i.region ? i.region : false;
52491             
52492             var add = ret.addxtype(i);
52493            
52494             if (region) {
52495                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
52496                 if (!i.background) {
52497                     abn[region] = nb[region] ;
52498                 }
52499             }
52500             
52501         });
52502         this.endUpdate();
52503
52504         // make the last non-background panel active..
52505         //if (nb) { Roo.log(abn); }
52506         if (nb) {
52507             
52508             for(var r in abn) {
52509                 region = this.getRegion(r);
52510                 if (region) {
52511                     // tried using nb[r], but it does not work..
52512                      
52513                     region.showPanel(abn[r]);
52514                    
52515                 }
52516             }
52517         }
52518         return ret;
52519         
52520     }
52521 });
52522
52523 /**
52524  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
52525  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
52526  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
52527  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
52528  * <pre><code>
52529 // shorthand
52530 var CP = Roo.ContentPanel;
52531
52532 var layout = Roo.BorderLayout.create({
52533     north: {
52534         initialSize: 25,
52535         titlebar: false,
52536         panels: [new CP("north", "North")]
52537     },
52538     west: {
52539         split:true,
52540         initialSize: 200,
52541         minSize: 175,
52542         maxSize: 400,
52543         titlebar: true,
52544         collapsible: true,
52545         panels: [new CP("west", {title: "West"})]
52546     },
52547     east: {
52548         split:true,
52549         initialSize: 202,
52550         minSize: 175,
52551         maxSize: 400,
52552         titlebar: true,
52553         collapsible: true,
52554         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
52555     },
52556     south: {
52557         split:true,
52558         initialSize: 100,
52559         minSize: 100,
52560         maxSize: 200,
52561         titlebar: true,
52562         collapsible: true,
52563         panels: [new CP("south", {title: "South", closable: true})]
52564     },
52565     center: {
52566         titlebar: true,
52567         autoScroll:true,
52568         resizeTabs: true,
52569         minTabWidth: 50,
52570         preferredTabWidth: 150,
52571         panels: [
52572             new CP("center1", {title: "Close Me", closable: true}),
52573             new CP("center2", {title: "Center Panel", closable: false})
52574         ]
52575     }
52576 }, document.body);
52577
52578 layout.getRegion("center").showPanel("center1");
52579 </code></pre>
52580  * @param config
52581  * @param targetEl
52582  */
52583 Roo.BorderLayout.create = function(config, targetEl){
52584     var layout = new Roo.BorderLayout(targetEl || document.body, config);
52585     layout.beginUpdate();
52586     var regions = Roo.BorderLayout.RegionFactory.validRegions;
52587     for(var j = 0, jlen = regions.length; j < jlen; j++){
52588         var lr = regions[j];
52589         if(layout.regions[lr] && config[lr].panels){
52590             var r = layout.regions[lr];
52591             var ps = config[lr].panels;
52592             layout.addTypedPanels(r, ps);
52593         }
52594     }
52595     layout.endUpdate();
52596     return layout;
52597 };
52598
52599 // private
52600 Roo.BorderLayout.RegionFactory = {
52601     // private
52602     validRegions : ["north","south","east","west","center"],
52603
52604     // private
52605     create : function(target, mgr, config){
52606         target = target.toLowerCase();
52607         if(config.lightweight || config.basic){
52608             return new Roo.BasicLayoutRegion(mgr, config, target);
52609         }
52610         switch(target){
52611             case "north":
52612                 return new Roo.NorthLayoutRegion(mgr, config);
52613             case "south":
52614                 return new Roo.SouthLayoutRegion(mgr, config);
52615             case "east":
52616                 return new Roo.EastLayoutRegion(mgr, config);
52617             case "west":
52618                 return new Roo.WestLayoutRegion(mgr, config);
52619             case "center":
52620                 return new Roo.CenterLayoutRegion(mgr, config);
52621         }
52622         throw 'Layout region "'+target+'" not supported.';
52623     }
52624 };/*
52625  * Based on:
52626  * Ext JS Library 1.1.1
52627  * Copyright(c) 2006-2007, Ext JS, LLC.
52628  *
52629  * Originally Released Under LGPL - original licence link has changed is not relivant.
52630  *
52631  * Fork - LGPL
52632  * <script type="text/javascript">
52633  */
52634  
52635 /**
52636  * @class Roo.BasicLayoutRegion
52637  * @extends Roo.util.Observable
52638  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
52639  * and does not have a titlebar, tabs or any other features. All it does is size and position 
52640  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
52641  */
52642 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
52643     this.mgr = mgr;
52644     this.position  = pos;
52645     this.events = {
52646         /**
52647          * @scope Roo.BasicLayoutRegion
52648          */
52649         
52650         /**
52651          * @event beforeremove
52652          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
52653          * @param {Roo.LayoutRegion} this
52654          * @param {Roo.ContentPanel} panel The panel
52655          * @param {Object} e The cancel event object
52656          */
52657         "beforeremove" : true,
52658         /**
52659          * @event invalidated
52660          * Fires when the layout for this region is changed.
52661          * @param {Roo.LayoutRegion} this
52662          */
52663         "invalidated" : true,
52664         /**
52665          * @event visibilitychange
52666          * Fires when this region is shown or hidden 
52667          * @param {Roo.LayoutRegion} this
52668          * @param {Boolean} visibility true or false
52669          */
52670         "visibilitychange" : true,
52671         /**
52672          * @event paneladded
52673          * Fires when a panel is added. 
52674          * @param {Roo.LayoutRegion} this
52675          * @param {Roo.ContentPanel} panel The panel
52676          */
52677         "paneladded" : true,
52678         /**
52679          * @event panelremoved
52680          * Fires when a panel is removed. 
52681          * @param {Roo.LayoutRegion} this
52682          * @param {Roo.ContentPanel} panel The panel
52683          */
52684         "panelremoved" : true,
52685         /**
52686          * @event beforecollapse
52687          * Fires when this region before collapse.
52688          * @param {Roo.LayoutRegion} this
52689          */
52690         "beforecollapse" : true,
52691         /**
52692          * @event collapsed
52693          * Fires when this region is collapsed.
52694          * @param {Roo.LayoutRegion} this
52695          */
52696         "collapsed" : true,
52697         /**
52698          * @event expanded
52699          * Fires when this region is expanded.
52700          * @param {Roo.LayoutRegion} this
52701          */
52702         "expanded" : true,
52703         /**
52704          * @event slideshow
52705          * Fires when this region is slid into view.
52706          * @param {Roo.LayoutRegion} this
52707          */
52708         "slideshow" : true,
52709         /**
52710          * @event slidehide
52711          * Fires when this region slides out of view. 
52712          * @param {Roo.LayoutRegion} this
52713          */
52714         "slidehide" : true,
52715         /**
52716          * @event panelactivated
52717          * Fires when a panel is activated. 
52718          * @param {Roo.LayoutRegion} this
52719          * @param {Roo.ContentPanel} panel The activated panel
52720          */
52721         "panelactivated" : true,
52722         /**
52723          * @event resized
52724          * Fires when the user resizes this region. 
52725          * @param {Roo.LayoutRegion} this
52726          * @param {Number} newSize The new size (width for east/west, height for north/south)
52727          */
52728         "resized" : true
52729     };
52730     /** A collection of panels in this region. @type Roo.util.MixedCollection */
52731     this.panels = new Roo.util.MixedCollection();
52732     this.panels.getKey = this.getPanelId.createDelegate(this);
52733     this.box = null;
52734     this.activePanel = null;
52735     // ensure listeners are added...
52736     
52737     if (config.listeners || config.events) {
52738         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
52739             listeners : config.listeners || {},
52740             events : config.events || {}
52741         });
52742     }
52743     
52744     if(skipConfig !== true){
52745         this.applyConfig(config);
52746     }
52747 };
52748
52749 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
52750     getPanelId : function(p){
52751         return p.getId();
52752     },
52753     
52754     applyConfig : function(config){
52755         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
52756         this.config = config;
52757         
52758     },
52759     
52760     /**
52761      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
52762      * the width, for horizontal (north, south) the height.
52763      * @param {Number} newSize The new width or height
52764      */
52765     resizeTo : function(newSize){
52766         var el = this.el ? this.el :
52767                  (this.activePanel ? this.activePanel.getEl() : null);
52768         if(el){
52769             switch(this.position){
52770                 case "east":
52771                 case "west":
52772                     el.setWidth(newSize);
52773                     this.fireEvent("resized", this, newSize);
52774                 break;
52775                 case "north":
52776                 case "south":
52777                     el.setHeight(newSize);
52778                     this.fireEvent("resized", this, newSize);
52779                 break;                
52780             }
52781         }
52782     },
52783     
52784     getBox : function(){
52785         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
52786     },
52787     
52788     getMargins : function(){
52789         return this.margins;
52790     },
52791     
52792     updateBox : function(box){
52793         this.box = box;
52794         var el = this.activePanel.getEl();
52795         el.dom.style.left = box.x + "px";
52796         el.dom.style.top = box.y + "px";
52797         this.activePanel.setSize(box.width, box.height);
52798     },
52799     
52800     /**
52801      * Returns the container element for this region.
52802      * @return {Roo.Element}
52803      */
52804     getEl : function(){
52805         return this.activePanel;
52806     },
52807     
52808     /**
52809      * Returns true if this region is currently visible.
52810      * @return {Boolean}
52811      */
52812     isVisible : function(){
52813         return this.activePanel ? true : false;
52814     },
52815     
52816     setActivePanel : function(panel){
52817         panel = this.getPanel(panel);
52818         if(this.activePanel && this.activePanel != panel){
52819             this.activePanel.setActiveState(false);
52820             this.activePanel.getEl().setLeftTop(-10000,-10000);
52821         }
52822         this.activePanel = panel;
52823         panel.setActiveState(true);
52824         if(this.box){
52825             panel.setSize(this.box.width, this.box.height);
52826         }
52827         this.fireEvent("panelactivated", this, panel);
52828         this.fireEvent("invalidated");
52829     },
52830     
52831     /**
52832      * Show the specified panel.
52833      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
52834      * @return {Roo.ContentPanel} The shown panel or null
52835      */
52836     showPanel : function(panel){
52837         if(panel = this.getPanel(panel)){
52838             this.setActivePanel(panel);
52839         }
52840         return panel;
52841     },
52842     
52843     /**
52844      * Get the active panel for this region.
52845      * @return {Roo.ContentPanel} The active panel or null
52846      */
52847     getActivePanel : function(){
52848         return this.activePanel;
52849     },
52850     
52851     /**
52852      * Add the passed ContentPanel(s)
52853      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
52854      * @return {Roo.ContentPanel} The panel added (if only one was added)
52855      */
52856     add : function(panel){
52857         if(arguments.length > 1){
52858             for(var i = 0, len = arguments.length; i < len; i++) {
52859                 this.add(arguments[i]);
52860             }
52861             return null;
52862         }
52863         if(this.hasPanel(panel)){
52864             this.showPanel(panel);
52865             return panel;
52866         }
52867         var el = panel.getEl();
52868         if(el.dom.parentNode != this.mgr.el.dom){
52869             this.mgr.el.dom.appendChild(el.dom);
52870         }
52871         if(panel.setRegion){
52872             panel.setRegion(this);
52873         }
52874         this.panels.add(panel);
52875         el.setStyle("position", "absolute");
52876         if(!panel.background){
52877             this.setActivePanel(panel);
52878             if(this.config.initialSize && this.panels.getCount()==1){
52879                 this.resizeTo(this.config.initialSize);
52880             }
52881         }
52882         this.fireEvent("paneladded", this, panel);
52883         return panel;
52884     },
52885     
52886     /**
52887      * Returns true if the panel is in this region.
52888      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
52889      * @return {Boolean}
52890      */
52891     hasPanel : function(panel){
52892         if(typeof panel == "object"){ // must be panel obj
52893             panel = panel.getId();
52894         }
52895         return this.getPanel(panel) ? true : false;
52896     },
52897     
52898     /**
52899      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
52900      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
52901      * @param {Boolean} preservePanel Overrides the config preservePanel option
52902      * @return {Roo.ContentPanel} The panel that was removed
52903      */
52904     remove : function(panel, preservePanel){
52905         panel = this.getPanel(panel);
52906         if(!panel){
52907             return null;
52908         }
52909         var e = {};
52910         this.fireEvent("beforeremove", this, panel, e);
52911         if(e.cancel === true){
52912             return null;
52913         }
52914         var panelId = panel.getId();
52915         this.panels.removeKey(panelId);
52916         return panel;
52917     },
52918     
52919     /**
52920      * Returns the panel specified or null if it's not in this region.
52921      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
52922      * @return {Roo.ContentPanel}
52923      */
52924     getPanel : function(id){
52925         if(typeof id == "object"){ // must be panel obj
52926             return id;
52927         }
52928         return this.panels.get(id);
52929     },
52930     
52931     /**
52932      * Returns this regions position (north/south/east/west/center).
52933      * @return {String} 
52934      */
52935     getPosition: function(){
52936         return this.position;    
52937     }
52938 });/*
52939  * Based on:
52940  * Ext JS Library 1.1.1
52941  * Copyright(c) 2006-2007, Ext JS, LLC.
52942  *
52943  * Originally Released Under LGPL - original licence link has changed is not relivant.
52944  *
52945  * Fork - LGPL
52946  * <script type="text/javascript">
52947  */
52948  
52949 /**
52950  * @class Roo.LayoutRegion
52951  * @extends Roo.BasicLayoutRegion
52952  * This class represents a region in a layout manager.
52953  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
52954  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
52955  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
52956  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
52957  * @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})
52958  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
52959  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
52960  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
52961  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
52962  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
52963  * @cfg {String}    title           The title for the region (overrides panel titles)
52964  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
52965  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
52966  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
52967  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
52968  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
52969  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
52970  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
52971  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
52972  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
52973  * @cfg {Boolean}   showPin         True to show a pin button
52974  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
52975  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
52976  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
52977  * @cfg {Number}    width           For East/West panels
52978  * @cfg {Number}    height          For North/South panels
52979  * @cfg {Boolean}   split           To show the splitter
52980  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
52981  */
52982 Roo.LayoutRegion = function(mgr, config, pos){
52983     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
52984     var dh = Roo.DomHelper;
52985     /** This region's container element 
52986     * @type Roo.Element */
52987     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
52988     /** This region's title element 
52989     * @type Roo.Element */
52990
52991     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
52992         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
52993         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
52994     ]}, true);
52995     this.titleEl.enableDisplayMode();
52996     /** This region's title text element 
52997     * @type HTMLElement */
52998     this.titleTextEl = this.titleEl.dom.firstChild;
52999     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
53000     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
53001     this.closeBtn.enableDisplayMode();
53002     this.closeBtn.on("click", this.closeClicked, this);
53003     this.closeBtn.hide();
53004
53005     this.createBody(config);
53006     this.visible = true;
53007     this.collapsed = false;
53008
53009     if(config.hideWhenEmpty){
53010         this.hide();
53011         this.on("paneladded", this.validateVisibility, this);
53012         this.on("panelremoved", this.validateVisibility, this);
53013     }
53014     this.applyConfig(config);
53015 };
53016
53017 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
53018
53019     createBody : function(){
53020         /** This region's body element 
53021         * @type Roo.Element */
53022         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
53023     },
53024
53025     applyConfig : function(c){
53026         if(c.collapsible && this.position != "center" && !this.collapsedEl){
53027             var dh = Roo.DomHelper;
53028             if(c.titlebar !== false){
53029                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
53030                 this.collapseBtn.on("click", this.collapse, this);
53031                 this.collapseBtn.enableDisplayMode();
53032
53033                 if(c.showPin === true || this.showPin){
53034                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
53035                     this.stickBtn.enableDisplayMode();
53036                     this.stickBtn.on("click", this.expand, this);
53037                     this.stickBtn.hide();
53038                 }
53039             }
53040             /** This region's collapsed element
53041             * @type Roo.Element */
53042             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
53043                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
53044             ]}, true);
53045             if(c.floatable !== false){
53046                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
53047                this.collapsedEl.on("click", this.collapseClick, this);
53048             }
53049
53050             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
53051                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
53052                    id: "message", unselectable: "on", style:{"float":"left"}});
53053                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
53054              }
53055             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
53056             this.expandBtn.on("click", this.expand, this);
53057         }
53058         if(this.collapseBtn){
53059             this.collapseBtn.setVisible(c.collapsible == true);
53060         }
53061         this.cmargins = c.cmargins || this.cmargins ||
53062                          (this.position == "west" || this.position == "east" ?
53063                              {top: 0, left: 2, right:2, bottom: 0} :
53064                              {top: 2, left: 0, right:0, bottom: 2});
53065         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
53066         this.bottomTabs = c.tabPosition != "top";
53067         this.autoScroll = c.autoScroll || false;
53068         if(this.autoScroll){
53069             this.bodyEl.setStyle("overflow", "auto");
53070         }else{
53071             this.bodyEl.setStyle("overflow", "hidden");
53072         }
53073         //if(c.titlebar !== false){
53074             if((!c.titlebar && !c.title) || c.titlebar === false){
53075                 this.titleEl.hide();
53076             }else{
53077                 this.titleEl.show();
53078                 if(c.title){
53079                     this.titleTextEl.innerHTML = c.title;
53080                 }
53081             }
53082         //}
53083         this.duration = c.duration || .30;
53084         this.slideDuration = c.slideDuration || .45;
53085         this.config = c;
53086         if(c.collapsed){
53087             this.collapse(true);
53088         }
53089         if(c.hidden){
53090             this.hide();
53091         }
53092     },
53093     /**
53094      * Returns true if this region is currently visible.
53095      * @return {Boolean}
53096      */
53097     isVisible : function(){
53098         return this.visible;
53099     },
53100
53101     /**
53102      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
53103      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
53104      */
53105     setCollapsedTitle : function(title){
53106         title = title || "&#160;";
53107         if(this.collapsedTitleTextEl){
53108             this.collapsedTitleTextEl.innerHTML = title;
53109         }
53110     },
53111
53112     getBox : function(){
53113         var b;
53114         if(!this.collapsed){
53115             b = this.el.getBox(false, true);
53116         }else{
53117             b = this.collapsedEl.getBox(false, true);
53118         }
53119         return b;
53120     },
53121
53122     getMargins : function(){
53123         return this.collapsed ? this.cmargins : this.margins;
53124     },
53125
53126     highlight : function(){
53127         this.el.addClass("x-layout-panel-dragover");
53128     },
53129
53130     unhighlight : function(){
53131         this.el.removeClass("x-layout-panel-dragover");
53132     },
53133
53134     updateBox : function(box){
53135         this.box = box;
53136         if(!this.collapsed){
53137             this.el.dom.style.left = box.x + "px";
53138             this.el.dom.style.top = box.y + "px";
53139             this.updateBody(box.width, box.height);
53140         }else{
53141             this.collapsedEl.dom.style.left = box.x + "px";
53142             this.collapsedEl.dom.style.top = box.y + "px";
53143             this.collapsedEl.setSize(box.width, box.height);
53144         }
53145         if(this.tabs){
53146             this.tabs.autoSizeTabs();
53147         }
53148     },
53149
53150     updateBody : function(w, h){
53151         if(w !== null){
53152             this.el.setWidth(w);
53153             w -= this.el.getBorderWidth("rl");
53154             if(this.config.adjustments){
53155                 w += this.config.adjustments[0];
53156             }
53157         }
53158         if(h !== null){
53159             this.el.setHeight(h);
53160             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
53161             h -= this.el.getBorderWidth("tb");
53162             if(this.config.adjustments){
53163                 h += this.config.adjustments[1];
53164             }
53165             this.bodyEl.setHeight(h);
53166             if(this.tabs){
53167                 h = this.tabs.syncHeight(h);
53168             }
53169         }
53170         if(this.panelSize){
53171             w = w !== null ? w : this.panelSize.width;
53172             h = h !== null ? h : this.panelSize.height;
53173         }
53174         if(this.activePanel){
53175             var el = this.activePanel.getEl();
53176             w = w !== null ? w : el.getWidth();
53177             h = h !== null ? h : el.getHeight();
53178             this.panelSize = {width: w, height: h};
53179             this.activePanel.setSize(w, h);
53180         }
53181         if(Roo.isIE && this.tabs){
53182             this.tabs.el.repaint();
53183         }
53184     },
53185
53186     /**
53187      * Returns the container element for this region.
53188      * @return {Roo.Element}
53189      */
53190     getEl : function(){
53191         return this.el;
53192     },
53193
53194     /**
53195      * Hides this region.
53196      */
53197     hide : function(){
53198         if(!this.collapsed){
53199             this.el.dom.style.left = "-2000px";
53200             this.el.hide();
53201         }else{
53202             this.collapsedEl.dom.style.left = "-2000px";
53203             this.collapsedEl.hide();
53204         }
53205         this.visible = false;
53206         this.fireEvent("visibilitychange", this, false);
53207     },
53208
53209     /**
53210      * Shows this region if it was previously hidden.
53211      */
53212     show : function(){
53213         if(!this.collapsed){
53214             this.el.show();
53215         }else{
53216             this.collapsedEl.show();
53217         }
53218         this.visible = true;
53219         this.fireEvent("visibilitychange", this, true);
53220     },
53221
53222     closeClicked : function(){
53223         if(this.activePanel){
53224             this.remove(this.activePanel);
53225         }
53226     },
53227
53228     collapseClick : function(e){
53229         if(this.isSlid){
53230            e.stopPropagation();
53231            this.slideIn();
53232         }else{
53233            e.stopPropagation();
53234            this.slideOut();
53235         }
53236     },
53237
53238     /**
53239      * Collapses this region.
53240      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
53241      */
53242     collapse : function(skipAnim, skipCheck){
53243         if(this.collapsed) {
53244             return;
53245         }
53246         
53247         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
53248             
53249             this.collapsed = true;
53250             if(this.split){
53251                 this.split.el.hide();
53252             }
53253             if(this.config.animate && skipAnim !== true){
53254                 this.fireEvent("invalidated", this);
53255                 this.animateCollapse();
53256             }else{
53257                 this.el.setLocation(-20000,-20000);
53258                 this.el.hide();
53259                 this.collapsedEl.show();
53260                 this.fireEvent("collapsed", this);
53261                 this.fireEvent("invalidated", this);
53262             }
53263         }
53264         
53265     },
53266
53267     animateCollapse : function(){
53268         // overridden
53269     },
53270
53271     /**
53272      * Expands this region if it was previously collapsed.
53273      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
53274      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
53275      */
53276     expand : function(e, skipAnim){
53277         if(e) {
53278             e.stopPropagation();
53279         }
53280         if(!this.collapsed || this.el.hasActiveFx()) {
53281             return;
53282         }
53283         if(this.isSlid){
53284             this.afterSlideIn();
53285             skipAnim = true;
53286         }
53287         this.collapsed = false;
53288         if(this.config.animate && skipAnim !== true){
53289             this.animateExpand();
53290         }else{
53291             this.el.show();
53292             if(this.split){
53293                 this.split.el.show();
53294             }
53295             this.collapsedEl.setLocation(-2000,-2000);
53296             this.collapsedEl.hide();
53297             this.fireEvent("invalidated", this);
53298             this.fireEvent("expanded", this);
53299         }
53300     },
53301
53302     animateExpand : function(){
53303         // overridden
53304     },
53305
53306     initTabs : function()
53307     {
53308         this.bodyEl.setStyle("overflow", "hidden");
53309         var ts = new Roo.TabPanel(
53310                 this.bodyEl.dom,
53311                 {
53312                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
53313                     disableTooltips: this.config.disableTabTips,
53314                     toolbar : this.config.toolbar
53315                 }
53316         );
53317         if(this.config.hideTabs){
53318             ts.stripWrap.setDisplayed(false);
53319         }
53320         this.tabs = ts;
53321         ts.resizeTabs = this.config.resizeTabs === true;
53322         ts.minTabWidth = this.config.minTabWidth || 40;
53323         ts.maxTabWidth = this.config.maxTabWidth || 250;
53324         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
53325         ts.monitorResize = false;
53326         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
53327         ts.bodyEl.addClass('x-layout-tabs-body');
53328         this.panels.each(this.initPanelAsTab, this);
53329     },
53330
53331     initPanelAsTab : function(panel){
53332         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
53333                     this.config.closeOnTab && panel.isClosable());
53334         if(panel.tabTip !== undefined){
53335             ti.setTooltip(panel.tabTip);
53336         }
53337         ti.on("activate", function(){
53338               this.setActivePanel(panel);
53339         }, this);
53340         if(this.config.closeOnTab){
53341             ti.on("beforeclose", function(t, e){
53342                 e.cancel = true;
53343                 this.remove(panel);
53344             }, this);
53345         }
53346         return ti;
53347     },
53348
53349     updatePanelTitle : function(panel, title){
53350         if(this.activePanel == panel){
53351             this.updateTitle(title);
53352         }
53353         if(this.tabs){
53354             var ti = this.tabs.getTab(panel.getEl().id);
53355             ti.setText(title);
53356             if(panel.tabTip !== undefined){
53357                 ti.setTooltip(panel.tabTip);
53358             }
53359         }
53360     },
53361
53362     updateTitle : function(title){
53363         if(this.titleTextEl && !this.config.title){
53364             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
53365         }
53366     },
53367
53368     setActivePanel : function(panel){
53369         panel = this.getPanel(panel);
53370         if(this.activePanel && this.activePanel != panel){
53371             this.activePanel.setActiveState(false);
53372         }
53373         this.activePanel = panel;
53374         panel.setActiveState(true);
53375         if(this.panelSize){
53376             panel.setSize(this.panelSize.width, this.panelSize.height);
53377         }
53378         if(this.closeBtn){
53379             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
53380         }
53381         this.updateTitle(panel.getTitle());
53382         if(this.tabs){
53383             this.fireEvent("invalidated", this);
53384         }
53385         this.fireEvent("panelactivated", this, panel);
53386     },
53387
53388     /**
53389      * Shows the specified panel.
53390      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
53391      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
53392      */
53393     showPanel : function(panel)
53394     {
53395         panel = this.getPanel(panel);
53396         if(panel){
53397             if(this.tabs){
53398                 var tab = this.tabs.getTab(panel.getEl().id);
53399                 if(tab.isHidden()){
53400                     this.tabs.unhideTab(tab.id);
53401                 }
53402                 tab.activate();
53403             }else{
53404                 this.setActivePanel(panel);
53405             }
53406         }
53407         return panel;
53408     },
53409
53410     /**
53411      * Get the active panel for this region.
53412      * @return {Roo.ContentPanel} The active panel or null
53413      */
53414     getActivePanel : function(){
53415         return this.activePanel;
53416     },
53417
53418     validateVisibility : function(){
53419         if(this.panels.getCount() < 1){
53420             this.updateTitle("&#160;");
53421             this.closeBtn.hide();
53422             this.hide();
53423         }else{
53424             if(!this.isVisible()){
53425                 this.show();
53426             }
53427         }
53428     },
53429
53430     /**
53431      * Adds the passed ContentPanel(s) to this region.
53432      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
53433      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
53434      */
53435     add : function(panel){
53436         if(arguments.length > 1){
53437             for(var i = 0, len = arguments.length; i < len; i++) {
53438                 this.add(arguments[i]);
53439             }
53440             return null;
53441         }
53442         if(this.hasPanel(panel)){
53443             this.showPanel(panel);
53444             return panel;
53445         }
53446         panel.setRegion(this);
53447         this.panels.add(panel);
53448         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
53449             this.bodyEl.dom.appendChild(panel.getEl().dom);
53450             if(panel.background !== true){
53451                 this.setActivePanel(panel);
53452             }
53453             this.fireEvent("paneladded", this, panel);
53454             return panel;
53455         }
53456         if(!this.tabs){
53457             this.initTabs();
53458         }else{
53459             this.initPanelAsTab(panel);
53460         }
53461         if(panel.background !== true){
53462             this.tabs.activate(panel.getEl().id);
53463         }
53464         this.fireEvent("paneladded", this, panel);
53465         return panel;
53466     },
53467
53468     /**
53469      * Hides the tab for the specified panel.
53470      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
53471      */
53472     hidePanel : function(panel){
53473         if(this.tabs && (panel = this.getPanel(panel))){
53474             this.tabs.hideTab(panel.getEl().id);
53475         }
53476     },
53477
53478     /**
53479      * Unhides the tab for a previously hidden panel.
53480      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
53481      */
53482     unhidePanel : function(panel){
53483         if(this.tabs && (panel = this.getPanel(panel))){
53484             this.tabs.unhideTab(panel.getEl().id);
53485         }
53486     },
53487
53488     clearPanels : function(){
53489         while(this.panels.getCount() > 0){
53490              this.remove(this.panels.first());
53491         }
53492     },
53493
53494     /**
53495      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
53496      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
53497      * @param {Boolean} preservePanel Overrides the config preservePanel option
53498      * @return {Roo.ContentPanel} The panel that was removed
53499      */
53500     remove : function(panel, preservePanel){
53501         panel = this.getPanel(panel);
53502         if(!panel){
53503             return null;
53504         }
53505         var e = {};
53506         this.fireEvent("beforeremove", this, panel, e);
53507         if(e.cancel === true){
53508             return null;
53509         }
53510         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
53511         var panelId = panel.getId();
53512         this.panels.removeKey(panelId);
53513         if(preservePanel){
53514             document.body.appendChild(panel.getEl().dom);
53515         }
53516         if(this.tabs){
53517             this.tabs.removeTab(panel.getEl().id);
53518         }else if (!preservePanel){
53519             this.bodyEl.dom.removeChild(panel.getEl().dom);
53520         }
53521         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
53522             var p = this.panels.first();
53523             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
53524             tempEl.appendChild(p.getEl().dom);
53525             this.bodyEl.update("");
53526             this.bodyEl.dom.appendChild(p.getEl().dom);
53527             tempEl = null;
53528             this.updateTitle(p.getTitle());
53529             this.tabs = null;
53530             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
53531             this.setActivePanel(p);
53532         }
53533         panel.setRegion(null);
53534         if(this.activePanel == panel){
53535             this.activePanel = null;
53536         }
53537         if(this.config.autoDestroy !== false && preservePanel !== true){
53538             try{panel.destroy();}catch(e){}
53539         }
53540         this.fireEvent("panelremoved", this, panel);
53541         return panel;
53542     },
53543
53544     /**
53545      * Returns the TabPanel component used by this region
53546      * @return {Roo.TabPanel}
53547      */
53548     getTabs : function(){
53549         return this.tabs;
53550     },
53551
53552     createTool : function(parentEl, className){
53553         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
53554             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
53555         btn.addClassOnOver("x-layout-tools-button-over");
53556         return btn;
53557     }
53558 });/*
53559  * Based on:
53560  * Ext JS Library 1.1.1
53561  * Copyright(c) 2006-2007, Ext JS, LLC.
53562  *
53563  * Originally Released Under LGPL - original licence link has changed is not relivant.
53564  *
53565  * Fork - LGPL
53566  * <script type="text/javascript">
53567  */
53568  
53569
53570
53571 /**
53572  * @class Roo.SplitLayoutRegion
53573  * @extends Roo.LayoutRegion
53574  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
53575  */
53576 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
53577     this.cursor = cursor;
53578     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
53579 };
53580
53581 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
53582     splitTip : "Drag to resize.",
53583     collapsibleSplitTip : "Drag to resize. Double click to hide.",
53584     useSplitTips : false,
53585
53586     applyConfig : function(config){
53587         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
53588         if(config.split){
53589             if(!this.split){
53590                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
53591                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
53592                 /** The SplitBar for this region 
53593                 * @type Roo.SplitBar */
53594                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
53595                 this.split.on("moved", this.onSplitMove, this);
53596                 this.split.useShim = config.useShim === true;
53597                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
53598                 if(this.useSplitTips){
53599                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
53600                 }
53601                 if(config.collapsible){
53602                     this.split.el.on("dblclick", this.collapse,  this);
53603                 }
53604             }
53605             if(typeof config.minSize != "undefined"){
53606                 this.split.minSize = config.minSize;
53607             }
53608             if(typeof config.maxSize != "undefined"){
53609                 this.split.maxSize = config.maxSize;
53610             }
53611             if(config.hideWhenEmpty || config.hidden || config.collapsed){
53612                 this.hideSplitter();
53613             }
53614         }
53615     },
53616
53617     getHMaxSize : function(){
53618          var cmax = this.config.maxSize || 10000;
53619          var center = this.mgr.getRegion("center");
53620          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
53621     },
53622
53623     getVMaxSize : function(){
53624          var cmax = this.config.maxSize || 10000;
53625          var center = this.mgr.getRegion("center");
53626          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
53627     },
53628
53629     onSplitMove : function(split, newSize){
53630         this.fireEvent("resized", this, newSize);
53631     },
53632     
53633     /** 
53634      * Returns the {@link Roo.SplitBar} for this region.
53635      * @return {Roo.SplitBar}
53636      */
53637     getSplitBar : function(){
53638         return this.split;
53639     },
53640     
53641     hide : function(){
53642         this.hideSplitter();
53643         Roo.SplitLayoutRegion.superclass.hide.call(this);
53644     },
53645
53646     hideSplitter : function(){
53647         if(this.split){
53648             this.split.el.setLocation(-2000,-2000);
53649             this.split.el.hide();
53650         }
53651     },
53652
53653     show : function(){
53654         if(this.split){
53655             this.split.el.show();
53656         }
53657         Roo.SplitLayoutRegion.superclass.show.call(this);
53658     },
53659     
53660     beforeSlide: function(){
53661         if(Roo.isGecko){// firefox overflow auto bug workaround
53662             this.bodyEl.clip();
53663             if(this.tabs) {
53664                 this.tabs.bodyEl.clip();
53665             }
53666             if(this.activePanel){
53667                 this.activePanel.getEl().clip();
53668                 
53669                 if(this.activePanel.beforeSlide){
53670                     this.activePanel.beforeSlide();
53671                 }
53672             }
53673         }
53674     },
53675     
53676     afterSlide : function(){
53677         if(Roo.isGecko){// firefox overflow auto bug workaround
53678             this.bodyEl.unclip();
53679             if(this.tabs) {
53680                 this.tabs.bodyEl.unclip();
53681             }
53682             if(this.activePanel){
53683                 this.activePanel.getEl().unclip();
53684                 if(this.activePanel.afterSlide){
53685                     this.activePanel.afterSlide();
53686                 }
53687             }
53688         }
53689     },
53690
53691     initAutoHide : function(){
53692         if(this.autoHide !== false){
53693             if(!this.autoHideHd){
53694                 var st = new Roo.util.DelayedTask(this.slideIn, this);
53695                 this.autoHideHd = {
53696                     "mouseout": function(e){
53697                         if(!e.within(this.el, true)){
53698                             st.delay(500);
53699                         }
53700                     },
53701                     "mouseover" : function(e){
53702                         st.cancel();
53703                     },
53704                     scope : this
53705                 };
53706             }
53707             this.el.on(this.autoHideHd);
53708         }
53709     },
53710
53711     clearAutoHide : function(){
53712         if(this.autoHide !== false){
53713             this.el.un("mouseout", this.autoHideHd.mouseout);
53714             this.el.un("mouseover", this.autoHideHd.mouseover);
53715         }
53716     },
53717
53718     clearMonitor : function(){
53719         Roo.get(document).un("click", this.slideInIf, this);
53720     },
53721
53722     // these names are backwards but not changed for compat
53723     slideOut : function(){
53724         if(this.isSlid || this.el.hasActiveFx()){
53725             return;
53726         }
53727         this.isSlid = true;
53728         if(this.collapseBtn){
53729             this.collapseBtn.hide();
53730         }
53731         this.closeBtnState = this.closeBtn.getStyle('display');
53732         this.closeBtn.hide();
53733         if(this.stickBtn){
53734             this.stickBtn.show();
53735         }
53736         this.el.show();
53737         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
53738         this.beforeSlide();
53739         this.el.setStyle("z-index", 10001);
53740         this.el.slideIn(this.getSlideAnchor(), {
53741             callback: function(){
53742                 this.afterSlide();
53743                 this.initAutoHide();
53744                 Roo.get(document).on("click", this.slideInIf, this);
53745                 this.fireEvent("slideshow", this);
53746             },
53747             scope: this,
53748             block: true
53749         });
53750     },
53751
53752     afterSlideIn : function(){
53753         this.clearAutoHide();
53754         this.isSlid = false;
53755         this.clearMonitor();
53756         this.el.setStyle("z-index", "");
53757         if(this.collapseBtn){
53758             this.collapseBtn.show();
53759         }
53760         this.closeBtn.setStyle('display', this.closeBtnState);
53761         if(this.stickBtn){
53762             this.stickBtn.hide();
53763         }
53764         this.fireEvent("slidehide", this);
53765     },
53766
53767     slideIn : function(cb){
53768         if(!this.isSlid || this.el.hasActiveFx()){
53769             Roo.callback(cb);
53770             return;
53771         }
53772         this.isSlid = false;
53773         this.beforeSlide();
53774         this.el.slideOut(this.getSlideAnchor(), {
53775             callback: function(){
53776                 this.el.setLeftTop(-10000, -10000);
53777                 this.afterSlide();
53778                 this.afterSlideIn();
53779                 Roo.callback(cb);
53780             },
53781             scope: this,
53782             block: true
53783         });
53784     },
53785     
53786     slideInIf : function(e){
53787         if(!e.within(this.el)){
53788             this.slideIn();
53789         }
53790     },
53791
53792     animateCollapse : function(){
53793         this.beforeSlide();
53794         this.el.setStyle("z-index", 20000);
53795         var anchor = this.getSlideAnchor();
53796         this.el.slideOut(anchor, {
53797             callback : function(){
53798                 this.el.setStyle("z-index", "");
53799                 this.collapsedEl.slideIn(anchor, {duration:.3});
53800                 this.afterSlide();
53801                 this.el.setLocation(-10000,-10000);
53802                 this.el.hide();
53803                 this.fireEvent("collapsed", this);
53804             },
53805             scope: this,
53806             block: true
53807         });
53808     },
53809
53810     animateExpand : function(){
53811         this.beforeSlide();
53812         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
53813         this.el.setStyle("z-index", 20000);
53814         this.collapsedEl.hide({
53815             duration:.1
53816         });
53817         this.el.slideIn(this.getSlideAnchor(), {
53818             callback : function(){
53819                 this.el.setStyle("z-index", "");
53820                 this.afterSlide();
53821                 if(this.split){
53822                     this.split.el.show();
53823                 }
53824                 this.fireEvent("invalidated", this);
53825                 this.fireEvent("expanded", this);
53826             },
53827             scope: this,
53828             block: true
53829         });
53830     },
53831
53832     anchors : {
53833         "west" : "left",
53834         "east" : "right",
53835         "north" : "top",
53836         "south" : "bottom"
53837     },
53838
53839     sanchors : {
53840         "west" : "l",
53841         "east" : "r",
53842         "north" : "t",
53843         "south" : "b"
53844     },
53845
53846     canchors : {
53847         "west" : "tl-tr",
53848         "east" : "tr-tl",
53849         "north" : "tl-bl",
53850         "south" : "bl-tl"
53851     },
53852
53853     getAnchor : function(){
53854         return this.anchors[this.position];
53855     },
53856
53857     getCollapseAnchor : function(){
53858         return this.canchors[this.position];
53859     },
53860
53861     getSlideAnchor : function(){
53862         return this.sanchors[this.position];
53863     },
53864
53865     getAlignAdj : function(){
53866         var cm = this.cmargins;
53867         switch(this.position){
53868             case "west":
53869                 return [0, 0];
53870             break;
53871             case "east":
53872                 return [0, 0];
53873             break;
53874             case "north":
53875                 return [0, 0];
53876             break;
53877             case "south":
53878                 return [0, 0];
53879             break;
53880         }
53881     },
53882
53883     getExpandAdj : function(){
53884         var c = this.collapsedEl, cm = this.cmargins;
53885         switch(this.position){
53886             case "west":
53887                 return [-(cm.right+c.getWidth()+cm.left), 0];
53888             break;
53889             case "east":
53890                 return [cm.right+c.getWidth()+cm.left, 0];
53891             break;
53892             case "north":
53893                 return [0, -(cm.top+cm.bottom+c.getHeight())];
53894             break;
53895             case "south":
53896                 return [0, cm.top+cm.bottom+c.getHeight()];
53897             break;
53898         }
53899     }
53900 });/*
53901  * Based on:
53902  * Ext JS Library 1.1.1
53903  * Copyright(c) 2006-2007, Ext JS, LLC.
53904  *
53905  * Originally Released Under LGPL - original licence link has changed is not relivant.
53906  *
53907  * Fork - LGPL
53908  * <script type="text/javascript">
53909  */
53910 /*
53911  * These classes are private internal classes
53912  */
53913 Roo.CenterLayoutRegion = function(mgr, config){
53914     Roo.LayoutRegion.call(this, mgr, config, "center");
53915     this.visible = true;
53916     this.minWidth = config.minWidth || 20;
53917     this.minHeight = config.minHeight || 20;
53918 };
53919
53920 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
53921     hide : function(){
53922         // center panel can't be hidden
53923     },
53924     
53925     show : function(){
53926         // center panel can't be hidden
53927     },
53928     
53929     getMinWidth: function(){
53930         return this.minWidth;
53931     },
53932     
53933     getMinHeight: function(){
53934         return this.minHeight;
53935     }
53936 });
53937
53938
53939 Roo.NorthLayoutRegion = function(mgr, config){
53940     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
53941     if(this.split){
53942         this.split.placement = Roo.SplitBar.TOP;
53943         this.split.orientation = Roo.SplitBar.VERTICAL;
53944         this.split.el.addClass("x-layout-split-v");
53945     }
53946     var size = config.initialSize || config.height;
53947     if(typeof size != "undefined"){
53948         this.el.setHeight(size);
53949     }
53950 };
53951 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
53952     orientation: Roo.SplitBar.VERTICAL,
53953     getBox : function(){
53954         if(this.collapsed){
53955             return this.collapsedEl.getBox();
53956         }
53957         var box = this.el.getBox();
53958         if(this.split){
53959             box.height += this.split.el.getHeight();
53960         }
53961         return box;
53962     },
53963     
53964     updateBox : function(box){
53965         if(this.split && !this.collapsed){
53966             box.height -= this.split.el.getHeight();
53967             this.split.el.setLeft(box.x);
53968             this.split.el.setTop(box.y+box.height);
53969             this.split.el.setWidth(box.width);
53970         }
53971         if(this.collapsed){
53972             this.updateBody(box.width, null);
53973         }
53974         Roo.LayoutRegion.prototype.updateBox.call(this, box);
53975     }
53976 });
53977
53978 Roo.SouthLayoutRegion = function(mgr, config){
53979     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
53980     if(this.split){
53981         this.split.placement = Roo.SplitBar.BOTTOM;
53982         this.split.orientation = Roo.SplitBar.VERTICAL;
53983         this.split.el.addClass("x-layout-split-v");
53984     }
53985     var size = config.initialSize || config.height;
53986     if(typeof size != "undefined"){
53987         this.el.setHeight(size);
53988     }
53989 };
53990 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
53991     orientation: Roo.SplitBar.VERTICAL,
53992     getBox : function(){
53993         if(this.collapsed){
53994             return this.collapsedEl.getBox();
53995         }
53996         var box = this.el.getBox();
53997         if(this.split){
53998             var sh = this.split.el.getHeight();
53999             box.height += sh;
54000             box.y -= sh;
54001         }
54002         return box;
54003     },
54004     
54005     updateBox : function(box){
54006         if(this.split && !this.collapsed){
54007             var sh = this.split.el.getHeight();
54008             box.height -= sh;
54009             box.y += sh;
54010             this.split.el.setLeft(box.x);
54011             this.split.el.setTop(box.y-sh);
54012             this.split.el.setWidth(box.width);
54013         }
54014         if(this.collapsed){
54015             this.updateBody(box.width, null);
54016         }
54017         Roo.LayoutRegion.prototype.updateBox.call(this, box);
54018     }
54019 });
54020
54021 Roo.EastLayoutRegion = function(mgr, config){
54022     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
54023     if(this.split){
54024         this.split.placement = Roo.SplitBar.RIGHT;
54025         this.split.orientation = Roo.SplitBar.HORIZONTAL;
54026         this.split.el.addClass("x-layout-split-h");
54027     }
54028     var size = config.initialSize || config.width;
54029     if(typeof size != "undefined"){
54030         this.el.setWidth(size);
54031     }
54032 };
54033 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
54034     orientation: Roo.SplitBar.HORIZONTAL,
54035     getBox : function(){
54036         if(this.collapsed){
54037             return this.collapsedEl.getBox();
54038         }
54039         var box = this.el.getBox();
54040         if(this.split){
54041             var sw = this.split.el.getWidth();
54042             box.width += sw;
54043             box.x -= sw;
54044         }
54045         return box;
54046     },
54047
54048     updateBox : function(box){
54049         if(this.split && !this.collapsed){
54050             var sw = this.split.el.getWidth();
54051             box.width -= sw;
54052             this.split.el.setLeft(box.x);
54053             this.split.el.setTop(box.y);
54054             this.split.el.setHeight(box.height);
54055             box.x += sw;
54056         }
54057         if(this.collapsed){
54058             this.updateBody(null, box.height);
54059         }
54060         Roo.LayoutRegion.prototype.updateBox.call(this, box);
54061     }
54062 });
54063
54064 Roo.WestLayoutRegion = function(mgr, config){
54065     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
54066     if(this.split){
54067         this.split.placement = Roo.SplitBar.LEFT;
54068         this.split.orientation = Roo.SplitBar.HORIZONTAL;
54069         this.split.el.addClass("x-layout-split-h");
54070     }
54071     var size = config.initialSize || config.width;
54072     if(typeof size != "undefined"){
54073         this.el.setWidth(size);
54074     }
54075 };
54076 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
54077     orientation: Roo.SplitBar.HORIZONTAL,
54078     getBox : function(){
54079         if(this.collapsed){
54080             return this.collapsedEl.getBox();
54081         }
54082         var box = this.el.getBox();
54083         if(this.split){
54084             box.width += this.split.el.getWidth();
54085         }
54086         return box;
54087     },
54088     
54089     updateBox : function(box){
54090         if(this.split && !this.collapsed){
54091             var sw = this.split.el.getWidth();
54092             box.width -= sw;
54093             this.split.el.setLeft(box.x+box.width);
54094             this.split.el.setTop(box.y);
54095             this.split.el.setHeight(box.height);
54096         }
54097         if(this.collapsed){
54098             this.updateBody(null, box.height);
54099         }
54100         Roo.LayoutRegion.prototype.updateBox.call(this, box);
54101     }
54102 });
54103 /*
54104  * Based on:
54105  * Ext JS Library 1.1.1
54106  * Copyright(c) 2006-2007, Ext JS, LLC.
54107  *
54108  * Originally Released Under LGPL - original licence link has changed is not relivant.
54109  *
54110  * Fork - LGPL
54111  * <script type="text/javascript">
54112  */
54113  
54114  
54115 /*
54116  * Private internal class for reading and applying state
54117  */
54118 Roo.LayoutStateManager = function(layout){
54119      // default empty state
54120      this.state = {
54121         north: {},
54122         south: {},
54123         east: {},
54124         west: {}       
54125     };
54126 };
54127
54128 Roo.LayoutStateManager.prototype = {
54129     init : function(layout, provider){
54130         this.provider = provider;
54131         var state = provider.get(layout.id+"-layout-state");
54132         if(state){
54133             var wasUpdating = layout.isUpdating();
54134             if(!wasUpdating){
54135                 layout.beginUpdate();
54136             }
54137             for(var key in state){
54138                 if(typeof state[key] != "function"){
54139                     var rstate = state[key];
54140                     var r = layout.getRegion(key);
54141                     if(r && rstate){
54142                         if(rstate.size){
54143                             r.resizeTo(rstate.size);
54144                         }
54145                         if(rstate.collapsed == true){
54146                             r.collapse(true);
54147                         }else{
54148                             r.expand(null, true);
54149                         }
54150                     }
54151                 }
54152             }
54153             if(!wasUpdating){
54154                 layout.endUpdate();
54155             }
54156             this.state = state; 
54157         }
54158         this.layout = layout;
54159         layout.on("regionresized", this.onRegionResized, this);
54160         layout.on("regioncollapsed", this.onRegionCollapsed, this);
54161         layout.on("regionexpanded", this.onRegionExpanded, this);
54162     },
54163     
54164     storeState : function(){
54165         this.provider.set(this.layout.id+"-layout-state", this.state);
54166     },
54167     
54168     onRegionResized : function(region, newSize){
54169         this.state[region.getPosition()].size = newSize;
54170         this.storeState();
54171     },
54172     
54173     onRegionCollapsed : function(region){
54174         this.state[region.getPosition()].collapsed = true;
54175         this.storeState();
54176     },
54177     
54178     onRegionExpanded : function(region){
54179         this.state[region.getPosition()].collapsed = false;
54180         this.storeState();
54181     }
54182 };/*
54183  * Based on:
54184  * Ext JS Library 1.1.1
54185  * Copyright(c) 2006-2007, Ext JS, LLC.
54186  *
54187  * Originally Released Under LGPL - original licence link has changed is not relivant.
54188  *
54189  * Fork - LGPL
54190  * <script type="text/javascript">
54191  */
54192 /**
54193  * @class Roo.ContentPanel
54194  * @extends Roo.util.Observable
54195  * A basic ContentPanel element.
54196  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
54197  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
54198  * @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
54199  * @cfg {Boolean}   closable      True if the panel can be closed/removed
54200  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
54201  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
54202  * @cfg {Toolbar}   toolbar       A toolbar for this panel
54203  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
54204  * @cfg {String} title          The title for this panel
54205  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
54206  * @cfg {String} url            Calls {@link #setUrl} with this value
54207  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
54208  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
54209  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
54210  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
54211  * @cfg {String}    style  Extra style to add to the content panel 
54212
54213  * @constructor
54214  * Create a new ContentPanel.
54215  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
54216  * @param {String/Object} config A string to set only the title or a config object
54217  * @param {String} content (optional) Set the HTML content for this panel
54218  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
54219  */
54220 Roo.ContentPanel = function(el, config, content){
54221     
54222      
54223     /*
54224     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
54225         config = el;
54226         el = Roo.id();
54227     }
54228     if (config && config.parentLayout) { 
54229         el = config.parentLayout.el.createChild(); 
54230     }
54231     */
54232     if(el.autoCreate){ // xtype is available if this is called from factory
54233         config = el;
54234         el = Roo.id();
54235     }
54236     this.el = Roo.get(el);
54237     if(!this.el && config && config.autoCreate){
54238         if(typeof config.autoCreate == "object"){
54239             if(!config.autoCreate.id){
54240                 config.autoCreate.id = config.id||el;
54241             }
54242             this.el = Roo.DomHelper.append(document.body,
54243                         config.autoCreate, true);
54244         }else{
54245             this.el = Roo.DomHelper.append(document.body,
54246                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
54247         }
54248     }
54249     
54250     
54251     this.closable = false;
54252     this.loaded = false;
54253     this.active = false;
54254     if(typeof config == "string"){
54255         this.title = config;
54256     }else{
54257         Roo.apply(this, config);
54258     }
54259     
54260     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
54261         this.wrapEl = this.el.wrap();
54262         this.toolbar.container = this.el.insertSibling(false, 'before');
54263         this.toolbar = new Roo.Toolbar(this.toolbar);
54264     }
54265     
54266     // xtype created footer. - not sure if will work as we normally have to render first..
54267     if (this.footer && !this.footer.el && this.footer.xtype) {
54268         if (!this.wrapEl) {
54269             this.wrapEl = this.el.wrap();
54270         }
54271     
54272         this.footer.container = this.wrapEl.createChild();
54273          
54274         this.footer = Roo.factory(this.footer, Roo);
54275         
54276     }
54277     
54278     if(this.resizeEl){
54279         this.resizeEl = Roo.get(this.resizeEl, true);
54280     }else{
54281         this.resizeEl = this.el;
54282     }
54283     // handle view.xtype
54284     
54285  
54286     
54287     
54288     this.addEvents({
54289         /**
54290          * @event activate
54291          * Fires when this panel is activated. 
54292          * @param {Roo.ContentPanel} this
54293          */
54294         "activate" : true,
54295         /**
54296          * @event deactivate
54297          * Fires when this panel is activated. 
54298          * @param {Roo.ContentPanel} this
54299          */
54300         "deactivate" : true,
54301
54302         /**
54303          * @event resize
54304          * Fires when this panel is resized if fitToFrame is true.
54305          * @param {Roo.ContentPanel} this
54306          * @param {Number} width The width after any component adjustments
54307          * @param {Number} height The height after any component adjustments
54308          */
54309         "resize" : true,
54310         
54311          /**
54312          * @event render
54313          * Fires when this tab is created
54314          * @param {Roo.ContentPanel} this
54315          */
54316         "render" : true
54317          
54318         
54319     });
54320     
54321
54322     
54323     
54324     if(this.autoScroll){
54325         this.resizeEl.setStyle("overflow", "auto");
54326     } else {
54327         // fix randome scrolling
54328         this.el.on('scroll', function() {
54329             Roo.log('fix random scolling');
54330             this.scrollTo('top',0); 
54331         });
54332     }
54333     content = content || this.content;
54334     if(content){
54335         this.setContent(content);
54336     }
54337     if(config && config.url){
54338         this.setUrl(this.url, this.params, this.loadOnce);
54339     }
54340     
54341     
54342     
54343     Roo.ContentPanel.superclass.constructor.call(this);
54344     
54345     if (this.view && typeof(this.view.xtype) != 'undefined') {
54346         this.view.el = this.el.appendChild(document.createElement("div"));
54347         this.view = Roo.factory(this.view); 
54348         this.view.render  &&  this.view.render(false, '');  
54349     }
54350     
54351     
54352     this.fireEvent('render', this);
54353 };
54354
54355 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
54356     tabTip:'',
54357     setRegion : function(region){
54358         this.region = region;
54359         if(region){
54360            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
54361         }else{
54362            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
54363         } 
54364     },
54365     
54366     /**
54367      * Returns the toolbar for this Panel if one was configured. 
54368      * @return {Roo.Toolbar} 
54369      */
54370     getToolbar : function(){
54371         return this.toolbar;
54372     },
54373     
54374     setActiveState : function(active){
54375         this.active = active;
54376         if(!active){
54377             this.fireEvent("deactivate", this);
54378         }else{
54379             this.fireEvent("activate", this);
54380         }
54381     },
54382     /**
54383      * Updates this panel's element
54384      * @param {String} content The new content
54385      * @param {Boolean} loadScripts (optional) true to look for and process scripts
54386     */
54387     setContent : function(content, loadScripts){
54388         this.el.update(content, loadScripts);
54389     },
54390
54391     ignoreResize : function(w, h){
54392         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
54393             return true;
54394         }else{
54395             this.lastSize = {width: w, height: h};
54396             return false;
54397         }
54398     },
54399     /**
54400      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
54401      * @return {Roo.UpdateManager} The UpdateManager
54402      */
54403     getUpdateManager : function(){
54404         return this.el.getUpdateManager();
54405     },
54406      /**
54407      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
54408      * @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:
54409 <pre><code>
54410 panel.load({
54411     url: "your-url.php",
54412     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
54413     callback: yourFunction,
54414     scope: yourObject, //(optional scope)
54415     discardUrl: false,
54416     nocache: false,
54417     text: "Loading...",
54418     timeout: 30,
54419     scripts: false
54420 });
54421 </code></pre>
54422      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
54423      * 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.
54424      * @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}
54425      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
54426      * @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.
54427      * @return {Roo.ContentPanel} this
54428      */
54429     load : function(){
54430         var um = this.el.getUpdateManager();
54431         um.update.apply(um, arguments);
54432         return this;
54433     },
54434
54435
54436     /**
54437      * 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.
54438      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
54439      * @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)
54440      * @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)
54441      * @return {Roo.UpdateManager} The UpdateManager
54442      */
54443     setUrl : function(url, params, loadOnce){
54444         if(this.refreshDelegate){
54445             this.removeListener("activate", this.refreshDelegate);
54446         }
54447         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
54448         this.on("activate", this.refreshDelegate);
54449         return this.el.getUpdateManager();
54450     },
54451     
54452     _handleRefresh : function(url, params, loadOnce){
54453         if(!loadOnce || !this.loaded){
54454             var updater = this.el.getUpdateManager();
54455             updater.update(url, params, this._setLoaded.createDelegate(this));
54456         }
54457     },
54458     
54459     _setLoaded : function(){
54460         this.loaded = true;
54461     }, 
54462     
54463     /**
54464      * Returns this panel's id
54465      * @return {String} 
54466      */
54467     getId : function(){
54468         return this.el.id;
54469     },
54470     
54471     /** 
54472      * Returns this panel's element - used by regiosn to add.
54473      * @return {Roo.Element} 
54474      */
54475     getEl : function(){
54476         return this.wrapEl || this.el;
54477     },
54478     
54479     adjustForComponents : function(width, height)
54480     {
54481         //Roo.log('adjustForComponents ');
54482         if(this.resizeEl != this.el){
54483             width -= this.el.getFrameWidth('lr');
54484             height -= this.el.getFrameWidth('tb');
54485         }
54486         if(this.toolbar){
54487             var te = this.toolbar.getEl();
54488             height -= te.getHeight();
54489             te.setWidth(width);
54490         }
54491         if(this.footer){
54492             var te = this.footer.getEl();
54493             //Roo.log("footer:" + te.getHeight());
54494             
54495             height -= te.getHeight();
54496             te.setWidth(width);
54497         }
54498         
54499         
54500         if(this.adjustments){
54501             width += this.adjustments[0];
54502             height += this.adjustments[1];
54503         }
54504         return {"width": width, "height": height};
54505     },
54506     
54507     setSize : function(width, height){
54508         if(this.fitToFrame && !this.ignoreResize(width, height)){
54509             if(this.fitContainer && this.resizeEl != this.el){
54510                 this.el.setSize(width, height);
54511             }
54512             var size = this.adjustForComponents(width, height);
54513             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
54514             this.fireEvent('resize', this, size.width, size.height);
54515         }
54516     },
54517     
54518     /**
54519      * Returns this panel's title
54520      * @return {String} 
54521      */
54522     getTitle : function(){
54523         return this.title;
54524     },
54525     
54526     /**
54527      * Set this panel's title
54528      * @param {String} title
54529      */
54530     setTitle : function(title){
54531         this.title = title;
54532         if(this.region){
54533             this.region.updatePanelTitle(this, title);
54534         }
54535     },
54536     
54537     /**
54538      * Returns true is this panel was configured to be closable
54539      * @return {Boolean} 
54540      */
54541     isClosable : function(){
54542         return this.closable;
54543     },
54544     
54545     beforeSlide : function(){
54546         this.el.clip();
54547         this.resizeEl.clip();
54548     },
54549     
54550     afterSlide : function(){
54551         this.el.unclip();
54552         this.resizeEl.unclip();
54553     },
54554     
54555     /**
54556      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
54557      *   Will fail silently if the {@link #setUrl} method has not been called.
54558      *   This does not activate the panel, just updates its content.
54559      */
54560     refresh : function(){
54561         if(this.refreshDelegate){
54562            this.loaded = false;
54563            this.refreshDelegate();
54564         }
54565     },
54566     
54567     /**
54568      * Destroys this panel
54569      */
54570     destroy : function(){
54571         this.el.removeAllListeners();
54572         var tempEl = document.createElement("span");
54573         tempEl.appendChild(this.el.dom);
54574         tempEl.innerHTML = "";
54575         this.el.remove();
54576         this.el = null;
54577     },
54578     
54579     /**
54580      * form - if the content panel contains a form - this is a reference to it.
54581      * @type {Roo.form.Form}
54582      */
54583     form : false,
54584     /**
54585      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
54586      *    This contains a reference to it.
54587      * @type {Roo.View}
54588      */
54589     view : false,
54590     
54591       /**
54592      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
54593      * <pre><code>
54594
54595 layout.addxtype({
54596        xtype : 'Form',
54597        items: [ .... ]
54598    }
54599 );
54600
54601 </code></pre>
54602      * @param {Object} cfg Xtype definition of item to add.
54603      */
54604     
54605     addxtype : function(cfg) {
54606         // add form..
54607         if (cfg.xtype.match(/^Form$/)) {
54608             
54609             var el;
54610             //if (this.footer) {
54611             //    el = this.footer.container.insertSibling(false, 'before');
54612             //} else {
54613                 el = this.el.createChild();
54614             //}
54615
54616             this.form = new  Roo.form.Form(cfg);
54617             
54618             
54619             if ( this.form.allItems.length) {
54620                 this.form.render(el.dom);
54621             }
54622             return this.form;
54623         }
54624         // should only have one of theses..
54625         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
54626             // views.. should not be just added - used named prop 'view''
54627             
54628             cfg.el = this.el.appendChild(document.createElement("div"));
54629             // factory?
54630             
54631             var ret = new Roo.factory(cfg);
54632              
54633              ret.render && ret.render(false, ''); // render blank..
54634             this.view = ret;
54635             return ret;
54636         }
54637         return false;
54638     }
54639 });
54640
54641 /**
54642  * @class Roo.GridPanel
54643  * @extends Roo.ContentPanel
54644  * @constructor
54645  * Create a new GridPanel.
54646  * @param {Roo.grid.Grid} grid The grid for this panel
54647  * @param {String/Object} config A string to set only the panel's title, or a config object
54648  */
54649 Roo.GridPanel = function(grid, config){
54650     
54651   
54652     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
54653         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
54654         
54655     this.wrapper.dom.appendChild(grid.getGridEl().dom);
54656     
54657     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
54658     
54659     if(this.toolbar){
54660         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
54661     }
54662     // xtype created footer. - not sure if will work as we normally have to render first..
54663     if (this.footer && !this.footer.el && this.footer.xtype) {
54664         
54665         this.footer.container = this.grid.getView().getFooterPanel(true);
54666         this.footer.dataSource = this.grid.dataSource;
54667         this.footer = Roo.factory(this.footer, Roo);
54668         
54669     }
54670     
54671     grid.monitorWindowResize = false; // turn off autosizing
54672     grid.autoHeight = false;
54673     grid.autoWidth = false;
54674     this.grid = grid;
54675     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
54676 };
54677
54678 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
54679     getId : function(){
54680         return this.grid.id;
54681     },
54682     
54683     /**
54684      * Returns the grid for this panel
54685      * @return {Roo.grid.Grid} 
54686      */
54687     getGrid : function(){
54688         return this.grid;    
54689     },
54690     
54691     setSize : function(width, height){
54692         if(!this.ignoreResize(width, height)){
54693             var grid = this.grid;
54694             var size = this.adjustForComponents(width, height);
54695             grid.getGridEl().setSize(size.width, size.height);
54696             grid.autoSize();
54697         }
54698     },
54699     
54700     beforeSlide : function(){
54701         this.grid.getView().scroller.clip();
54702     },
54703     
54704     afterSlide : function(){
54705         this.grid.getView().scroller.unclip();
54706     },
54707     
54708     destroy : function(){
54709         this.grid.destroy();
54710         delete this.grid;
54711         Roo.GridPanel.superclass.destroy.call(this); 
54712     }
54713 });
54714
54715
54716 /**
54717  * @class Roo.NestedLayoutPanel
54718  * @extends Roo.ContentPanel
54719  * @constructor
54720  * Create a new NestedLayoutPanel.
54721  * 
54722  * 
54723  * @param {Roo.BorderLayout} layout The layout for this panel
54724  * @param {String/Object} config A string to set only the title or a config object
54725  */
54726 Roo.NestedLayoutPanel = function(layout, config)
54727 {
54728     // construct with only one argument..
54729     /* FIXME - implement nicer consturctors
54730     if (layout.layout) {
54731         config = layout;
54732         layout = config.layout;
54733         delete config.layout;
54734     }
54735     if (layout.xtype && !layout.getEl) {
54736         // then layout needs constructing..
54737         layout = Roo.factory(layout, Roo);
54738     }
54739     */
54740     
54741     
54742     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
54743     
54744     layout.monitorWindowResize = false; // turn off autosizing
54745     this.layout = layout;
54746     this.layout.getEl().addClass("x-layout-nested-layout");
54747     
54748     
54749     
54750     
54751 };
54752
54753 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
54754
54755     setSize : function(width, height){
54756         if(!this.ignoreResize(width, height)){
54757             var size = this.adjustForComponents(width, height);
54758             var el = this.layout.getEl();
54759             el.setSize(size.width, size.height);
54760             var touch = el.dom.offsetWidth;
54761             this.layout.layout();
54762             // ie requires a double layout on the first pass
54763             if(Roo.isIE && !this.initialized){
54764                 this.initialized = true;
54765                 this.layout.layout();
54766             }
54767         }
54768     },
54769     
54770     // activate all subpanels if not currently active..
54771     
54772     setActiveState : function(active){
54773         this.active = active;
54774         if(!active){
54775             this.fireEvent("deactivate", this);
54776             return;
54777         }
54778         
54779         this.fireEvent("activate", this);
54780         // not sure if this should happen before or after..
54781         if (!this.layout) {
54782             return; // should not happen..
54783         }
54784         var reg = false;
54785         for (var r in this.layout.regions) {
54786             reg = this.layout.getRegion(r);
54787             if (reg.getActivePanel()) {
54788                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
54789                 reg.setActivePanel(reg.getActivePanel());
54790                 continue;
54791             }
54792             if (!reg.panels.length) {
54793                 continue;
54794             }
54795             reg.showPanel(reg.getPanel(0));
54796         }
54797         
54798         
54799         
54800         
54801     },
54802     
54803     /**
54804      * Returns the nested BorderLayout for this panel
54805      * @return {Roo.BorderLayout} 
54806      */
54807     getLayout : function(){
54808         return this.layout;
54809     },
54810     
54811      /**
54812      * Adds a xtype elements to the layout of the nested panel
54813      * <pre><code>
54814
54815 panel.addxtype({
54816        xtype : 'ContentPanel',
54817        region: 'west',
54818        items: [ .... ]
54819    }
54820 );
54821
54822 panel.addxtype({
54823         xtype : 'NestedLayoutPanel',
54824         region: 'west',
54825         layout: {
54826            center: { },
54827            west: { }   
54828         },
54829         items : [ ... list of content panels or nested layout panels.. ]
54830    }
54831 );
54832 </code></pre>
54833      * @param {Object} cfg Xtype definition of item to add.
54834      */
54835     addxtype : function(cfg) {
54836         return this.layout.addxtype(cfg);
54837     
54838     }
54839 });
54840
54841 Roo.ScrollPanel = function(el, config, content){
54842     config = config || {};
54843     config.fitToFrame = true;
54844     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
54845     
54846     this.el.dom.style.overflow = "hidden";
54847     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
54848     this.el.removeClass("x-layout-inactive-content");
54849     this.el.on("mousewheel", this.onWheel, this);
54850
54851     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
54852     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
54853     up.unselectable(); down.unselectable();
54854     up.on("click", this.scrollUp, this);
54855     down.on("click", this.scrollDown, this);
54856     up.addClassOnOver("x-scroller-btn-over");
54857     down.addClassOnOver("x-scroller-btn-over");
54858     up.addClassOnClick("x-scroller-btn-click");
54859     down.addClassOnClick("x-scroller-btn-click");
54860     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
54861
54862     this.resizeEl = this.el;
54863     this.el = wrap; this.up = up; this.down = down;
54864 };
54865
54866 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
54867     increment : 100,
54868     wheelIncrement : 5,
54869     scrollUp : function(){
54870         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
54871     },
54872
54873     scrollDown : function(){
54874         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
54875     },
54876
54877     afterScroll : function(){
54878         var el = this.resizeEl;
54879         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
54880         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
54881         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
54882     },
54883
54884     setSize : function(){
54885         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
54886         this.afterScroll();
54887     },
54888
54889     onWheel : function(e){
54890         var d = e.getWheelDelta();
54891         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
54892         this.afterScroll();
54893         e.stopEvent();
54894     },
54895
54896     setContent : function(content, loadScripts){
54897         this.resizeEl.update(content, loadScripts);
54898     }
54899
54900 });
54901
54902
54903
54904
54905
54906
54907
54908
54909
54910 /**
54911  * @class Roo.TreePanel
54912  * @extends Roo.ContentPanel
54913  * @constructor
54914  * Create a new TreePanel. - defaults to fit/scoll contents.
54915  * @param {String/Object} config A string to set only the panel's title, or a config object
54916  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
54917  */
54918 Roo.TreePanel = function(config){
54919     var el = config.el;
54920     var tree = config.tree;
54921     delete config.tree; 
54922     delete config.el; // hopefull!
54923     
54924     // wrapper for IE7 strict & safari scroll issue
54925     
54926     var treeEl = el.createChild();
54927     config.resizeEl = treeEl;
54928     
54929     
54930     
54931     Roo.TreePanel.superclass.constructor.call(this, el, config);
54932  
54933  
54934     this.tree = new Roo.tree.TreePanel(treeEl , tree);
54935     //console.log(tree);
54936     this.on('activate', function()
54937     {
54938         if (this.tree.rendered) {
54939             return;
54940         }
54941         //console.log('render tree');
54942         this.tree.render();
54943     });
54944     // this should not be needed.. - it's actually the 'el' that resizes?
54945     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
54946     
54947     //this.on('resize',  function (cp, w, h) {
54948     //        this.tree.innerCt.setWidth(w);
54949     //        this.tree.innerCt.setHeight(h);
54950     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
54951     //});
54952
54953         
54954     
54955 };
54956
54957 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
54958     fitToFrame : true,
54959     autoScroll : true
54960 });
54961
54962
54963
54964
54965
54966
54967
54968
54969
54970
54971
54972 /*
54973  * Based on:
54974  * Ext JS Library 1.1.1
54975  * Copyright(c) 2006-2007, Ext JS, LLC.
54976  *
54977  * Originally Released Under LGPL - original licence link has changed is not relivant.
54978  *
54979  * Fork - LGPL
54980  * <script type="text/javascript">
54981  */
54982  
54983
54984 /**
54985  * @class Roo.ReaderLayout
54986  * @extends Roo.BorderLayout
54987  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
54988  * center region containing two nested regions (a top one for a list view and one for item preview below),
54989  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
54990  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
54991  * expedites the setup of the overall layout and regions for this common application style.
54992  * Example:
54993  <pre><code>
54994 var reader = new Roo.ReaderLayout();
54995 var CP = Roo.ContentPanel;  // shortcut for adding
54996
54997 reader.beginUpdate();
54998 reader.add("north", new CP("north", "North"));
54999 reader.add("west", new CP("west", {title: "West"}));
55000 reader.add("east", new CP("east", {title: "East"}));
55001
55002 reader.regions.listView.add(new CP("listView", "List"));
55003 reader.regions.preview.add(new CP("preview", "Preview"));
55004 reader.endUpdate();
55005 </code></pre>
55006 * @constructor
55007 * Create a new ReaderLayout
55008 * @param {Object} config Configuration options
55009 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
55010 * document.body if omitted)
55011 */
55012 Roo.ReaderLayout = function(config, renderTo){
55013     var c = config || {size:{}};
55014     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
55015         north: c.north !== false ? Roo.apply({
55016             split:false,
55017             initialSize: 32,
55018             titlebar: false
55019         }, c.north) : false,
55020         west: c.west !== false ? Roo.apply({
55021             split:true,
55022             initialSize: 200,
55023             minSize: 175,
55024             maxSize: 400,
55025             titlebar: true,
55026             collapsible: true,
55027             animate: true,
55028             margins:{left:5,right:0,bottom:5,top:5},
55029             cmargins:{left:5,right:5,bottom:5,top:5}
55030         }, c.west) : false,
55031         east: c.east !== false ? Roo.apply({
55032             split:true,
55033             initialSize: 200,
55034             minSize: 175,
55035             maxSize: 400,
55036             titlebar: true,
55037             collapsible: true,
55038             animate: true,
55039             margins:{left:0,right:5,bottom:5,top:5},
55040             cmargins:{left:5,right:5,bottom:5,top:5}
55041         }, c.east) : false,
55042         center: Roo.apply({
55043             tabPosition: 'top',
55044             autoScroll:false,
55045             closeOnTab: true,
55046             titlebar:false,
55047             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
55048         }, c.center)
55049     });
55050
55051     this.el.addClass('x-reader');
55052
55053     this.beginUpdate();
55054
55055     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
55056         south: c.preview !== false ? Roo.apply({
55057             split:true,
55058             initialSize: 200,
55059             minSize: 100,
55060             autoScroll:true,
55061             collapsible:true,
55062             titlebar: true,
55063             cmargins:{top:5,left:0, right:0, bottom:0}
55064         }, c.preview) : false,
55065         center: Roo.apply({
55066             autoScroll:false,
55067             titlebar:false,
55068             minHeight:200
55069         }, c.listView)
55070     });
55071     this.add('center', new Roo.NestedLayoutPanel(inner,
55072             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
55073
55074     this.endUpdate();
55075
55076     this.regions.preview = inner.getRegion('south');
55077     this.regions.listView = inner.getRegion('center');
55078 };
55079
55080 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
55081  * Based on:
55082  * Ext JS Library 1.1.1
55083  * Copyright(c) 2006-2007, Ext JS, LLC.
55084  *
55085  * Originally Released Under LGPL - original licence link has changed is not relivant.
55086  *
55087  * Fork - LGPL
55088  * <script type="text/javascript">
55089  */
55090  
55091 /**
55092  * @class Roo.grid.Grid
55093  * @extends Roo.util.Observable
55094  * This class represents the primary interface of a component based grid control.
55095  * <br><br>Usage:<pre><code>
55096  var grid = new Roo.grid.Grid("my-container-id", {
55097      ds: myDataStore,
55098      cm: myColModel,
55099      selModel: mySelectionModel,
55100      autoSizeColumns: true,
55101      monitorWindowResize: false,
55102      trackMouseOver: true
55103  });
55104  // set any options
55105  grid.render();
55106  * </code></pre>
55107  * <b>Common Problems:</b><br/>
55108  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
55109  * element will correct this<br/>
55110  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
55111  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
55112  * are unpredictable.<br/>
55113  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
55114  * grid to calculate dimensions/offsets.<br/>
55115   * @constructor
55116  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
55117  * The container MUST have some type of size defined for the grid to fill. The container will be
55118  * automatically set to position relative if it isn't already.
55119  * @param {Object} config A config object that sets properties on this grid.
55120  */
55121 Roo.grid.Grid = function(container, config){
55122         // initialize the container
55123         this.container = Roo.get(container);
55124         this.container.update("");
55125         this.container.setStyle("overflow", "hidden");
55126     this.container.addClass('x-grid-container');
55127
55128     this.id = this.container.id;
55129
55130     Roo.apply(this, config);
55131     // check and correct shorthanded configs
55132     if(this.ds){
55133         this.dataSource = this.ds;
55134         delete this.ds;
55135     }
55136     if(this.cm){
55137         this.colModel = this.cm;
55138         delete this.cm;
55139     }
55140     if(this.sm){
55141         this.selModel = this.sm;
55142         delete this.sm;
55143     }
55144
55145     if (this.selModel) {
55146         this.selModel = Roo.factory(this.selModel, Roo.grid);
55147         this.sm = this.selModel;
55148         this.sm.xmodule = this.xmodule || false;
55149     }
55150     if (typeof(this.colModel.config) == 'undefined') {
55151         this.colModel = new Roo.grid.ColumnModel(this.colModel);
55152         this.cm = this.colModel;
55153         this.cm.xmodule = this.xmodule || false;
55154     }
55155     if (this.dataSource) {
55156         this.dataSource= Roo.factory(this.dataSource, Roo.data);
55157         this.ds = this.dataSource;
55158         this.ds.xmodule = this.xmodule || false;
55159          
55160     }
55161     
55162     
55163     
55164     if(this.width){
55165         this.container.setWidth(this.width);
55166     }
55167
55168     if(this.height){
55169         this.container.setHeight(this.height);
55170     }
55171     /** @private */
55172         this.addEvents({
55173         // raw events
55174         /**
55175          * @event click
55176          * The raw click event for the entire grid.
55177          * @param {Roo.EventObject} e
55178          */
55179         "click" : true,
55180         /**
55181          * @event dblclick
55182          * The raw dblclick event for the entire grid.
55183          * @param {Roo.EventObject} e
55184          */
55185         "dblclick" : true,
55186         /**
55187          * @event contextmenu
55188          * The raw contextmenu event for the entire grid.
55189          * @param {Roo.EventObject} e
55190          */
55191         "contextmenu" : true,
55192         /**
55193          * @event mousedown
55194          * The raw mousedown event for the entire grid.
55195          * @param {Roo.EventObject} e
55196          */
55197         "mousedown" : true,
55198         /**
55199          * @event mouseup
55200          * The raw mouseup event for the entire grid.
55201          * @param {Roo.EventObject} e
55202          */
55203         "mouseup" : true,
55204         /**
55205          * @event mouseover
55206          * The raw mouseover event for the entire grid.
55207          * @param {Roo.EventObject} e
55208          */
55209         "mouseover" : true,
55210         /**
55211          * @event mouseout
55212          * The raw mouseout event for the entire grid.
55213          * @param {Roo.EventObject} e
55214          */
55215         "mouseout" : true,
55216         /**
55217          * @event keypress
55218          * The raw keypress event for the entire grid.
55219          * @param {Roo.EventObject} e
55220          */
55221         "keypress" : true,
55222         /**
55223          * @event keydown
55224          * The raw keydown event for the entire grid.
55225          * @param {Roo.EventObject} e
55226          */
55227         "keydown" : true,
55228
55229         // custom events
55230
55231         /**
55232          * @event cellclick
55233          * Fires when a cell is clicked
55234          * @param {Grid} this
55235          * @param {Number} rowIndex
55236          * @param {Number} columnIndex
55237          * @param {Roo.EventObject} e
55238          */
55239         "cellclick" : true,
55240         /**
55241          * @event celldblclick
55242          * Fires when a cell is double clicked
55243          * @param {Grid} this
55244          * @param {Number} rowIndex
55245          * @param {Number} columnIndex
55246          * @param {Roo.EventObject} e
55247          */
55248         "celldblclick" : true,
55249         /**
55250          * @event rowclick
55251          * Fires when a row is clicked
55252          * @param {Grid} this
55253          * @param {Number} rowIndex
55254          * @param {Roo.EventObject} e
55255          */
55256         "rowclick" : true,
55257         /**
55258          * @event rowdblclick
55259          * Fires when a row is double clicked
55260          * @param {Grid} this
55261          * @param {Number} rowIndex
55262          * @param {Roo.EventObject} e
55263          */
55264         "rowdblclick" : true,
55265         /**
55266          * @event headerclick
55267          * Fires when a header is clicked
55268          * @param {Grid} this
55269          * @param {Number} columnIndex
55270          * @param {Roo.EventObject} e
55271          */
55272         "headerclick" : true,
55273         /**
55274          * @event headerdblclick
55275          * Fires when a header cell is double clicked
55276          * @param {Grid} this
55277          * @param {Number} columnIndex
55278          * @param {Roo.EventObject} e
55279          */
55280         "headerdblclick" : true,
55281         /**
55282          * @event rowcontextmenu
55283          * Fires when a row is right clicked
55284          * @param {Grid} this
55285          * @param {Number} rowIndex
55286          * @param {Roo.EventObject} e
55287          */
55288         "rowcontextmenu" : true,
55289         /**
55290          * @event cellcontextmenu
55291          * Fires when a cell is right clicked
55292          * @param {Grid} this
55293          * @param {Number} rowIndex
55294          * @param {Number} cellIndex
55295          * @param {Roo.EventObject} e
55296          */
55297          "cellcontextmenu" : true,
55298         /**
55299          * @event headercontextmenu
55300          * Fires when a header is right clicked
55301          * @param {Grid} this
55302          * @param {Number} columnIndex
55303          * @param {Roo.EventObject} e
55304          */
55305         "headercontextmenu" : true,
55306         /**
55307          * @event bodyscroll
55308          * Fires when the body element is scrolled
55309          * @param {Number} scrollLeft
55310          * @param {Number} scrollTop
55311          */
55312         "bodyscroll" : true,
55313         /**
55314          * @event columnresize
55315          * Fires when the user resizes a column
55316          * @param {Number} columnIndex
55317          * @param {Number} newSize
55318          */
55319         "columnresize" : true,
55320         /**
55321          * @event columnmove
55322          * Fires when the user moves a column
55323          * @param {Number} oldIndex
55324          * @param {Number} newIndex
55325          */
55326         "columnmove" : true,
55327         /**
55328          * @event startdrag
55329          * Fires when row(s) start being dragged
55330          * @param {Grid} this
55331          * @param {Roo.GridDD} dd The drag drop object
55332          * @param {event} e The raw browser event
55333          */
55334         "startdrag" : true,
55335         /**
55336          * @event enddrag
55337          * Fires when a drag operation is complete
55338          * @param {Grid} this
55339          * @param {Roo.GridDD} dd The drag drop object
55340          * @param {event} e The raw browser event
55341          */
55342         "enddrag" : true,
55343         /**
55344          * @event dragdrop
55345          * Fires when dragged row(s) are dropped on a valid DD target
55346          * @param {Grid} this
55347          * @param {Roo.GridDD} dd The drag drop object
55348          * @param {String} targetId The target drag drop object
55349          * @param {event} e The raw browser event
55350          */
55351         "dragdrop" : true,
55352         /**
55353          * @event dragover
55354          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
55355          * @param {Grid} this
55356          * @param {Roo.GridDD} dd The drag drop object
55357          * @param {String} targetId The target drag drop object
55358          * @param {event} e The raw browser event
55359          */
55360         "dragover" : true,
55361         /**
55362          * @event dragenter
55363          *  Fires when the dragged row(s) first cross another DD target while being dragged
55364          * @param {Grid} this
55365          * @param {Roo.GridDD} dd The drag drop object
55366          * @param {String} targetId The target drag drop object
55367          * @param {event} e The raw browser event
55368          */
55369         "dragenter" : true,
55370         /**
55371          * @event dragout
55372          * Fires when the dragged row(s) leave another DD target while being dragged
55373          * @param {Grid} this
55374          * @param {Roo.GridDD} dd The drag drop object
55375          * @param {String} targetId The target drag drop object
55376          * @param {event} e The raw browser event
55377          */
55378         "dragout" : true,
55379         /**
55380          * @event rowclass
55381          * Fires when a row is rendered, so you can change add a style to it.
55382          * @param {GridView} gridview   The grid view
55383          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
55384          */
55385         'rowclass' : true,
55386
55387         /**
55388          * @event render
55389          * Fires when the grid is rendered
55390          * @param {Grid} grid
55391          */
55392         'render' : true
55393     });
55394
55395     Roo.grid.Grid.superclass.constructor.call(this);
55396 };
55397 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
55398     
55399     /**
55400      * @cfg {String} ddGroup - drag drop group.
55401      */
55402
55403     /**
55404      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
55405      */
55406     minColumnWidth : 25,
55407
55408     /**
55409      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
55410      * <b>on initial render.</b> It is more efficient to explicitly size the columns
55411      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
55412      */
55413     autoSizeColumns : false,
55414
55415     /**
55416      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
55417      */
55418     autoSizeHeaders : true,
55419
55420     /**
55421      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
55422      */
55423     monitorWindowResize : true,
55424
55425     /**
55426      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
55427      * rows measured to get a columns size. Default is 0 (all rows).
55428      */
55429     maxRowsToMeasure : 0,
55430
55431     /**
55432      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
55433      */
55434     trackMouseOver : true,
55435
55436     /**
55437     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
55438     */
55439     
55440     /**
55441     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
55442     */
55443     enableDragDrop : false,
55444     
55445     /**
55446     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
55447     */
55448     enableColumnMove : true,
55449     
55450     /**
55451     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
55452     */
55453     enableColumnHide : true,
55454     
55455     /**
55456     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
55457     */
55458     enableRowHeightSync : false,
55459     
55460     /**
55461     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
55462     */
55463     stripeRows : true,
55464     
55465     /**
55466     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
55467     */
55468     autoHeight : false,
55469
55470     /**
55471      * @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.
55472      */
55473     autoExpandColumn : false,
55474
55475     /**
55476     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
55477     * Default is 50.
55478     */
55479     autoExpandMin : 50,
55480
55481     /**
55482     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
55483     */
55484     autoExpandMax : 1000,
55485
55486     /**
55487     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
55488     */
55489     view : null,
55490
55491     /**
55492     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
55493     */
55494     loadMask : false,
55495     /**
55496     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
55497     */
55498     dropTarget: false,
55499     
55500    
55501     
55502     // private
55503     rendered : false,
55504
55505     /**
55506     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
55507     * of a fixed width. Default is false.
55508     */
55509     /**
55510     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
55511     */
55512     /**
55513      * Called once after all setup has been completed and the grid is ready to be rendered.
55514      * @return {Roo.grid.Grid} this
55515      */
55516     render : function()
55517     {
55518         var c = this.container;
55519         // try to detect autoHeight/width mode
55520         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
55521             this.autoHeight = true;
55522         }
55523         var view = this.getView();
55524         view.init(this);
55525
55526         c.on("click", this.onClick, this);
55527         c.on("dblclick", this.onDblClick, this);
55528         c.on("contextmenu", this.onContextMenu, this);
55529         c.on("keydown", this.onKeyDown, this);
55530         if (Roo.isTouch) {
55531             c.on("touchstart", this.onTouchStart, this);
55532         }
55533
55534         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
55535
55536         this.getSelectionModel().init(this);
55537
55538         view.render();
55539
55540         if(this.loadMask){
55541             this.loadMask = new Roo.LoadMask(this.container,
55542                     Roo.apply({store:this.dataSource}, this.loadMask));
55543         }
55544         
55545         
55546         if (this.toolbar && this.toolbar.xtype) {
55547             this.toolbar.container = this.getView().getHeaderPanel(true);
55548             this.toolbar = new Roo.Toolbar(this.toolbar);
55549         }
55550         if (this.footer && this.footer.xtype) {
55551             this.footer.dataSource = this.getDataSource();
55552             this.footer.container = this.getView().getFooterPanel(true);
55553             this.footer = Roo.factory(this.footer, Roo);
55554         }
55555         if (this.dropTarget && this.dropTarget.xtype) {
55556             delete this.dropTarget.xtype;
55557             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
55558         }
55559         
55560         
55561         this.rendered = true;
55562         this.fireEvent('render', this);
55563         return this;
55564     },
55565
55566     /**
55567      * Reconfigures the grid to use a different Store and Column Model.
55568      * The View will be bound to the new objects and refreshed.
55569      * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
55570      * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
55571      */
55572     reconfigure : function(dataSource, colModel){
55573         if(this.loadMask){
55574             this.loadMask.destroy();
55575             this.loadMask = new Roo.LoadMask(this.container,
55576                     Roo.apply({store:dataSource}, this.loadMask));
55577         }
55578         this.view.bind(dataSource, colModel);
55579         this.dataSource = dataSource;
55580         this.colModel = colModel;
55581         this.view.refresh(true);
55582     },
55583     /**
55584      * addColumns
55585      * Add's a column, default at the end..
55586      
55587      * @param {int} position to add (default end)
55588      * @param {Array} of objects of column configuration see {@link Roo.grid.ColumnModel} 
55589      */
55590     addColumns : function(pos, ar)
55591     {
55592         
55593         for (var i =0;i< ar.length;i++) {
55594             var cfg = ar[i];
55595             cfg.id = typeof(cfg.id) == 'undefined' ? Roo.id() : cfg.id; // don't normally use this..
55596             this.cm.lookup[cfg.id] = cfg;
55597         }
55598         
55599         
55600         if (typeof(pos) == 'undefined' || pos >= this.cm.config.length) {
55601             pos = this.cm.config.length; //this.cm.config.push(cfg);
55602         } 
55603         pos = Math.max(0,pos);
55604         ar.unshift(0);
55605         ar.unshift(pos);
55606         this.cm.config.splice.apply(this.cm.config, ar);
55607         
55608         
55609         
55610         this.view.generateRules(this.cm);
55611         this.view.refresh(true);
55612         
55613     },
55614     
55615     
55616     
55617     
55618     // private
55619     onKeyDown : function(e){
55620         this.fireEvent("keydown", e);
55621     },
55622
55623     /**
55624      * Destroy this grid.
55625      * @param {Boolean} removeEl True to remove the element
55626      */
55627     destroy : function(removeEl, keepListeners){
55628         if(this.loadMask){
55629             this.loadMask.destroy();
55630         }
55631         var c = this.container;
55632         c.removeAllListeners();
55633         this.view.destroy();
55634         this.colModel.purgeListeners();
55635         if(!keepListeners){
55636             this.purgeListeners();
55637         }
55638         c.update("");
55639         if(removeEl === true){
55640             c.remove();
55641         }
55642     },
55643
55644     // private
55645     processEvent : function(name, e){
55646         // does this fire select???
55647         //Roo.log('grid:processEvent '  + name);
55648         
55649         if (name != 'touchstart' ) {
55650             this.fireEvent(name, e);    
55651         }
55652         
55653         var t = e.getTarget();
55654         var v = this.view;
55655         var header = v.findHeaderIndex(t);
55656         if(header !== false){
55657             var ename = name == 'touchstart' ? 'click' : name;
55658              
55659             this.fireEvent("header" + ename, this, header, e);
55660         }else{
55661             var row = v.findRowIndex(t);
55662             var cell = v.findCellIndex(t);
55663             if (name == 'touchstart') {
55664                 // first touch is always a click.
55665                 // hopefull this happens after selection is updated.?
55666                 name = false;
55667                 
55668                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
55669                     var cs = this.selModel.getSelectedCell();
55670                     if (row == cs[0] && cell == cs[1]){
55671                         name = 'dblclick';
55672                     }
55673                 }
55674                 if (typeof(this.selModel.getSelections) != 'undefined') {
55675                     var cs = this.selModel.getSelections();
55676                     var ds = this.dataSource;
55677                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
55678                         name = 'dblclick';
55679                     }
55680                 }
55681                 if (!name) {
55682                     return;
55683                 }
55684             }
55685             
55686             
55687             if(row !== false){
55688                 this.fireEvent("row" + name, this, row, e);
55689                 if(cell !== false){
55690                     this.fireEvent("cell" + name, this, row, cell, e);
55691                 }
55692             }
55693         }
55694     },
55695
55696     // private
55697     onClick : function(e){
55698         this.processEvent("click", e);
55699     },
55700    // private
55701     onTouchStart : function(e){
55702         this.processEvent("touchstart", e);
55703     },
55704
55705     // private
55706     onContextMenu : function(e, t){
55707         this.processEvent("contextmenu", e);
55708     },
55709
55710     // private
55711     onDblClick : function(e){
55712         this.processEvent("dblclick", e);
55713     },
55714
55715     // private
55716     walkCells : function(row, col, step, fn, scope){
55717         var cm = this.colModel, clen = cm.getColumnCount();
55718         var ds = this.dataSource, rlen = ds.getCount(), first = true;
55719         if(step < 0){
55720             if(col < 0){
55721                 row--;
55722                 first = false;
55723             }
55724             while(row >= 0){
55725                 if(!first){
55726                     col = clen-1;
55727                 }
55728                 first = false;
55729                 while(col >= 0){
55730                     if(fn.call(scope || this, row, col, cm) === true){
55731                         return [row, col];
55732                     }
55733                     col--;
55734                 }
55735                 row--;
55736             }
55737         } else {
55738             if(col >= clen){
55739                 row++;
55740                 first = false;
55741             }
55742             while(row < rlen){
55743                 if(!first){
55744                     col = 0;
55745                 }
55746                 first = false;
55747                 while(col < clen){
55748                     if(fn.call(scope || this, row, col, cm) === true){
55749                         return [row, col];
55750                     }
55751                     col++;
55752                 }
55753                 row++;
55754             }
55755         }
55756         return null;
55757     },
55758
55759     // private
55760     getSelections : function(){
55761         return this.selModel.getSelections();
55762     },
55763
55764     /**
55765      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
55766      * but if manual update is required this method will initiate it.
55767      */
55768     autoSize : function(){
55769         if(this.rendered){
55770             this.view.layout();
55771             if(this.view.adjustForScroll){
55772                 this.view.adjustForScroll();
55773             }
55774         }
55775     },
55776
55777     /**
55778      * Returns the grid's underlying element.
55779      * @return {Element} The element
55780      */
55781     getGridEl : function(){
55782         return this.container;
55783     },
55784
55785     // private for compatibility, overridden by editor grid
55786     stopEditing : function(){},
55787
55788     /**
55789      * Returns the grid's SelectionModel.
55790      * @return {SelectionModel}
55791      */
55792     getSelectionModel : function(){
55793         if(!this.selModel){
55794             this.selModel = new Roo.grid.RowSelectionModel();
55795         }
55796         return this.selModel;
55797     },
55798
55799     /**
55800      * Returns the grid's DataSource.
55801      * @return {DataSource}
55802      */
55803     getDataSource : function(){
55804         return this.dataSource;
55805     },
55806
55807     /**
55808      * Returns the grid's ColumnModel.
55809      * @return {ColumnModel}
55810      */
55811     getColumnModel : function(){
55812         return this.colModel;
55813     },
55814
55815     /**
55816      * Returns the grid's GridView object.
55817      * @return {GridView}
55818      */
55819     getView : function(){
55820         if(!this.view){
55821             this.view = new Roo.grid.GridView(this.viewConfig);
55822         }
55823         return this.view;
55824     },
55825     /**
55826      * Called to get grid's drag proxy text, by default returns this.ddText.
55827      * @return {String}
55828      */
55829     getDragDropText : function(){
55830         var count = this.selModel.getCount();
55831         return String.format(this.ddText, count, count == 1 ? '' : 's');
55832     }
55833 });
55834 /**
55835  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
55836  * %0 is replaced with the number of selected rows.
55837  * @type String
55838  */
55839 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
55840  * Based on:
55841  * Ext JS Library 1.1.1
55842  * Copyright(c) 2006-2007, Ext JS, LLC.
55843  *
55844  * Originally Released Under LGPL - original licence link has changed is not relivant.
55845  *
55846  * Fork - LGPL
55847  * <script type="text/javascript">
55848  */
55849  
55850 Roo.grid.AbstractGridView = function(){
55851         this.grid = null;
55852         
55853         this.events = {
55854             "beforerowremoved" : true,
55855             "beforerowsinserted" : true,
55856             "beforerefresh" : true,
55857             "rowremoved" : true,
55858             "rowsinserted" : true,
55859             "rowupdated" : true,
55860             "refresh" : true
55861         };
55862     Roo.grid.AbstractGridView.superclass.constructor.call(this);
55863 };
55864
55865 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
55866     rowClass : "x-grid-row",
55867     cellClass : "x-grid-cell",
55868     tdClass : "x-grid-td",
55869     hdClass : "x-grid-hd",
55870     splitClass : "x-grid-hd-split",
55871     
55872     init: function(grid){
55873         this.grid = grid;
55874                 var cid = this.grid.getGridEl().id;
55875         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
55876         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
55877         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
55878         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
55879         },
55880         
55881     getColumnRenderers : function(){
55882         var renderers = [];
55883         var cm = this.grid.colModel;
55884         var colCount = cm.getColumnCount();
55885         for(var i = 0; i < colCount; i++){
55886             renderers[i] = cm.getRenderer(i);
55887         }
55888         return renderers;
55889     },
55890     
55891     getColumnIds : function(){
55892         var ids = [];
55893         var cm = this.grid.colModel;
55894         var colCount = cm.getColumnCount();
55895         for(var i = 0; i < colCount; i++){
55896             ids[i] = cm.getColumnId(i);
55897         }
55898         return ids;
55899     },
55900     
55901     getDataIndexes : function(){
55902         if(!this.indexMap){
55903             this.indexMap = this.buildIndexMap();
55904         }
55905         return this.indexMap.colToData;
55906     },
55907     
55908     getColumnIndexByDataIndex : function(dataIndex){
55909         if(!this.indexMap){
55910             this.indexMap = this.buildIndexMap();
55911         }
55912         return this.indexMap.dataToCol[dataIndex];
55913     },
55914     
55915     /**
55916      * Set a css style for a column dynamically. 
55917      * @param {Number} colIndex The index of the column
55918      * @param {String} name The css property name
55919      * @param {String} value The css value
55920      */
55921     setCSSStyle : function(colIndex, name, value){
55922         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
55923         Roo.util.CSS.updateRule(selector, name, value);
55924     },
55925     
55926     generateRules : function(cm){
55927         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
55928         Roo.util.CSS.removeStyleSheet(rulesId);
55929         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
55930             var cid = cm.getColumnId(i);
55931             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
55932                          this.tdSelector, cid, " {\n}\n",
55933                          this.hdSelector, cid, " {\n}\n",
55934                          this.splitSelector, cid, " {\n}\n");
55935         }
55936         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
55937     }
55938 });/*
55939  * Based on:
55940  * Ext JS Library 1.1.1
55941  * Copyright(c) 2006-2007, Ext JS, LLC.
55942  *
55943  * Originally Released Under LGPL - original licence link has changed is not relivant.
55944  *
55945  * Fork - LGPL
55946  * <script type="text/javascript">
55947  */
55948
55949 // private
55950 // This is a support class used internally by the Grid components
55951 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
55952     this.grid = grid;
55953     this.view = grid.getView();
55954     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
55955     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
55956     if(hd2){
55957         this.setHandleElId(Roo.id(hd));
55958         this.setOuterHandleElId(Roo.id(hd2));
55959     }
55960     this.scroll = false;
55961 };
55962 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
55963     maxDragWidth: 120,
55964     getDragData : function(e){
55965         var t = Roo.lib.Event.getTarget(e);
55966         var h = this.view.findHeaderCell(t);
55967         if(h){
55968             return {ddel: h.firstChild, header:h};
55969         }
55970         return false;
55971     },
55972
55973     onInitDrag : function(e){
55974         this.view.headersDisabled = true;
55975         var clone = this.dragData.ddel.cloneNode(true);
55976         clone.id = Roo.id();
55977         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
55978         this.proxy.update(clone);
55979         return true;
55980     },
55981
55982     afterValidDrop : function(){
55983         var v = this.view;
55984         setTimeout(function(){
55985             v.headersDisabled = false;
55986         }, 50);
55987     },
55988
55989     afterInvalidDrop : function(){
55990         var v = this.view;
55991         setTimeout(function(){
55992             v.headersDisabled = false;
55993         }, 50);
55994     }
55995 });
55996 /*
55997  * Based on:
55998  * Ext JS Library 1.1.1
55999  * Copyright(c) 2006-2007, Ext JS, LLC.
56000  *
56001  * Originally Released Under LGPL - original licence link has changed is not relivant.
56002  *
56003  * Fork - LGPL
56004  * <script type="text/javascript">
56005  */
56006 // private
56007 // This is a support class used internally by the Grid components
56008 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
56009     this.grid = grid;
56010     this.view = grid.getView();
56011     // split the proxies so they don't interfere with mouse events
56012     this.proxyTop = Roo.DomHelper.append(document.body, {
56013         cls:"col-move-top", html:"&#160;"
56014     }, true);
56015     this.proxyBottom = Roo.DomHelper.append(document.body, {
56016         cls:"col-move-bottom", html:"&#160;"
56017     }, true);
56018     this.proxyTop.hide = this.proxyBottom.hide = function(){
56019         this.setLeftTop(-100,-100);
56020         this.setStyle("visibility", "hidden");
56021     };
56022     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
56023     // temporarily disabled
56024     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
56025     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
56026 };
56027 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
56028     proxyOffsets : [-4, -9],
56029     fly: Roo.Element.fly,
56030
56031     getTargetFromEvent : function(e){
56032         var t = Roo.lib.Event.getTarget(e);
56033         var cindex = this.view.findCellIndex(t);
56034         if(cindex !== false){
56035             return this.view.getHeaderCell(cindex);
56036         }
56037         return null;
56038     },
56039
56040     nextVisible : function(h){
56041         var v = this.view, cm = this.grid.colModel;
56042         h = h.nextSibling;
56043         while(h){
56044             if(!cm.isHidden(v.getCellIndex(h))){
56045                 return h;
56046             }
56047             h = h.nextSibling;
56048         }
56049         return null;
56050     },
56051
56052     prevVisible : function(h){
56053         var v = this.view, cm = this.grid.colModel;
56054         h = h.prevSibling;
56055         while(h){
56056             if(!cm.isHidden(v.getCellIndex(h))){
56057                 return h;
56058             }
56059             h = h.prevSibling;
56060         }
56061         return null;
56062     },
56063
56064     positionIndicator : function(h, n, e){
56065         var x = Roo.lib.Event.getPageX(e);
56066         var r = Roo.lib.Dom.getRegion(n.firstChild);
56067         var px, pt, py = r.top + this.proxyOffsets[1];
56068         if((r.right - x) <= (r.right-r.left)/2){
56069             px = r.right+this.view.borderWidth;
56070             pt = "after";
56071         }else{
56072             px = r.left;
56073             pt = "before";
56074         }
56075         var oldIndex = this.view.getCellIndex(h);
56076         var newIndex = this.view.getCellIndex(n);
56077
56078         if(this.grid.colModel.isFixed(newIndex)){
56079             return false;
56080         }
56081
56082         var locked = this.grid.colModel.isLocked(newIndex);
56083
56084         if(pt == "after"){
56085             newIndex++;
56086         }
56087         if(oldIndex < newIndex){
56088             newIndex--;
56089         }
56090         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
56091             return false;
56092         }
56093         px +=  this.proxyOffsets[0];
56094         this.proxyTop.setLeftTop(px, py);
56095         this.proxyTop.show();
56096         if(!this.bottomOffset){
56097             this.bottomOffset = this.view.mainHd.getHeight();
56098         }
56099         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
56100         this.proxyBottom.show();
56101         return pt;
56102     },
56103
56104     onNodeEnter : function(n, dd, e, data){
56105         if(data.header != n){
56106             this.positionIndicator(data.header, n, e);
56107         }
56108     },
56109
56110     onNodeOver : function(n, dd, e, data){
56111         var result = false;
56112         if(data.header != n){
56113             result = this.positionIndicator(data.header, n, e);
56114         }
56115         if(!result){
56116             this.proxyTop.hide();
56117             this.proxyBottom.hide();
56118         }
56119         return result ? this.dropAllowed : this.dropNotAllowed;
56120     },
56121
56122     onNodeOut : function(n, dd, e, data){
56123         this.proxyTop.hide();
56124         this.proxyBottom.hide();
56125     },
56126
56127     onNodeDrop : function(n, dd, e, data){
56128         var h = data.header;
56129         if(h != n){
56130             var cm = this.grid.colModel;
56131             var x = Roo.lib.Event.getPageX(e);
56132             var r = Roo.lib.Dom.getRegion(n.firstChild);
56133             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
56134             var oldIndex = this.view.getCellIndex(h);
56135             var newIndex = this.view.getCellIndex(n);
56136             var locked = cm.isLocked(newIndex);
56137             if(pt == "after"){
56138                 newIndex++;
56139             }
56140             if(oldIndex < newIndex){
56141                 newIndex--;
56142             }
56143             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
56144                 return false;
56145             }
56146             cm.setLocked(oldIndex, locked, true);
56147             cm.moveColumn(oldIndex, newIndex);
56148             this.grid.fireEvent("columnmove", oldIndex, newIndex);
56149             return true;
56150         }
56151         return false;
56152     }
56153 });
56154 /*
56155  * Based on:
56156  * Ext JS Library 1.1.1
56157  * Copyright(c) 2006-2007, Ext JS, LLC.
56158  *
56159  * Originally Released Under LGPL - original licence link has changed is not relivant.
56160  *
56161  * Fork - LGPL
56162  * <script type="text/javascript">
56163  */
56164   
56165 /**
56166  * @class Roo.grid.GridView
56167  * @extends Roo.util.Observable
56168  *
56169  * @constructor
56170  * @param {Object} config
56171  */
56172 Roo.grid.GridView = function(config){
56173     Roo.grid.GridView.superclass.constructor.call(this);
56174     this.el = null;
56175
56176     Roo.apply(this, config);
56177 };
56178
56179 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
56180
56181     unselectable :  'unselectable="on"',
56182     unselectableCls :  'x-unselectable',
56183     
56184     
56185     rowClass : "x-grid-row",
56186
56187     cellClass : "x-grid-col",
56188
56189     tdClass : "x-grid-td",
56190
56191     hdClass : "x-grid-hd",
56192
56193     splitClass : "x-grid-split",
56194
56195     sortClasses : ["sort-asc", "sort-desc"],
56196
56197     enableMoveAnim : false,
56198
56199     hlColor: "C3DAF9",
56200
56201     dh : Roo.DomHelper,
56202
56203     fly : Roo.Element.fly,
56204
56205     css : Roo.util.CSS,
56206
56207     borderWidth: 1,
56208
56209     splitOffset: 3,
56210
56211     scrollIncrement : 22,
56212
56213     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
56214
56215     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
56216
56217     bind : function(ds, cm){
56218         if(this.ds){
56219             this.ds.un("load", this.onLoad, this);
56220             this.ds.un("datachanged", this.onDataChange, this);
56221             this.ds.un("add", this.onAdd, this);
56222             this.ds.un("remove", this.onRemove, this);
56223             this.ds.un("update", this.onUpdate, this);
56224             this.ds.un("clear", this.onClear, this);
56225         }
56226         if(ds){
56227             ds.on("load", this.onLoad, this);
56228             ds.on("datachanged", this.onDataChange, this);
56229             ds.on("add", this.onAdd, this);
56230             ds.on("remove", this.onRemove, this);
56231             ds.on("update", this.onUpdate, this);
56232             ds.on("clear", this.onClear, this);
56233         }
56234         this.ds = ds;
56235
56236         if(this.cm){
56237             this.cm.un("widthchange", this.onColWidthChange, this);
56238             this.cm.un("headerchange", this.onHeaderChange, this);
56239             this.cm.un("hiddenchange", this.onHiddenChange, this);
56240             this.cm.un("columnmoved", this.onColumnMove, this);
56241             this.cm.un("columnlockchange", this.onColumnLock, this);
56242         }
56243         if(cm){
56244             this.generateRules(cm);
56245             cm.on("widthchange", this.onColWidthChange, this);
56246             cm.on("headerchange", this.onHeaderChange, this);
56247             cm.on("hiddenchange", this.onHiddenChange, this);
56248             cm.on("columnmoved", this.onColumnMove, this);
56249             cm.on("columnlockchange", this.onColumnLock, this);
56250         }
56251         this.cm = cm;
56252     },
56253
56254     init: function(grid){
56255         Roo.grid.GridView.superclass.init.call(this, grid);
56256
56257         this.bind(grid.dataSource, grid.colModel);
56258
56259         grid.on("headerclick", this.handleHeaderClick, this);
56260
56261         if(grid.trackMouseOver){
56262             grid.on("mouseover", this.onRowOver, this);
56263             grid.on("mouseout", this.onRowOut, this);
56264         }
56265         grid.cancelTextSelection = function(){};
56266         this.gridId = grid.id;
56267
56268         var tpls = this.templates || {};
56269
56270         if(!tpls.master){
56271             tpls.master = new Roo.Template(
56272                '<div class="x-grid" hidefocus="true">',
56273                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
56274                   '<div class="x-grid-topbar"></div>',
56275                   '<div class="x-grid-scroller"><div></div></div>',
56276                   '<div class="x-grid-locked">',
56277                       '<div class="x-grid-header">{lockedHeader}</div>',
56278                       '<div class="x-grid-body">{lockedBody}</div>',
56279                   "</div>",
56280                   '<div class="x-grid-viewport">',
56281                       '<div class="x-grid-header">{header}</div>',
56282                       '<div class="x-grid-body">{body}</div>',
56283                   "</div>",
56284                   '<div class="x-grid-bottombar"></div>',
56285                  
56286                   '<div class="x-grid-resize-proxy">&#160;</div>',
56287                "</div>"
56288             );
56289             tpls.master.disableformats = true;
56290         }
56291
56292         if(!tpls.header){
56293             tpls.header = new Roo.Template(
56294                '<table border="0" cellspacing="0" cellpadding="0">',
56295                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
56296                "</table>{splits}"
56297             );
56298             tpls.header.disableformats = true;
56299         }
56300         tpls.header.compile();
56301
56302         if(!tpls.hcell){
56303             tpls.hcell = new Roo.Template(
56304                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
56305                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
56306                 "</div></td>"
56307              );
56308              tpls.hcell.disableFormats = true;
56309         }
56310         tpls.hcell.compile();
56311
56312         if(!tpls.hsplit){
56313             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
56314                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
56315             tpls.hsplit.disableFormats = true;
56316         }
56317         tpls.hsplit.compile();
56318
56319         if(!tpls.body){
56320             tpls.body = new Roo.Template(
56321                '<table border="0" cellspacing="0" cellpadding="0">',
56322                "<tbody>{rows}</tbody>",
56323                "</table>"
56324             );
56325             tpls.body.disableFormats = true;
56326         }
56327         tpls.body.compile();
56328
56329         if(!tpls.row){
56330             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
56331             tpls.row.disableFormats = true;
56332         }
56333         tpls.row.compile();
56334
56335         if(!tpls.cell){
56336             tpls.cell = new Roo.Template(
56337                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
56338                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
56339                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
56340                 "</td>"
56341             );
56342             tpls.cell.disableFormats = true;
56343         }
56344         tpls.cell.compile();
56345
56346         this.templates = tpls;
56347     },
56348
56349     // remap these for backwards compat
56350     onColWidthChange : function(){
56351         this.updateColumns.apply(this, arguments);
56352     },
56353     onHeaderChange : function(){
56354         this.updateHeaders.apply(this, arguments);
56355     }, 
56356     onHiddenChange : function(){
56357         this.handleHiddenChange.apply(this, arguments);
56358     },
56359     onColumnMove : function(){
56360         this.handleColumnMove.apply(this, arguments);
56361     },
56362     onColumnLock : function(){
56363         this.handleLockChange.apply(this, arguments);
56364     },
56365
56366     onDataChange : function(){
56367         this.refresh();
56368         this.updateHeaderSortState();
56369     },
56370
56371     onClear : function(){
56372         this.refresh();
56373     },
56374
56375     onUpdate : function(ds, record){
56376         this.refreshRow(record);
56377     },
56378
56379     refreshRow : function(record){
56380         var ds = this.ds, index;
56381         if(typeof record == 'number'){
56382             index = record;
56383             record = ds.getAt(index);
56384         }else{
56385             index = ds.indexOf(record);
56386         }
56387         this.insertRows(ds, index, index, true);
56388         this.onRemove(ds, record, index+1, true);
56389         this.syncRowHeights(index, index);
56390         this.layout();
56391         this.fireEvent("rowupdated", this, index, record);
56392     },
56393
56394     onAdd : function(ds, records, index){
56395         this.insertRows(ds, index, index + (records.length-1));
56396     },
56397
56398     onRemove : function(ds, record, index, isUpdate){
56399         if(isUpdate !== true){
56400             this.fireEvent("beforerowremoved", this, index, record);
56401         }
56402         var bt = this.getBodyTable(), lt = this.getLockedTable();
56403         if(bt.rows[index]){
56404             bt.firstChild.removeChild(bt.rows[index]);
56405         }
56406         if(lt.rows[index]){
56407             lt.firstChild.removeChild(lt.rows[index]);
56408         }
56409         if(isUpdate !== true){
56410             this.stripeRows(index);
56411             this.syncRowHeights(index, index);
56412             this.layout();
56413             this.fireEvent("rowremoved", this, index, record);
56414         }
56415     },
56416
56417     onLoad : function(){
56418         this.scrollToTop();
56419     },
56420
56421     /**
56422      * Scrolls the grid to the top
56423      */
56424     scrollToTop : function(){
56425         if(this.scroller){
56426             this.scroller.dom.scrollTop = 0;
56427             this.syncScroll();
56428         }
56429     },
56430
56431     /**
56432      * Gets a panel in the header of the grid that can be used for toolbars etc.
56433      * After modifying the contents of this panel a call to grid.autoSize() may be
56434      * required to register any changes in size.
56435      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
56436      * @return Roo.Element
56437      */
56438     getHeaderPanel : function(doShow){
56439         if(doShow){
56440             this.headerPanel.show();
56441         }
56442         return this.headerPanel;
56443     },
56444
56445     /**
56446      * Gets a panel in the footer of the grid that can be used for toolbars etc.
56447      * After modifying the contents of this panel a call to grid.autoSize() may be
56448      * required to register any changes in size.
56449      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
56450      * @return Roo.Element
56451      */
56452     getFooterPanel : function(doShow){
56453         if(doShow){
56454             this.footerPanel.show();
56455         }
56456         return this.footerPanel;
56457     },
56458
56459     initElements : function(){
56460         var E = Roo.Element;
56461         var el = this.grid.getGridEl().dom.firstChild;
56462         var cs = el.childNodes;
56463
56464         this.el = new E(el);
56465         
56466          this.focusEl = new E(el.firstChild);
56467         this.focusEl.swallowEvent("click", true);
56468         
56469         this.headerPanel = new E(cs[1]);
56470         this.headerPanel.enableDisplayMode("block");
56471
56472         this.scroller = new E(cs[2]);
56473         this.scrollSizer = new E(this.scroller.dom.firstChild);
56474
56475         this.lockedWrap = new E(cs[3]);
56476         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
56477         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
56478
56479         this.mainWrap = new E(cs[4]);
56480         this.mainHd = new E(this.mainWrap.dom.firstChild);
56481         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
56482
56483         this.footerPanel = new E(cs[5]);
56484         this.footerPanel.enableDisplayMode("block");
56485
56486         this.resizeProxy = new E(cs[6]);
56487
56488         this.headerSelector = String.format(
56489            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
56490            this.lockedHd.id, this.mainHd.id
56491         );
56492
56493         this.splitterSelector = String.format(
56494            '#{0} div.x-grid-split, #{1} div.x-grid-split',
56495            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
56496         );
56497     },
56498     idToCssName : function(s)
56499     {
56500         return s.replace(/[^a-z0-9]+/ig, '-');
56501     },
56502
56503     getHeaderCell : function(index){
56504         return Roo.DomQuery.select(this.headerSelector)[index];
56505     },
56506
56507     getHeaderCellMeasure : function(index){
56508         return this.getHeaderCell(index).firstChild;
56509     },
56510
56511     getHeaderCellText : function(index){
56512         return this.getHeaderCell(index).firstChild.firstChild;
56513     },
56514
56515     getLockedTable : function(){
56516         return this.lockedBody.dom.firstChild;
56517     },
56518
56519     getBodyTable : function(){
56520         return this.mainBody.dom.firstChild;
56521     },
56522
56523     getLockedRow : function(index){
56524         return this.getLockedTable().rows[index];
56525     },
56526
56527     getRow : function(index){
56528         return this.getBodyTable().rows[index];
56529     },
56530
56531     getRowComposite : function(index){
56532         if(!this.rowEl){
56533             this.rowEl = new Roo.CompositeElementLite();
56534         }
56535         var els = [], lrow, mrow;
56536         if(lrow = this.getLockedRow(index)){
56537             els.push(lrow);
56538         }
56539         if(mrow = this.getRow(index)){
56540             els.push(mrow);
56541         }
56542         this.rowEl.elements = els;
56543         return this.rowEl;
56544     },
56545     /**
56546      * Gets the 'td' of the cell
56547      * 
56548      * @param {Integer} rowIndex row to select
56549      * @param {Integer} colIndex column to select
56550      * 
56551      * @return {Object} 
56552      */
56553     getCell : function(rowIndex, colIndex){
56554         var locked = this.cm.getLockedCount();
56555         var source;
56556         if(colIndex < locked){
56557             source = this.lockedBody.dom.firstChild;
56558         }else{
56559             source = this.mainBody.dom.firstChild;
56560             colIndex -= locked;
56561         }
56562         return source.rows[rowIndex].childNodes[colIndex];
56563     },
56564
56565     getCellText : function(rowIndex, colIndex){
56566         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
56567     },
56568
56569     getCellBox : function(cell){
56570         var b = this.fly(cell).getBox();
56571         if(Roo.isOpera){ // opera fails to report the Y
56572             b.y = cell.offsetTop + this.mainBody.getY();
56573         }
56574         return b;
56575     },
56576
56577     getCellIndex : function(cell){
56578         var id = String(cell.className).match(this.cellRE);
56579         if(id){
56580             return parseInt(id[1], 10);
56581         }
56582         return 0;
56583     },
56584
56585     findHeaderIndex : function(n){
56586         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
56587         return r ? this.getCellIndex(r) : false;
56588     },
56589
56590     findHeaderCell : function(n){
56591         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
56592         return r ? r : false;
56593     },
56594
56595     findRowIndex : function(n){
56596         if(!n){
56597             return false;
56598         }
56599         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
56600         return r ? r.rowIndex : false;
56601     },
56602
56603     findCellIndex : function(node){
56604         var stop = this.el.dom;
56605         while(node && node != stop){
56606             if(this.findRE.test(node.className)){
56607                 return this.getCellIndex(node);
56608             }
56609             node = node.parentNode;
56610         }
56611         return false;
56612     },
56613
56614     getColumnId : function(index){
56615         return this.cm.getColumnId(index);
56616     },
56617
56618     getSplitters : function()
56619     {
56620         if(this.splitterSelector){
56621            return Roo.DomQuery.select(this.splitterSelector);
56622         }else{
56623             return null;
56624       }
56625     },
56626
56627     getSplitter : function(index){
56628         return this.getSplitters()[index];
56629     },
56630
56631     onRowOver : function(e, t){
56632         var row;
56633         if((row = this.findRowIndex(t)) !== false){
56634             this.getRowComposite(row).addClass("x-grid-row-over");
56635         }
56636     },
56637
56638     onRowOut : function(e, t){
56639         var row;
56640         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
56641             this.getRowComposite(row).removeClass("x-grid-row-over");
56642         }
56643     },
56644
56645     renderHeaders : function(){
56646         var cm = this.cm;
56647         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
56648         var cb = [], lb = [], sb = [], lsb = [], p = {};
56649         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
56650             p.cellId = "x-grid-hd-0-" + i;
56651             p.splitId = "x-grid-csplit-0-" + i;
56652             p.id = cm.getColumnId(i);
56653             p.value = cm.getColumnHeader(i) || "";
56654             p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</)  ? '' :  p.value  || "";
56655             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
56656             if(!cm.isLocked(i)){
56657                 cb[cb.length] = ct.apply(p);
56658                 sb[sb.length] = st.apply(p);
56659             }else{
56660                 lb[lb.length] = ct.apply(p);
56661                 lsb[lsb.length] = st.apply(p);
56662             }
56663         }
56664         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
56665                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
56666     },
56667
56668     updateHeaders : function(){
56669         var html = this.renderHeaders();
56670         this.lockedHd.update(html[0]);
56671         this.mainHd.update(html[1]);
56672     },
56673
56674     /**
56675      * Focuses the specified row.
56676      * @param {Number} row The row index
56677      */
56678     focusRow : function(row)
56679     {
56680         //Roo.log('GridView.focusRow');
56681         var x = this.scroller.dom.scrollLeft;
56682         this.focusCell(row, 0, false);
56683         this.scroller.dom.scrollLeft = x;
56684     },
56685
56686     /**
56687      * Focuses the specified cell.
56688      * @param {Number} row The row index
56689      * @param {Number} col The column index
56690      * @param {Boolean} hscroll false to disable horizontal scrolling
56691      */
56692     focusCell : function(row, col, hscroll)
56693     {
56694         //Roo.log('GridView.focusCell');
56695         var el = this.ensureVisible(row, col, hscroll);
56696         this.focusEl.alignTo(el, "tl-tl");
56697         if(Roo.isGecko){
56698             this.focusEl.focus();
56699         }else{
56700             this.focusEl.focus.defer(1, this.focusEl);
56701         }
56702     },
56703
56704     /**
56705      * Scrolls the specified cell into view
56706      * @param {Number} row The row index
56707      * @param {Number} col The column index
56708      * @param {Boolean} hscroll false to disable horizontal scrolling
56709      */
56710     ensureVisible : function(row, col, hscroll)
56711     {
56712         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
56713         //return null; //disable for testing.
56714         if(typeof row != "number"){
56715             row = row.rowIndex;
56716         }
56717         if(row < 0 && row >= this.ds.getCount()){
56718             return  null;
56719         }
56720         col = (col !== undefined ? col : 0);
56721         var cm = this.grid.colModel;
56722         while(cm.isHidden(col)){
56723             col++;
56724         }
56725
56726         var el = this.getCell(row, col);
56727         if(!el){
56728             return null;
56729         }
56730         var c = this.scroller.dom;
56731
56732         var ctop = parseInt(el.offsetTop, 10);
56733         var cleft = parseInt(el.offsetLeft, 10);
56734         var cbot = ctop + el.offsetHeight;
56735         var cright = cleft + el.offsetWidth;
56736         
56737         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
56738         var stop = parseInt(c.scrollTop, 10);
56739         var sleft = parseInt(c.scrollLeft, 10);
56740         var sbot = stop + ch;
56741         var sright = sleft + c.clientWidth;
56742         /*
56743         Roo.log('GridView.ensureVisible:' +
56744                 ' ctop:' + ctop +
56745                 ' c.clientHeight:' + c.clientHeight +
56746                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
56747                 ' stop:' + stop +
56748                 ' cbot:' + cbot +
56749                 ' sbot:' + sbot +
56750                 ' ch:' + ch  
56751                 );
56752         */
56753         if(ctop < stop){
56754              c.scrollTop = ctop;
56755             //Roo.log("set scrolltop to ctop DISABLE?");
56756         }else if(cbot > sbot){
56757             //Roo.log("set scrolltop to cbot-ch");
56758             c.scrollTop = cbot-ch;
56759         }
56760         
56761         if(hscroll !== false){
56762             if(cleft < sleft){
56763                 c.scrollLeft = cleft;
56764             }else if(cright > sright){
56765                 c.scrollLeft = cright-c.clientWidth;
56766             }
56767         }
56768          
56769         return el;
56770     },
56771
56772     updateColumns : function(){
56773         this.grid.stopEditing();
56774         var cm = this.grid.colModel, colIds = this.getColumnIds();
56775         //var totalWidth = cm.getTotalWidth();
56776         var pos = 0;
56777         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
56778             //if(cm.isHidden(i)) continue;
56779             var w = cm.getColumnWidth(i);
56780             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
56781             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
56782         }
56783         this.updateSplitters();
56784     },
56785
56786     generateRules : function(cm){
56787         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
56788         Roo.util.CSS.removeStyleSheet(rulesId);
56789         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
56790             var cid = cm.getColumnId(i);
56791             var align = '';
56792             if(cm.config[i].align){
56793                 align = 'text-align:'+cm.config[i].align+';';
56794             }
56795             var hidden = '';
56796             if(cm.isHidden(i)){
56797                 hidden = 'display:none;';
56798             }
56799             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
56800             ruleBuf.push(
56801                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
56802                     this.hdSelector, cid, " {\n", align, width, "}\n",
56803                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
56804                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
56805         }
56806         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
56807     },
56808
56809     updateSplitters : function(){
56810         var cm = this.cm, s = this.getSplitters();
56811         if(s){ // splitters not created yet
56812             var pos = 0, locked = true;
56813             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
56814                 if(cm.isHidden(i)) {
56815                     continue;
56816                 }
56817                 var w = cm.getColumnWidth(i); // make sure it's a number
56818                 if(!cm.isLocked(i) && locked){
56819                     pos = 0;
56820                     locked = false;
56821                 }
56822                 pos += w;
56823                 s[i].style.left = (pos-this.splitOffset) + "px";
56824             }
56825         }
56826     },
56827
56828     handleHiddenChange : function(colModel, colIndex, hidden){
56829         if(hidden){
56830             this.hideColumn(colIndex);
56831         }else{
56832             this.unhideColumn(colIndex);
56833         }
56834     },
56835
56836     hideColumn : function(colIndex){
56837         var cid = this.getColumnId(colIndex);
56838         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
56839         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
56840         if(Roo.isSafari){
56841             this.updateHeaders();
56842         }
56843         this.updateSplitters();
56844         this.layout();
56845     },
56846
56847     unhideColumn : function(colIndex){
56848         var cid = this.getColumnId(colIndex);
56849         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
56850         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
56851
56852         if(Roo.isSafari){
56853             this.updateHeaders();
56854         }
56855         this.updateSplitters();
56856         this.layout();
56857     },
56858
56859     insertRows : function(dm, firstRow, lastRow, isUpdate){
56860         if(firstRow == 0 && lastRow == dm.getCount()-1){
56861             this.refresh();
56862         }else{
56863             if(!isUpdate){
56864                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
56865             }
56866             var s = this.getScrollState();
56867             var markup = this.renderRows(firstRow, lastRow);
56868             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
56869             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
56870             this.restoreScroll(s);
56871             if(!isUpdate){
56872                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
56873                 this.syncRowHeights(firstRow, lastRow);
56874                 this.stripeRows(firstRow);
56875                 this.layout();
56876             }
56877         }
56878     },
56879
56880     bufferRows : function(markup, target, index){
56881         var before = null, trows = target.rows, tbody = target.tBodies[0];
56882         if(index < trows.length){
56883             before = trows[index];
56884         }
56885         var b = document.createElement("div");
56886         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
56887         var rows = b.firstChild.rows;
56888         for(var i = 0, len = rows.length; i < len; i++){
56889             if(before){
56890                 tbody.insertBefore(rows[0], before);
56891             }else{
56892                 tbody.appendChild(rows[0]);
56893             }
56894         }
56895         b.innerHTML = "";
56896         b = null;
56897     },
56898
56899     deleteRows : function(dm, firstRow, lastRow){
56900         if(dm.getRowCount()<1){
56901             this.fireEvent("beforerefresh", this);
56902             this.mainBody.update("");
56903             this.lockedBody.update("");
56904             this.fireEvent("refresh", this);
56905         }else{
56906             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
56907             var bt = this.getBodyTable();
56908             var tbody = bt.firstChild;
56909             var rows = bt.rows;
56910             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
56911                 tbody.removeChild(rows[firstRow]);
56912             }
56913             this.stripeRows(firstRow);
56914             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
56915         }
56916     },
56917
56918     updateRows : function(dataSource, firstRow, lastRow){
56919         var s = this.getScrollState();
56920         this.refresh();
56921         this.restoreScroll(s);
56922     },
56923
56924     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
56925         if(!noRefresh){
56926            this.refresh();
56927         }
56928         this.updateHeaderSortState();
56929     },
56930
56931     getScrollState : function(){
56932         
56933         var sb = this.scroller.dom;
56934         return {left: sb.scrollLeft, top: sb.scrollTop};
56935     },
56936
56937     stripeRows : function(startRow){
56938         if(!this.grid.stripeRows || this.ds.getCount() < 1){
56939             return;
56940         }
56941         startRow = startRow || 0;
56942         var rows = this.getBodyTable().rows;
56943         var lrows = this.getLockedTable().rows;
56944         var cls = ' x-grid-row-alt ';
56945         for(var i = startRow, len = rows.length; i < len; i++){
56946             var row = rows[i], lrow = lrows[i];
56947             var isAlt = ((i+1) % 2 == 0);
56948             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
56949             if(isAlt == hasAlt){
56950                 continue;
56951             }
56952             if(isAlt){
56953                 row.className += " x-grid-row-alt";
56954             }else{
56955                 row.className = row.className.replace("x-grid-row-alt", "");
56956             }
56957             if(lrow){
56958                 lrow.className = row.className;
56959             }
56960         }
56961     },
56962
56963     restoreScroll : function(state){
56964         //Roo.log('GridView.restoreScroll');
56965         var sb = this.scroller.dom;
56966         sb.scrollLeft = state.left;
56967         sb.scrollTop = state.top;
56968         this.syncScroll();
56969     },
56970
56971     syncScroll : function(){
56972         //Roo.log('GridView.syncScroll');
56973         var sb = this.scroller.dom;
56974         var sh = this.mainHd.dom;
56975         var bs = this.mainBody.dom;
56976         var lv = this.lockedBody.dom;
56977         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
56978         lv.scrollTop = bs.scrollTop = sb.scrollTop;
56979     },
56980
56981     handleScroll : function(e){
56982         this.syncScroll();
56983         var sb = this.scroller.dom;
56984         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
56985         e.stopEvent();
56986     },
56987
56988     handleWheel : function(e){
56989         var d = e.getWheelDelta();
56990         this.scroller.dom.scrollTop -= d*22;
56991         // set this here to prevent jumpy scrolling on large tables
56992         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
56993         e.stopEvent();
56994     },
56995
56996     renderRows : function(startRow, endRow){
56997         // pull in all the crap needed to render rows
56998         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
56999         var colCount = cm.getColumnCount();
57000
57001         if(ds.getCount() < 1){
57002             return ["", ""];
57003         }
57004
57005         // build a map for all the columns
57006         var cs = [];
57007         for(var i = 0; i < colCount; i++){
57008             var name = cm.getDataIndex(i);
57009             cs[i] = {
57010                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
57011                 renderer : cm.getRenderer(i),
57012                 id : cm.getColumnId(i),
57013                 locked : cm.isLocked(i),
57014                 has_editor : cm.isCellEditable(i)
57015             };
57016         }
57017
57018         startRow = startRow || 0;
57019         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
57020
57021         // records to render
57022         var rs = ds.getRange(startRow, endRow);
57023
57024         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
57025     },
57026
57027     // As much as I hate to duplicate code, this was branched because FireFox really hates
57028     // [].join("") on strings. The performance difference was substantial enough to
57029     // branch this function
57030     doRender : Roo.isGecko ?
57031             function(cs, rs, ds, startRow, colCount, stripe){
57032                 var ts = this.templates, ct = ts.cell, rt = ts.row;
57033                 // buffers
57034                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
57035                 
57036                 var hasListener = this.grid.hasListener('rowclass');
57037                 var rowcfg = {};
57038                 for(var j = 0, len = rs.length; j < len; j++){
57039                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
57040                     for(var i = 0; i < colCount; i++){
57041                         c = cs[i];
57042                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
57043                         p.id = c.id;
57044                         p.css = p.attr = "";
57045                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
57046                         if(p.value == undefined || p.value === "") {
57047                             p.value = "&#160;";
57048                         }
57049                         if(c.has_editor){
57050                             p.css += ' x-grid-editable-cell';
57051                         }
57052                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
57053                             p.css +=  ' x-grid-dirty-cell';
57054                         }
57055                         var markup = ct.apply(p);
57056                         if(!c.locked){
57057                             cb+= markup;
57058                         }else{
57059                             lcb+= markup;
57060                         }
57061                     }
57062                     var alt = [];
57063                     if(stripe && ((rowIndex+1) % 2 == 0)){
57064                         alt.push("x-grid-row-alt")
57065                     }
57066                     if(r.dirty){
57067                         alt.push(  " x-grid-dirty-row");
57068                     }
57069                     rp.cells = lcb;
57070                     if(this.getRowClass){
57071                         alt.push(this.getRowClass(r, rowIndex));
57072                     }
57073                     if (hasListener) {
57074                         rowcfg = {
57075                              
57076                             record: r,
57077                             rowIndex : rowIndex,
57078                             rowClass : ''
57079                         };
57080                         this.grid.fireEvent('rowclass', this, rowcfg);
57081                         alt.push(rowcfg.rowClass);
57082                     }
57083                     rp.alt = alt.join(" ");
57084                     lbuf+= rt.apply(rp);
57085                     rp.cells = cb;
57086                     buf+=  rt.apply(rp);
57087                 }
57088                 return [lbuf, buf];
57089             } :
57090             function(cs, rs, ds, startRow, colCount, stripe){
57091                 var ts = this.templates, ct = ts.cell, rt = ts.row;
57092                 // buffers
57093                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
57094                 var hasListener = this.grid.hasListener('rowclass');
57095  
57096                 var rowcfg = {};
57097                 for(var j = 0, len = rs.length; j < len; j++){
57098                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
57099                     for(var i = 0; i < colCount; i++){
57100                         c = cs[i];
57101                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
57102                         p.id = c.id;
57103                         p.css = p.attr = "";
57104                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
57105                         if(p.value == undefined || p.value === "") {
57106                             p.value = "&#160;";
57107                         }
57108                         //Roo.log(c);
57109                          if(c.has_editor){
57110                             p.css += ' x-grid-editable-cell';
57111                         }
57112                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
57113                             p.css += ' x-grid-dirty-cell' 
57114                         }
57115                         
57116                         var markup = ct.apply(p);
57117                         if(!c.locked){
57118                             cb[cb.length] = markup;
57119                         }else{
57120                             lcb[lcb.length] = markup;
57121                         }
57122                     }
57123                     var alt = [];
57124                     if(stripe && ((rowIndex+1) % 2 == 0)){
57125                         alt.push( "x-grid-row-alt");
57126                     }
57127                     if(r.dirty){
57128                         alt.push(" x-grid-dirty-row");
57129                     }
57130                     rp.cells = lcb;
57131                     if(this.getRowClass){
57132                         alt.push( this.getRowClass(r, rowIndex));
57133                     }
57134                     if (hasListener) {
57135                         rowcfg = {
57136                              
57137                             record: r,
57138                             rowIndex : rowIndex,
57139                             rowClass : ''
57140                         };
57141                         this.grid.fireEvent('rowclass', this, rowcfg);
57142                         alt.push(rowcfg.rowClass);
57143                     }
57144                     
57145                     rp.alt = alt.join(" ");
57146                     rp.cells = lcb.join("");
57147                     lbuf[lbuf.length] = rt.apply(rp);
57148                     rp.cells = cb.join("");
57149                     buf[buf.length] =  rt.apply(rp);
57150                 }
57151                 return [lbuf.join(""), buf.join("")];
57152             },
57153
57154     renderBody : function(){
57155         var markup = this.renderRows();
57156         var bt = this.templates.body;
57157         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
57158     },
57159
57160     /**
57161      * Refreshes the grid
57162      * @param {Boolean} headersToo
57163      */
57164     refresh : function(headersToo){
57165         this.fireEvent("beforerefresh", this);
57166         this.grid.stopEditing();
57167         var result = this.renderBody();
57168         this.lockedBody.update(result[0]);
57169         this.mainBody.update(result[1]);
57170         if(headersToo === true){
57171             this.updateHeaders();
57172             this.updateColumns();
57173             this.updateSplitters();
57174             this.updateHeaderSortState();
57175         }
57176         this.syncRowHeights();
57177         this.layout();
57178         this.fireEvent("refresh", this);
57179     },
57180
57181     handleColumnMove : function(cm, oldIndex, newIndex){
57182         this.indexMap = null;
57183         var s = this.getScrollState();
57184         this.refresh(true);
57185         this.restoreScroll(s);
57186         this.afterMove(newIndex);
57187     },
57188
57189     afterMove : function(colIndex){
57190         if(this.enableMoveAnim && Roo.enableFx){
57191             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
57192         }
57193         // if multisort - fix sortOrder, and reload..
57194         if (this.grid.dataSource.multiSort) {
57195             // the we can call sort again..
57196             var dm = this.grid.dataSource;
57197             var cm = this.grid.colModel;
57198             var so = [];
57199             for(var i = 0; i < cm.config.length; i++ ) {
57200                 
57201                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
57202                     continue; // dont' bother, it's not in sort list or being set.
57203                 }
57204                 
57205                 so.push(cm.config[i].dataIndex);
57206             };
57207             dm.sortOrder = so;
57208             dm.load(dm.lastOptions);
57209             
57210             
57211         }
57212         
57213     },
57214
57215     updateCell : function(dm, rowIndex, dataIndex){
57216         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
57217         if(typeof colIndex == "undefined"){ // not present in grid
57218             return;
57219         }
57220         var cm = this.grid.colModel;
57221         var cell = this.getCell(rowIndex, colIndex);
57222         var cellText = this.getCellText(rowIndex, colIndex);
57223
57224         var p = {
57225             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
57226             id : cm.getColumnId(colIndex),
57227             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
57228         };
57229         var renderer = cm.getRenderer(colIndex);
57230         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
57231         if(typeof val == "undefined" || val === "") {
57232             val = "&#160;";
57233         }
57234         cellText.innerHTML = val;
57235         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
57236         this.syncRowHeights(rowIndex, rowIndex);
57237     },
57238
57239     calcColumnWidth : function(colIndex, maxRowsToMeasure){
57240         var maxWidth = 0;
57241         if(this.grid.autoSizeHeaders){
57242             var h = this.getHeaderCellMeasure(colIndex);
57243             maxWidth = Math.max(maxWidth, h.scrollWidth);
57244         }
57245         var tb, index;
57246         if(this.cm.isLocked(colIndex)){
57247             tb = this.getLockedTable();
57248             index = colIndex;
57249         }else{
57250             tb = this.getBodyTable();
57251             index = colIndex - this.cm.getLockedCount();
57252         }
57253         if(tb && tb.rows){
57254             var rows = tb.rows;
57255             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
57256             for(var i = 0; i < stopIndex; i++){
57257                 var cell = rows[i].childNodes[index].firstChild;
57258                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
57259             }
57260         }
57261         return maxWidth + /*margin for error in IE*/ 5;
57262     },
57263     /**
57264      * Autofit a column to its content.
57265      * @param {Number} colIndex
57266      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
57267      */
57268      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
57269          if(this.cm.isHidden(colIndex)){
57270              return; // can't calc a hidden column
57271          }
57272         if(forceMinSize){
57273             var cid = this.cm.getColumnId(colIndex);
57274             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
57275            if(this.grid.autoSizeHeaders){
57276                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
57277            }
57278         }
57279         var newWidth = this.calcColumnWidth(colIndex);
57280         this.cm.setColumnWidth(colIndex,
57281             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
57282         if(!suppressEvent){
57283             this.grid.fireEvent("columnresize", colIndex, newWidth);
57284         }
57285     },
57286
57287     /**
57288      * Autofits all columns to their content and then expands to fit any extra space in the grid
57289      */
57290      autoSizeColumns : function(){
57291         var cm = this.grid.colModel;
57292         var colCount = cm.getColumnCount();
57293         for(var i = 0; i < colCount; i++){
57294             this.autoSizeColumn(i, true, true);
57295         }
57296         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
57297             this.fitColumns();
57298         }else{
57299             this.updateColumns();
57300             this.layout();
57301         }
57302     },
57303
57304     /**
57305      * Autofits all columns to the grid's width proportionate with their current size
57306      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
57307      */
57308     fitColumns : function(reserveScrollSpace){
57309         var cm = this.grid.colModel;
57310         var colCount = cm.getColumnCount();
57311         var cols = [];
57312         var width = 0;
57313         var i, w;
57314         for (i = 0; i < colCount; i++){
57315             if(!cm.isHidden(i) && !cm.isFixed(i)){
57316                 w = cm.getColumnWidth(i);
57317                 cols.push(i);
57318                 cols.push(w);
57319                 width += w;
57320             }
57321         }
57322         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
57323         if(reserveScrollSpace){
57324             avail -= 17;
57325         }
57326         var frac = (avail - cm.getTotalWidth())/width;
57327         while (cols.length){
57328             w = cols.pop();
57329             i = cols.pop();
57330             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
57331         }
57332         this.updateColumns();
57333         this.layout();
57334     },
57335
57336     onRowSelect : function(rowIndex){
57337         var row = this.getRowComposite(rowIndex);
57338         row.addClass("x-grid-row-selected");
57339     },
57340
57341     onRowDeselect : function(rowIndex){
57342         var row = this.getRowComposite(rowIndex);
57343         row.removeClass("x-grid-row-selected");
57344     },
57345
57346     onCellSelect : function(row, col){
57347         var cell = this.getCell(row, col);
57348         if(cell){
57349             Roo.fly(cell).addClass("x-grid-cell-selected");
57350         }
57351     },
57352
57353     onCellDeselect : function(row, col){
57354         var cell = this.getCell(row, col);
57355         if(cell){
57356             Roo.fly(cell).removeClass("x-grid-cell-selected");
57357         }
57358     },
57359
57360     updateHeaderSortState : function(){
57361         
57362         // sort state can be single { field: xxx, direction : yyy}
57363         // or   { xxx=>ASC , yyy : DESC ..... }
57364         
57365         var mstate = {};
57366         if (!this.ds.multiSort) { 
57367             var state = this.ds.getSortState();
57368             if(!state){
57369                 return;
57370             }
57371             mstate[state.field] = state.direction;
57372             // FIXME... - this is not used here.. but might be elsewhere..
57373             this.sortState = state;
57374             
57375         } else {
57376             mstate = this.ds.sortToggle;
57377         }
57378         //remove existing sort classes..
57379         
57380         var sc = this.sortClasses;
57381         var hds = this.el.select(this.headerSelector).removeClass(sc);
57382         
57383         for(var f in mstate) {
57384         
57385             var sortColumn = this.cm.findColumnIndex(f);
57386             
57387             if(sortColumn != -1){
57388                 var sortDir = mstate[f];        
57389                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
57390             }
57391         }
57392         
57393          
57394         
57395     },
57396
57397
57398     handleHeaderClick : function(g, index,e){
57399         
57400         Roo.log("header click");
57401         
57402         if (Roo.isTouch) {
57403             // touch events on header are handled by context
57404             this.handleHdCtx(g,index,e);
57405             return;
57406         }
57407         
57408         
57409         if(this.headersDisabled){
57410             return;
57411         }
57412         var dm = g.dataSource, cm = g.colModel;
57413         if(!cm.isSortable(index)){
57414             return;
57415         }
57416         g.stopEditing();
57417         
57418         if (dm.multiSort) {
57419             // update the sortOrder
57420             var so = [];
57421             for(var i = 0; i < cm.config.length; i++ ) {
57422                 
57423                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
57424                     continue; // dont' bother, it's not in sort list or being set.
57425                 }
57426                 
57427                 so.push(cm.config[i].dataIndex);
57428             };
57429             dm.sortOrder = so;
57430         }
57431         
57432         
57433         dm.sort(cm.getDataIndex(index));
57434     },
57435
57436
57437     destroy : function(){
57438         if(this.colMenu){
57439             this.colMenu.removeAll();
57440             Roo.menu.MenuMgr.unregister(this.colMenu);
57441             this.colMenu.getEl().remove();
57442             delete this.colMenu;
57443         }
57444         if(this.hmenu){
57445             this.hmenu.removeAll();
57446             Roo.menu.MenuMgr.unregister(this.hmenu);
57447             this.hmenu.getEl().remove();
57448             delete this.hmenu;
57449         }
57450         if(this.grid.enableColumnMove){
57451             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
57452             if(dds){
57453                 for(var dd in dds){
57454                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
57455                         var elid = dds[dd].dragElId;
57456                         dds[dd].unreg();
57457                         Roo.get(elid).remove();
57458                     } else if(dds[dd].config.isTarget){
57459                         dds[dd].proxyTop.remove();
57460                         dds[dd].proxyBottom.remove();
57461                         dds[dd].unreg();
57462                     }
57463                     if(Roo.dd.DDM.locationCache[dd]){
57464                         delete Roo.dd.DDM.locationCache[dd];
57465                     }
57466                 }
57467                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
57468             }
57469         }
57470         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
57471         this.bind(null, null);
57472         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
57473     },
57474
57475     handleLockChange : function(){
57476         this.refresh(true);
57477     },
57478
57479     onDenyColumnLock : function(){
57480
57481     },
57482
57483     onDenyColumnHide : function(){
57484
57485     },
57486
57487     handleHdMenuClick : function(item){
57488         var index = this.hdCtxIndex;
57489         var cm = this.cm, ds = this.ds;
57490         switch(item.id){
57491             case "asc":
57492                 ds.sort(cm.getDataIndex(index), "ASC");
57493                 break;
57494             case "desc":
57495                 ds.sort(cm.getDataIndex(index), "DESC");
57496                 break;
57497             case "lock":
57498                 var lc = cm.getLockedCount();
57499                 if(cm.getColumnCount(true) <= lc+1){
57500                     this.onDenyColumnLock();
57501                     return;
57502                 }
57503                 if(lc != index){
57504                     cm.setLocked(index, true, true);
57505                     cm.moveColumn(index, lc);
57506                     this.grid.fireEvent("columnmove", index, lc);
57507                 }else{
57508                     cm.setLocked(index, true);
57509                 }
57510             break;
57511             case "unlock":
57512                 var lc = cm.getLockedCount();
57513                 if((lc-1) != index){
57514                     cm.setLocked(index, false, true);
57515                     cm.moveColumn(index, lc-1);
57516                     this.grid.fireEvent("columnmove", index, lc-1);
57517                 }else{
57518                     cm.setLocked(index, false);
57519                 }
57520             break;
57521             case 'wider': // used to expand cols on touch..
57522             case 'narrow':
57523                 var cw = cm.getColumnWidth(index);
57524                 cw += (item.id == 'wider' ? 1 : -1) * 50;
57525                 cw = Math.max(0, cw);
57526                 cw = Math.min(cw,4000);
57527                 cm.setColumnWidth(index, cw);
57528                 break;
57529                 
57530             default:
57531                 index = cm.getIndexById(item.id.substr(4));
57532                 if(index != -1){
57533                     if(item.checked && cm.getColumnCount(true) <= 1){
57534                         this.onDenyColumnHide();
57535                         return false;
57536                     }
57537                     cm.setHidden(index, item.checked);
57538                 }
57539         }
57540         return true;
57541     },
57542
57543     beforeColMenuShow : function(){
57544         var cm = this.cm,  colCount = cm.getColumnCount();
57545         this.colMenu.removeAll();
57546         for(var i = 0; i < colCount; i++){
57547             this.colMenu.add(new Roo.menu.CheckItem({
57548                 id: "col-"+cm.getColumnId(i),
57549                 text: cm.getColumnHeader(i),
57550                 checked: !cm.isHidden(i),
57551                 hideOnClick:false
57552             }));
57553         }
57554     },
57555
57556     handleHdCtx : function(g, index, e){
57557         e.stopEvent();
57558         var hd = this.getHeaderCell(index);
57559         this.hdCtxIndex = index;
57560         var ms = this.hmenu.items, cm = this.cm;
57561         ms.get("asc").setDisabled(!cm.isSortable(index));
57562         ms.get("desc").setDisabled(!cm.isSortable(index));
57563         if(this.grid.enableColLock !== false){
57564             ms.get("lock").setDisabled(cm.isLocked(index));
57565             ms.get("unlock").setDisabled(!cm.isLocked(index));
57566         }
57567         this.hmenu.show(hd, "tl-bl");
57568     },
57569
57570     handleHdOver : function(e){
57571         var hd = this.findHeaderCell(e.getTarget());
57572         if(hd && !this.headersDisabled){
57573             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
57574                this.fly(hd).addClass("x-grid-hd-over");
57575             }
57576         }
57577     },
57578
57579     handleHdOut : function(e){
57580         var hd = this.findHeaderCell(e.getTarget());
57581         if(hd){
57582             this.fly(hd).removeClass("x-grid-hd-over");
57583         }
57584     },
57585
57586     handleSplitDblClick : function(e, t){
57587         var i = this.getCellIndex(t);
57588         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
57589             this.autoSizeColumn(i, true);
57590             this.layout();
57591         }
57592     },
57593
57594     render : function(){
57595
57596         var cm = this.cm;
57597         var colCount = cm.getColumnCount();
57598
57599         if(this.grid.monitorWindowResize === true){
57600             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
57601         }
57602         var header = this.renderHeaders();
57603         var body = this.templates.body.apply({rows:""});
57604         var html = this.templates.master.apply({
57605             lockedBody: body,
57606             body: body,
57607             lockedHeader: header[0],
57608             header: header[1]
57609         });
57610
57611         //this.updateColumns();
57612
57613         this.grid.getGridEl().dom.innerHTML = html;
57614
57615         this.initElements();
57616         
57617         // a kludge to fix the random scolling effect in webkit
57618         this.el.on("scroll", function() {
57619             this.el.dom.scrollTop=0; // hopefully not recursive..
57620         },this);
57621
57622         this.scroller.on("scroll", this.handleScroll, this);
57623         this.lockedBody.on("mousewheel", this.handleWheel, this);
57624         this.mainBody.on("mousewheel", this.handleWheel, this);
57625
57626         this.mainHd.on("mouseover", this.handleHdOver, this);
57627         this.mainHd.on("mouseout", this.handleHdOut, this);
57628         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
57629                 {delegate: "."+this.splitClass});
57630
57631         this.lockedHd.on("mouseover", this.handleHdOver, this);
57632         this.lockedHd.on("mouseout", this.handleHdOut, this);
57633         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
57634                 {delegate: "."+this.splitClass});
57635
57636         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
57637             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
57638         }
57639
57640         this.updateSplitters();
57641
57642         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
57643             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
57644             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
57645         }
57646
57647         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
57648             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
57649             this.hmenu.add(
57650                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
57651                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
57652             );
57653             if(this.grid.enableColLock !== false){
57654                 this.hmenu.add('-',
57655                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
57656                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
57657                 );
57658             }
57659             if (Roo.isTouch) {
57660                  this.hmenu.add('-',
57661                     {id:"wider", text: this.columnsWiderText},
57662                     {id:"narrow", text: this.columnsNarrowText }
57663                 );
57664                 
57665                  
57666             }
57667             
57668             if(this.grid.enableColumnHide !== false){
57669
57670                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
57671                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
57672                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
57673
57674                 this.hmenu.add('-',
57675                     {id:"columns", text: this.columnsText, menu: this.colMenu}
57676                 );
57677             }
57678             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
57679
57680             this.grid.on("headercontextmenu", this.handleHdCtx, this);
57681         }
57682
57683         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
57684             this.dd = new Roo.grid.GridDragZone(this.grid, {
57685                 ddGroup : this.grid.ddGroup || 'GridDD'
57686             });
57687             
57688         }
57689
57690         /*
57691         for(var i = 0; i < colCount; i++){
57692             if(cm.isHidden(i)){
57693                 this.hideColumn(i);
57694             }
57695             if(cm.config[i].align){
57696                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
57697                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
57698             }
57699         }*/
57700         
57701         this.updateHeaderSortState();
57702
57703         this.beforeInitialResize();
57704         this.layout(true);
57705
57706         // two part rendering gives faster view to the user
57707         this.renderPhase2.defer(1, this);
57708     },
57709
57710     renderPhase2 : function(){
57711         // render the rows now
57712         this.refresh();
57713         if(this.grid.autoSizeColumns){
57714             this.autoSizeColumns();
57715         }
57716     },
57717
57718     beforeInitialResize : function(){
57719
57720     },
57721
57722     onColumnSplitterMoved : function(i, w){
57723         this.userResized = true;
57724         var cm = this.grid.colModel;
57725         cm.setColumnWidth(i, w, true);
57726         var cid = cm.getColumnId(i);
57727         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
57728         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
57729         this.updateSplitters();
57730         this.layout();
57731         this.grid.fireEvent("columnresize", i, w);
57732     },
57733
57734     syncRowHeights : function(startIndex, endIndex){
57735         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
57736             startIndex = startIndex || 0;
57737             var mrows = this.getBodyTable().rows;
57738             var lrows = this.getLockedTable().rows;
57739             var len = mrows.length-1;
57740             endIndex = Math.min(endIndex || len, len);
57741             for(var i = startIndex; i <= endIndex; i++){
57742                 var m = mrows[i], l = lrows[i];
57743                 var h = Math.max(m.offsetHeight, l.offsetHeight);
57744                 m.style.height = l.style.height = h + "px";
57745             }
57746         }
57747     },
57748
57749     layout : function(initialRender, is2ndPass){
57750         var g = this.grid;
57751         var auto = g.autoHeight;
57752         var scrollOffset = 16;
57753         var c = g.getGridEl(), cm = this.cm,
57754                 expandCol = g.autoExpandColumn,
57755                 gv = this;
57756         //c.beginMeasure();
57757
57758         if(!c.dom.offsetWidth){ // display:none?
57759             if(initialRender){
57760                 this.lockedWrap.show();
57761                 this.mainWrap.show();
57762             }
57763             return;
57764         }
57765
57766         var hasLock = this.cm.isLocked(0);
57767
57768         var tbh = this.headerPanel.getHeight();
57769         var bbh = this.footerPanel.getHeight();
57770
57771         if(auto){
57772             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
57773             var newHeight = ch + c.getBorderWidth("tb");
57774             if(g.maxHeight){
57775                 newHeight = Math.min(g.maxHeight, newHeight);
57776             }
57777             c.setHeight(newHeight);
57778         }
57779
57780         if(g.autoWidth){
57781             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
57782         }
57783
57784         var s = this.scroller;
57785
57786         var csize = c.getSize(true);
57787
57788         this.el.setSize(csize.width, csize.height);
57789
57790         this.headerPanel.setWidth(csize.width);
57791         this.footerPanel.setWidth(csize.width);
57792
57793         var hdHeight = this.mainHd.getHeight();
57794         var vw = csize.width;
57795         var vh = csize.height - (tbh + bbh);
57796
57797         s.setSize(vw, vh);
57798
57799         var bt = this.getBodyTable();
57800         
57801         if(cm.getLockedCount() == cm.config.length){
57802             bt = this.getLockedTable();
57803         }
57804         
57805         var ltWidth = hasLock ?
57806                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
57807
57808         var scrollHeight = bt.offsetHeight;
57809         var scrollWidth = ltWidth + bt.offsetWidth;
57810         var vscroll = false, hscroll = false;
57811
57812         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
57813
57814         var lw = this.lockedWrap, mw = this.mainWrap;
57815         var lb = this.lockedBody, mb = this.mainBody;
57816
57817         setTimeout(function(){
57818             var t = s.dom.offsetTop;
57819             var w = s.dom.clientWidth,
57820                 h = s.dom.clientHeight;
57821
57822             lw.setTop(t);
57823             lw.setSize(ltWidth, h);
57824
57825             mw.setLeftTop(ltWidth, t);
57826             mw.setSize(w-ltWidth, h);
57827
57828             lb.setHeight(h-hdHeight);
57829             mb.setHeight(h-hdHeight);
57830
57831             if(is2ndPass !== true && !gv.userResized && expandCol){
57832                 // high speed resize without full column calculation
57833                 
57834                 var ci = cm.getIndexById(expandCol);
57835                 if (ci < 0) {
57836                     ci = cm.findColumnIndex(expandCol);
57837                 }
57838                 ci = Math.max(0, ci); // make sure it's got at least the first col.
57839                 var expandId = cm.getColumnId(ci);
57840                 var  tw = cm.getTotalWidth(false);
57841                 var currentWidth = cm.getColumnWidth(ci);
57842                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
57843                 if(currentWidth != cw){
57844                     cm.setColumnWidth(ci, cw, true);
57845                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
57846                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
57847                     gv.updateSplitters();
57848                     gv.layout(false, true);
57849                 }
57850             }
57851
57852             if(initialRender){
57853                 lw.show();
57854                 mw.show();
57855             }
57856             //c.endMeasure();
57857         }, 10);
57858     },
57859
57860     onWindowResize : function(){
57861         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
57862             return;
57863         }
57864         this.layout();
57865     },
57866
57867     appendFooter : function(parentEl){
57868         return null;
57869     },
57870
57871     sortAscText : "Sort Ascending",
57872     sortDescText : "Sort Descending",
57873     lockText : "Lock Column",
57874     unlockText : "Unlock Column",
57875     columnsText : "Columns",
57876  
57877     columnsWiderText : "Wider",
57878     columnsNarrowText : "Thinner"
57879 });
57880
57881
57882 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
57883     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
57884     this.proxy.el.addClass('x-grid3-col-dd');
57885 };
57886
57887 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
57888     handleMouseDown : function(e){
57889
57890     },
57891
57892     callHandleMouseDown : function(e){
57893         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
57894     }
57895 });
57896 /*
57897  * Based on:
57898  * Ext JS Library 1.1.1
57899  * Copyright(c) 2006-2007, Ext JS, LLC.
57900  *
57901  * Originally Released Under LGPL - original licence link has changed is not relivant.
57902  *
57903  * Fork - LGPL
57904  * <script type="text/javascript">
57905  */
57906  
57907 // private
57908 // This is a support class used internally by the Grid components
57909 Roo.grid.SplitDragZone = function(grid, hd, hd2){
57910     this.grid = grid;
57911     this.view = grid.getView();
57912     this.proxy = this.view.resizeProxy;
57913     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
57914         "gridSplitters" + this.grid.getGridEl().id, {
57915         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
57916     });
57917     this.setHandleElId(Roo.id(hd));
57918     this.setOuterHandleElId(Roo.id(hd2));
57919     this.scroll = false;
57920 };
57921 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
57922     fly: Roo.Element.fly,
57923
57924     b4StartDrag : function(x, y){
57925         this.view.headersDisabled = true;
57926         this.proxy.setHeight(this.view.mainWrap.getHeight());
57927         var w = this.cm.getColumnWidth(this.cellIndex);
57928         var minw = Math.max(w-this.grid.minColumnWidth, 0);
57929         this.resetConstraints();
57930         this.setXConstraint(minw, 1000);
57931         this.setYConstraint(0, 0);
57932         this.minX = x - minw;
57933         this.maxX = x + 1000;
57934         this.startPos = x;
57935         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
57936     },
57937
57938
57939     handleMouseDown : function(e){
57940         ev = Roo.EventObject.setEvent(e);
57941         var t = this.fly(ev.getTarget());
57942         if(t.hasClass("x-grid-split")){
57943             this.cellIndex = this.view.getCellIndex(t.dom);
57944             this.split = t.dom;
57945             this.cm = this.grid.colModel;
57946             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
57947                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
57948             }
57949         }
57950     },
57951
57952     endDrag : function(e){
57953         this.view.headersDisabled = false;
57954         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
57955         var diff = endX - this.startPos;
57956         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
57957     },
57958
57959     autoOffset : function(){
57960         this.setDelta(0,0);
57961     }
57962 });/*
57963  * Based on:
57964  * Ext JS Library 1.1.1
57965  * Copyright(c) 2006-2007, Ext JS, LLC.
57966  *
57967  * Originally Released Under LGPL - original licence link has changed is not relivant.
57968  *
57969  * Fork - LGPL
57970  * <script type="text/javascript">
57971  */
57972  
57973 // private
57974 // This is a support class used internally by the Grid components
57975 Roo.grid.GridDragZone = function(grid, config){
57976     this.view = grid.getView();
57977     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
57978     if(this.view.lockedBody){
57979         this.setHandleElId(Roo.id(this.view.mainBody.dom));
57980         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
57981     }
57982     this.scroll = false;
57983     this.grid = grid;
57984     this.ddel = document.createElement('div');
57985     this.ddel.className = 'x-grid-dd-wrap';
57986 };
57987
57988 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
57989     ddGroup : "GridDD",
57990
57991     getDragData : function(e){
57992         var t = Roo.lib.Event.getTarget(e);
57993         var rowIndex = this.view.findRowIndex(t);
57994         var sm = this.grid.selModel;
57995             
57996         //Roo.log(rowIndex);
57997         
57998         if (sm.getSelectedCell) {
57999             // cell selection..
58000             if (!sm.getSelectedCell()) {
58001                 return false;
58002             }
58003             if (rowIndex != sm.getSelectedCell()[0]) {
58004                 return false;
58005             }
58006         
58007         }
58008         
58009         if(rowIndex !== false){
58010             
58011             // if editorgrid.. 
58012             
58013             
58014             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
58015                
58016             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
58017               //  
58018             //}
58019             if (e.hasModifier()){
58020                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
58021             }
58022             
58023             Roo.log("getDragData");
58024             
58025             return {
58026                 grid: this.grid,
58027                 ddel: this.ddel,
58028                 rowIndex: rowIndex,
58029                 selections:sm.getSelections ? sm.getSelections() : (
58030                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
58031                 )
58032             };
58033         }
58034         return false;
58035     },
58036
58037     onInitDrag : function(e){
58038         var data = this.dragData;
58039         this.ddel.innerHTML = this.grid.getDragDropText();
58040         this.proxy.update(this.ddel);
58041         // fire start drag?
58042     },
58043
58044     afterRepair : function(){
58045         this.dragging = false;
58046     },
58047
58048     getRepairXY : function(e, data){
58049         return false;
58050     },
58051
58052     onEndDrag : function(data, e){
58053         // fire end drag?
58054     },
58055
58056     onValidDrop : function(dd, e, id){
58057         // fire drag drop?
58058         this.hideProxy();
58059     },
58060
58061     beforeInvalidDrop : function(e, id){
58062
58063     }
58064 });/*
58065  * Based on:
58066  * Ext JS Library 1.1.1
58067  * Copyright(c) 2006-2007, Ext JS, LLC.
58068  *
58069  * Originally Released Under LGPL - original licence link has changed is not relivant.
58070  *
58071  * Fork - LGPL
58072  * <script type="text/javascript">
58073  */
58074  
58075
58076 /**
58077  * @class Roo.grid.ColumnModel
58078  * @extends Roo.util.Observable
58079  * This is the default implementation of a ColumnModel used by the Grid. It defines
58080  * the columns in the grid.
58081  * <br>Usage:<br>
58082  <pre><code>
58083  var colModel = new Roo.grid.ColumnModel([
58084         {header: "Ticker", width: 60, sortable: true, locked: true},
58085         {header: "Company Name", width: 150, sortable: true},
58086         {header: "Market Cap.", width: 100, sortable: true},
58087         {header: "$ Sales", width: 100, sortable: true, renderer: money},
58088         {header: "Employees", width: 100, sortable: true, resizable: false}
58089  ]);
58090  </code></pre>
58091  * <p>
58092  
58093  * The config options listed for this class are options which may appear in each
58094  * individual column definition.
58095  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
58096  * @constructor
58097  * @param {Object} config An Array of column config objects. See this class's
58098  * config objects for details.
58099 */
58100 Roo.grid.ColumnModel = function(config){
58101         /**
58102      * The config passed into the constructor
58103      */
58104     this.config = config;
58105     this.lookup = {};
58106
58107     // if no id, create one
58108     // if the column does not have a dataIndex mapping,
58109     // map it to the order it is in the config
58110     for(var i = 0, len = config.length; i < len; i++){
58111         var c = config[i];
58112         if(typeof c.dataIndex == "undefined"){
58113             c.dataIndex = i;
58114         }
58115         if(typeof c.renderer == "string"){
58116             c.renderer = Roo.util.Format[c.renderer];
58117         }
58118         if(typeof c.id == "undefined"){
58119             c.id = Roo.id();
58120         }
58121         if(c.editor && c.editor.xtype){
58122             c.editor  = Roo.factory(c.editor, Roo.grid);
58123         }
58124         if(c.editor && c.editor.isFormField){
58125             c.editor = new Roo.grid.GridEditor(c.editor);
58126         }
58127         this.lookup[c.id] = c;
58128     }
58129
58130     /**
58131      * The width of columns which have no width specified (defaults to 100)
58132      * @type Number
58133      */
58134     this.defaultWidth = 100;
58135
58136     /**
58137      * Default sortable of columns which have no sortable specified (defaults to false)
58138      * @type Boolean
58139      */
58140     this.defaultSortable = false;
58141
58142     this.addEvents({
58143         /**
58144              * @event widthchange
58145              * Fires when the width of a column changes.
58146              * @param {ColumnModel} this
58147              * @param {Number} columnIndex The column index
58148              * @param {Number} newWidth The new width
58149              */
58150             "widthchange": true,
58151         /**
58152              * @event headerchange
58153              * Fires when the text of a header changes.
58154              * @param {ColumnModel} this
58155              * @param {Number} columnIndex The column index
58156              * @param {Number} newText The new header text
58157              */
58158             "headerchange": true,
58159         /**
58160              * @event hiddenchange
58161              * Fires when a column is hidden or "unhidden".
58162              * @param {ColumnModel} this
58163              * @param {Number} columnIndex The column index
58164              * @param {Boolean} hidden true if hidden, false otherwise
58165              */
58166             "hiddenchange": true,
58167             /**
58168          * @event columnmoved
58169          * Fires when a column is moved.
58170          * @param {ColumnModel} this
58171          * @param {Number} oldIndex
58172          * @param {Number} newIndex
58173          */
58174         "columnmoved" : true,
58175         /**
58176          * @event columlockchange
58177          * Fires when a column's locked state is changed
58178          * @param {ColumnModel} this
58179          * @param {Number} colIndex
58180          * @param {Boolean} locked true if locked
58181          */
58182         "columnlockchange" : true
58183     });
58184     Roo.grid.ColumnModel.superclass.constructor.call(this);
58185 };
58186 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
58187     /**
58188      * @cfg {String} header The header text to display in the Grid view.
58189      */
58190     /**
58191      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
58192      * {@link Roo.data.Record} definition from which to draw the column's value. If not
58193      * specified, the column's index is used as an index into the Record's data Array.
58194      */
58195     /**
58196      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
58197      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
58198      */
58199     /**
58200      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
58201      * Defaults to the value of the {@link #defaultSortable} property.
58202      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
58203      */
58204     /**
58205      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
58206      */
58207     /**
58208      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
58209      */
58210     /**
58211      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
58212      */
58213     /**
58214      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
58215      */
58216     /**
58217      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
58218      * given the cell's data value. See {@link #setRenderer}. If not specified, the
58219      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
58220      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
58221      */
58222        /**
58223      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
58224      */
58225     /**
58226      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
58227      */
58228     /**
58229      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
58230      */
58231     /**
58232      * @cfg {String} cursor (Optional)
58233      */
58234     /**
58235      * @cfg {String} tooltip (Optional)
58236      */
58237     /**
58238      * @cfg {Number} xs (Optional)
58239      */
58240     /**
58241      * @cfg {Number} sm (Optional)
58242      */
58243     /**
58244      * @cfg {Number} md (Optional)
58245      */
58246     /**
58247      * @cfg {Number} lg (Optional)
58248      */
58249     /**
58250      * Returns the id of the column at the specified index.
58251      * @param {Number} index The column index
58252      * @return {String} the id
58253      */
58254     getColumnId : function(index){
58255         return this.config[index].id;
58256     },
58257
58258     /**
58259      * Returns the column for a specified id.
58260      * @param {String} id The column id
58261      * @return {Object} the column
58262      */
58263     getColumnById : function(id){
58264         return this.lookup[id];
58265     },
58266
58267     
58268     /**
58269      * Returns the column for a specified dataIndex.
58270      * @param {String} dataIndex The column dataIndex
58271      * @return {Object|Boolean} the column or false if not found
58272      */
58273     getColumnByDataIndex: function(dataIndex){
58274         var index = this.findColumnIndex(dataIndex);
58275         return index > -1 ? this.config[index] : false;
58276     },
58277     
58278     /**
58279      * Returns the index for a specified column id.
58280      * @param {String} id The column id
58281      * @return {Number} the index, or -1 if not found
58282      */
58283     getIndexById : function(id){
58284         for(var i = 0, len = this.config.length; i < len; i++){
58285             if(this.config[i].id == id){
58286                 return i;
58287             }
58288         }
58289         return -1;
58290     },
58291     
58292     /**
58293      * Returns the index for a specified column dataIndex.
58294      * @param {String} dataIndex The column dataIndex
58295      * @return {Number} the index, or -1 if not found
58296      */
58297     
58298     findColumnIndex : function(dataIndex){
58299         for(var i = 0, len = this.config.length; i < len; i++){
58300             if(this.config[i].dataIndex == dataIndex){
58301                 return i;
58302             }
58303         }
58304         return -1;
58305     },
58306     
58307     
58308     moveColumn : function(oldIndex, newIndex){
58309         var c = this.config[oldIndex];
58310         this.config.splice(oldIndex, 1);
58311         this.config.splice(newIndex, 0, c);
58312         this.dataMap = null;
58313         this.fireEvent("columnmoved", this, oldIndex, newIndex);
58314     },
58315
58316     isLocked : function(colIndex){
58317         return this.config[colIndex].locked === true;
58318     },
58319
58320     setLocked : function(colIndex, value, suppressEvent){
58321         if(this.isLocked(colIndex) == value){
58322             return;
58323         }
58324         this.config[colIndex].locked = value;
58325         if(!suppressEvent){
58326             this.fireEvent("columnlockchange", this, colIndex, value);
58327         }
58328     },
58329
58330     getTotalLockedWidth : function(){
58331         var totalWidth = 0;
58332         for(var i = 0; i < this.config.length; i++){
58333             if(this.isLocked(i) && !this.isHidden(i)){
58334                 this.totalWidth += this.getColumnWidth(i);
58335             }
58336         }
58337         return totalWidth;
58338     },
58339
58340     getLockedCount : function(){
58341         for(var i = 0, len = this.config.length; i < len; i++){
58342             if(!this.isLocked(i)){
58343                 return i;
58344             }
58345         }
58346         
58347         return this.config.length;
58348     },
58349
58350     /**
58351      * Returns the number of columns.
58352      * @return {Number}
58353      */
58354     getColumnCount : function(visibleOnly){
58355         if(visibleOnly === true){
58356             var c = 0;
58357             for(var i = 0, len = this.config.length; i < len; i++){
58358                 if(!this.isHidden(i)){
58359                     c++;
58360                 }
58361             }
58362             return c;
58363         }
58364         return this.config.length;
58365     },
58366
58367     /**
58368      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
58369      * @param {Function} fn
58370      * @param {Object} scope (optional)
58371      * @return {Array} result
58372      */
58373     getColumnsBy : function(fn, scope){
58374         var r = [];
58375         for(var i = 0, len = this.config.length; i < len; i++){
58376             var c = this.config[i];
58377             if(fn.call(scope||this, c, i) === true){
58378                 r[r.length] = c;
58379             }
58380         }
58381         return r;
58382     },
58383
58384     /**
58385      * Returns true if the specified column is sortable.
58386      * @param {Number} col The column index
58387      * @return {Boolean}
58388      */
58389     isSortable : function(col){
58390         if(typeof this.config[col].sortable == "undefined"){
58391             return this.defaultSortable;
58392         }
58393         return this.config[col].sortable;
58394     },
58395
58396     /**
58397      * Returns the rendering (formatting) function defined for the column.
58398      * @param {Number} col The column index.
58399      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
58400      */
58401     getRenderer : function(col){
58402         if(!this.config[col].renderer){
58403             return Roo.grid.ColumnModel.defaultRenderer;
58404         }
58405         return this.config[col].renderer;
58406     },
58407
58408     /**
58409      * Sets the rendering (formatting) function for a column.
58410      * @param {Number} col The column index
58411      * @param {Function} fn The function to use to process the cell's raw data
58412      * to return HTML markup for the grid view. The render function is called with
58413      * the following parameters:<ul>
58414      * <li>Data value.</li>
58415      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
58416      * <li>css A CSS style string to apply to the table cell.</li>
58417      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
58418      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
58419      * <li>Row index</li>
58420      * <li>Column index</li>
58421      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
58422      */
58423     setRenderer : function(col, fn){
58424         this.config[col].renderer = fn;
58425     },
58426
58427     /**
58428      * Returns the width for the specified column.
58429      * @param {Number} col The column index
58430      * @return {Number}
58431      */
58432     getColumnWidth : function(col){
58433         return this.config[col].width * 1 || this.defaultWidth;
58434     },
58435
58436     /**
58437      * Sets the width for a column.
58438      * @param {Number} col The column index
58439      * @param {Number} width The new width
58440      */
58441     setColumnWidth : function(col, width, suppressEvent){
58442         this.config[col].width = width;
58443         this.totalWidth = null;
58444         if(!suppressEvent){
58445              this.fireEvent("widthchange", this, col, width);
58446         }
58447     },
58448
58449     /**
58450      * Returns the total width of all columns.
58451      * @param {Boolean} includeHidden True to include hidden column widths
58452      * @return {Number}
58453      */
58454     getTotalWidth : function(includeHidden){
58455         if(!this.totalWidth){
58456             this.totalWidth = 0;
58457             for(var i = 0, len = this.config.length; i < len; i++){
58458                 if(includeHidden || !this.isHidden(i)){
58459                     this.totalWidth += this.getColumnWidth(i);
58460                 }
58461             }
58462         }
58463         return this.totalWidth;
58464     },
58465
58466     /**
58467      * Returns the header for the specified column.
58468      * @param {Number} col The column index
58469      * @return {String}
58470      */
58471     getColumnHeader : function(col){
58472         return this.config[col].header;
58473     },
58474
58475     /**
58476      * Sets the header for a column.
58477      * @param {Number} col The column index
58478      * @param {String} header The new header
58479      */
58480     setColumnHeader : function(col, header){
58481         this.config[col].header = header;
58482         this.fireEvent("headerchange", this, col, header);
58483     },
58484
58485     /**
58486      * Returns the tooltip for the specified column.
58487      * @param {Number} col The column index
58488      * @return {String}
58489      */
58490     getColumnTooltip : function(col){
58491             return this.config[col].tooltip;
58492     },
58493     /**
58494      * Sets the tooltip for a column.
58495      * @param {Number} col The column index
58496      * @param {String} tooltip The new tooltip
58497      */
58498     setColumnTooltip : function(col, tooltip){
58499             this.config[col].tooltip = tooltip;
58500     },
58501
58502     /**
58503      * Returns the dataIndex for the specified column.
58504      * @param {Number} col The column index
58505      * @return {Number}
58506      */
58507     getDataIndex : function(col){
58508         return this.config[col].dataIndex;
58509     },
58510
58511     /**
58512      * Sets the dataIndex for a column.
58513      * @param {Number} col The column index
58514      * @param {Number} dataIndex The new dataIndex
58515      */
58516     setDataIndex : function(col, dataIndex){
58517         this.config[col].dataIndex = dataIndex;
58518     },
58519
58520     
58521     
58522     /**
58523      * Returns true if the cell is editable.
58524      * @param {Number} colIndex The column index
58525      * @param {Number} rowIndex The row index - this is nto actually used..?
58526      * @return {Boolean}
58527      */
58528     isCellEditable : function(colIndex, rowIndex){
58529         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
58530     },
58531
58532     /**
58533      * Returns the editor defined for the cell/column.
58534      * return false or null to disable editing.
58535      * @param {Number} colIndex The column index
58536      * @param {Number} rowIndex The row index
58537      * @return {Object}
58538      */
58539     getCellEditor : function(colIndex, rowIndex){
58540         return this.config[colIndex].editor;
58541     },
58542
58543     /**
58544      * Sets if a column is editable.
58545      * @param {Number} col The column index
58546      * @param {Boolean} editable True if the column is editable
58547      */
58548     setEditable : function(col, editable){
58549         this.config[col].editable = editable;
58550     },
58551
58552
58553     /**
58554      * Returns true if the column is hidden.
58555      * @param {Number} colIndex The column index
58556      * @return {Boolean}
58557      */
58558     isHidden : function(colIndex){
58559         return this.config[colIndex].hidden;
58560     },
58561
58562
58563     /**
58564      * Returns true if the column width cannot be changed
58565      */
58566     isFixed : function(colIndex){
58567         return this.config[colIndex].fixed;
58568     },
58569
58570     /**
58571      * Returns true if the column can be resized
58572      * @return {Boolean}
58573      */
58574     isResizable : function(colIndex){
58575         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
58576     },
58577     /**
58578      * Sets if a column is hidden.
58579      * @param {Number} colIndex The column index
58580      * @param {Boolean} hidden True if the column is hidden
58581      */
58582     setHidden : function(colIndex, hidden){
58583         this.config[colIndex].hidden = hidden;
58584         this.totalWidth = null;
58585         this.fireEvent("hiddenchange", this, colIndex, hidden);
58586     },
58587
58588     /**
58589      * Sets the editor for a column.
58590      * @param {Number} col The column index
58591      * @param {Object} editor The editor object
58592      */
58593     setEditor : function(col, editor){
58594         this.config[col].editor = editor;
58595     }
58596 });
58597
58598 Roo.grid.ColumnModel.defaultRenderer = function(value)
58599 {
58600     if(typeof value == "object") {
58601         return value;
58602     }
58603         if(typeof value == "string" && value.length < 1){
58604             return "&#160;";
58605         }
58606     
58607         return String.format("{0}", value);
58608 };
58609
58610 // Alias for backwards compatibility
58611 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
58612 /*
58613  * Based on:
58614  * Ext JS Library 1.1.1
58615  * Copyright(c) 2006-2007, Ext JS, LLC.
58616  *
58617  * Originally Released Under LGPL - original licence link has changed is not relivant.
58618  *
58619  * Fork - LGPL
58620  * <script type="text/javascript">
58621  */
58622
58623 /**
58624  * @class Roo.grid.AbstractSelectionModel
58625  * @extends Roo.util.Observable
58626  * Abstract base class for grid SelectionModels.  It provides the interface that should be
58627  * implemented by descendant classes.  This class should not be directly instantiated.
58628  * @constructor
58629  */
58630 Roo.grid.AbstractSelectionModel = function(){
58631     this.locked = false;
58632     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
58633 };
58634
58635 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
58636     /** @ignore Called by the grid automatically. Do not call directly. */
58637     init : function(grid){
58638         this.grid = grid;
58639         this.initEvents();
58640     },
58641
58642     /**
58643      * Locks the selections.
58644      */
58645     lock : function(){
58646         this.locked = true;
58647     },
58648
58649     /**
58650      * Unlocks the selections.
58651      */
58652     unlock : function(){
58653         this.locked = false;
58654     },
58655
58656     /**
58657      * Returns true if the selections are locked.
58658      * @return {Boolean}
58659      */
58660     isLocked : function(){
58661         return this.locked;
58662     }
58663 });/*
58664  * Based on:
58665  * Ext JS Library 1.1.1
58666  * Copyright(c) 2006-2007, Ext JS, LLC.
58667  *
58668  * Originally Released Under LGPL - original licence link has changed is not relivant.
58669  *
58670  * Fork - LGPL
58671  * <script type="text/javascript">
58672  */
58673 /**
58674  * @extends Roo.grid.AbstractSelectionModel
58675  * @class Roo.grid.RowSelectionModel
58676  * The default SelectionModel used by {@link Roo.grid.Grid}.
58677  * It supports multiple selections and keyboard selection/navigation. 
58678  * @constructor
58679  * @param {Object} config
58680  */
58681 Roo.grid.RowSelectionModel = function(config){
58682     Roo.apply(this, config);
58683     this.selections = new Roo.util.MixedCollection(false, function(o){
58684         return o.id;
58685     });
58686
58687     this.last = false;
58688     this.lastActive = false;
58689
58690     this.addEvents({
58691         /**
58692              * @event selectionchange
58693              * Fires when the selection changes
58694              * @param {SelectionModel} this
58695              */
58696             "selectionchange" : true,
58697         /**
58698              * @event afterselectionchange
58699              * Fires after the selection changes (eg. by key press or clicking)
58700              * @param {SelectionModel} this
58701              */
58702             "afterselectionchange" : true,
58703         /**
58704              * @event beforerowselect
58705              * Fires when a row is selected being selected, return false to cancel.
58706              * @param {SelectionModel} this
58707              * @param {Number} rowIndex The selected index
58708              * @param {Boolean} keepExisting False if other selections will be cleared
58709              */
58710             "beforerowselect" : true,
58711         /**
58712              * @event rowselect
58713              * Fires when a row is selected.
58714              * @param {SelectionModel} this
58715              * @param {Number} rowIndex The selected index
58716              * @param {Roo.data.Record} r The record
58717              */
58718             "rowselect" : true,
58719         /**
58720              * @event rowdeselect
58721              * Fires when a row is deselected.
58722              * @param {SelectionModel} this
58723              * @param {Number} rowIndex The selected index
58724              */
58725         "rowdeselect" : true
58726     });
58727     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
58728     this.locked = false;
58729 };
58730
58731 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
58732     /**
58733      * @cfg {Boolean} singleSelect
58734      * True to allow selection of only one row at a time (defaults to false)
58735      */
58736     singleSelect : false,
58737
58738     // private
58739     initEvents : function(){
58740
58741         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
58742             this.grid.on("mousedown", this.handleMouseDown, this);
58743         }else{ // allow click to work like normal
58744             this.grid.on("rowclick", this.handleDragableRowClick, this);
58745         }
58746
58747         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
58748             "up" : function(e){
58749                 if(!e.shiftKey){
58750                     this.selectPrevious(e.shiftKey);
58751                 }else if(this.last !== false && this.lastActive !== false){
58752                     var last = this.last;
58753                     this.selectRange(this.last,  this.lastActive-1);
58754                     this.grid.getView().focusRow(this.lastActive);
58755                     if(last !== false){
58756                         this.last = last;
58757                     }
58758                 }else{
58759                     this.selectFirstRow();
58760                 }
58761                 this.fireEvent("afterselectionchange", this);
58762             },
58763             "down" : function(e){
58764                 if(!e.shiftKey){
58765                     this.selectNext(e.shiftKey);
58766                 }else if(this.last !== false && this.lastActive !== false){
58767                     var last = this.last;
58768                     this.selectRange(this.last,  this.lastActive+1);
58769                     this.grid.getView().focusRow(this.lastActive);
58770                     if(last !== false){
58771                         this.last = last;
58772                     }
58773                 }else{
58774                     this.selectFirstRow();
58775                 }
58776                 this.fireEvent("afterselectionchange", this);
58777             },
58778             scope: this
58779         });
58780
58781         var view = this.grid.view;
58782         view.on("refresh", this.onRefresh, this);
58783         view.on("rowupdated", this.onRowUpdated, this);
58784         view.on("rowremoved", this.onRemove, this);
58785     },
58786
58787     // private
58788     onRefresh : function(){
58789         var ds = this.grid.dataSource, i, v = this.grid.view;
58790         var s = this.selections;
58791         s.each(function(r){
58792             if((i = ds.indexOfId(r.id)) != -1){
58793                 v.onRowSelect(i);
58794                 s.add(ds.getAt(i)); // updating the selection relate data
58795             }else{
58796                 s.remove(r);
58797             }
58798         });
58799     },
58800
58801     // private
58802     onRemove : function(v, index, r){
58803         this.selections.remove(r);
58804     },
58805
58806     // private
58807     onRowUpdated : function(v, index, r){
58808         if(this.isSelected(r)){
58809             v.onRowSelect(index);
58810         }
58811     },
58812
58813     /**
58814      * Select records.
58815      * @param {Array} records The records to select
58816      * @param {Boolean} keepExisting (optional) True to keep existing selections
58817      */
58818     selectRecords : function(records, keepExisting){
58819         if(!keepExisting){
58820             this.clearSelections();
58821         }
58822         var ds = this.grid.dataSource;
58823         for(var i = 0, len = records.length; i < len; i++){
58824             this.selectRow(ds.indexOf(records[i]), true);
58825         }
58826     },
58827
58828     /**
58829      * Gets the number of selected rows.
58830      * @return {Number}
58831      */
58832     getCount : function(){
58833         return this.selections.length;
58834     },
58835
58836     /**
58837      * Selects the first row in the grid.
58838      */
58839     selectFirstRow : function(){
58840         this.selectRow(0);
58841     },
58842
58843     /**
58844      * Select the last row.
58845      * @param {Boolean} keepExisting (optional) True to keep existing selections
58846      */
58847     selectLastRow : function(keepExisting){
58848         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
58849     },
58850
58851     /**
58852      * Selects the row immediately following the last selected row.
58853      * @param {Boolean} keepExisting (optional) True to keep existing selections
58854      */
58855     selectNext : function(keepExisting){
58856         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
58857             this.selectRow(this.last+1, keepExisting);
58858             this.grid.getView().focusRow(this.last);
58859         }
58860     },
58861
58862     /**
58863      * Selects the row that precedes the last selected row.
58864      * @param {Boolean} keepExisting (optional) True to keep existing selections
58865      */
58866     selectPrevious : function(keepExisting){
58867         if(this.last){
58868             this.selectRow(this.last-1, keepExisting);
58869             this.grid.getView().focusRow(this.last);
58870         }
58871     },
58872
58873     /**
58874      * Returns the selected records
58875      * @return {Array} Array of selected records
58876      */
58877     getSelections : function(){
58878         return [].concat(this.selections.items);
58879     },
58880
58881     /**
58882      * Returns the first selected record.
58883      * @return {Record}
58884      */
58885     getSelected : function(){
58886         return this.selections.itemAt(0);
58887     },
58888
58889
58890     /**
58891      * Clears all selections.
58892      */
58893     clearSelections : function(fast){
58894         if(this.locked) {
58895             return;
58896         }
58897         if(fast !== true){
58898             var ds = this.grid.dataSource;
58899             var s = this.selections;
58900             s.each(function(r){
58901                 this.deselectRow(ds.indexOfId(r.id));
58902             }, this);
58903             s.clear();
58904         }else{
58905             this.selections.clear();
58906         }
58907         this.last = false;
58908     },
58909
58910
58911     /**
58912      * Selects all rows.
58913      */
58914     selectAll : function(){
58915         if(this.locked) {
58916             return;
58917         }
58918         this.selections.clear();
58919         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
58920             this.selectRow(i, true);
58921         }
58922     },
58923
58924     /**
58925      * Returns True if there is a selection.
58926      * @return {Boolean}
58927      */
58928     hasSelection : function(){
58929         return this.selections.length > 0;
58930     },
58931
58932     /**
58933      * Returns True if the specified row is selected.
58934      * @param {Number/Record} record The record or index of the record to check
58935      * @return {Boolean}
58936      */
58937     isSelected : function(index){
58938         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
58939         return (r && this.selections.key(r.id) ? true : false);
58940     },
58941
58942     /**
58943      * Returns True if the specified record id is selected.
58944      * @param {String} id The id of record to check
58945      * @return {Boolean}
58946      */
58947     isIdSelected : function(id){
58948         return (this.selections.key(id) ? true : false);
58949     },
58950
58951     // private
58952     handleMouseDown : function(e, t){
58953         var view = this.grid.getView(), rowIndex;
58954         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
58955             return;
58956         };
58957         if(e.shiftKey && this.last !== false){
58958             var last = this.last;
58959             this.selectRange(last, rowIndex, e.ctrlKey);
58960             this.last = last; // reset the last
58961             view.focusRow(rowIndex);
58962         }else{
58963             var isSelected = this.isSelected(rowIndex);
58964             if(e.button !== 0 && isSelected){
58965                 view.focusRow(rowIndex);
58966             }else if(e.ctrlKey && isSelected){
58967                 this.deselectRow(rowIndex);
58968             }else if(!isSelected){
58969                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
58970                 view.focusRow(rowIndex);
58971             }
58972         }
58973         this.fireEvent("afterselectionchange", this);
58974     },
58975     // private
58976     handleDragableRowClick :  function(grid, rowIndex, e) 
58977     {
58978         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
58979             this.selectRow(rowIndex, false);
58980             grid.view.focusRow(rowIndex);
58981              this.fireEvent("afterselectionchange", this);
58982         }
58983     },
58984     
58985     /**
58986      * Selects multiple rows.
58987      * @param {Array} rows Array of the indexes of the row to select
58988      * @param {Boolean} keepExisting (optional) True to keep existing selections
58989      */
58990     selectRows : function(rows, keepExisting){
58991         if(!keepExisting){
58992             this.clearSelections();
58993         }
58994         for(var i = 0, len = rows.length; i < len; i++){
58995             this.selectRow(rows[i], true);
58996         }
58997     },
58998
58999     /**
59000      * Selects a range of rows. All rows in between startRow and endRow are also selected.
59001      * @param {Number} startRow The index of the first row in the range
59002      * @param {Number} endRow The index of the last row in the range
59003      * @param {Boolean} keepExisting (optional) True to retain existing selections
59004      */
59005     selectRange : function(startRow, endRow, keepExisting){
59006         if(this.locked) {
59007             return;
59008         }
59009         if(!keepExisting){
59010             this.clearSelections();
59011         }
59012         if(startRow <= endRow){
59013             for(var i = startRow; i <= endRow; i++){
59014                 this.selectRow(i, true);
59015             }
59016         }else{
59017             for(var i = startRow; i >= endRow; i--){
59018                 this.selectRow(i, true);
59019             }
59020         }
59021     },
59022
59023     /**
59024      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
59025      * @param {Number} startRow The index of the first row in the range
59026      * @param {Number} endRow The index of the last row in the range
59027      */
59028     deselectRange : function(startRow, endRow, preventViewNotify){
59029         if(this.locked) {
59030             return;
59031         }
59032         for(var i = startRow; i <= endRow; i++){
59033             this.deselectRow(i, preventViewNotify);
59034         }
59035     },
59036
59037     /**
59038      * Selects a row.
59039      * @param {Number} row The index of the row to select
59040      * @param {Boolean} keepExisting (optional) True to keep existing selections
59041      */
59042     selectRow : function(index, keepExisting, preventViewNotify){
59043         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
59044             return;
59045         }
59046         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
59047             if(!keepExisting || this.singleSelect){
59048                 this.clearSelections();
59049             }
59050             var r = this.grid.dataSource.getAt(index);
59051             this.selections.add(r);
59052             this.last = this.lastActive = index;
59053             if(!preventViewNotify){
59054                 this.grid.getView().onRowSelect(index);
59055             }
59056             this.fireEvent("rowselect", this, index, r);
59057             this.fireEvent("selectionchange", this);
59058         }
59059     },
59060
59061     /**
59062      * Deselects a row.
59063      * @param {Number} row The index of the row to deselect
59064      */
59065     deselectRow : function(index, preventViewNotify){
59066         if(this.locked) {
59067             return;
59068         }
59069         if(this.last == index){
59070             this.last = false;
59071         }
59072         if(this.lastActive == index){
59073             this.lastActive = false;
59074         }
59075         var r = this.grid.dataSource.getAt(index);
59076         this.selections.remove(r);
59077         if(!preventViewNotify){
59078             this.grid.getView().onRowDeselect(index);
59079         }
59080         this.fireEvent("rowdeselect", this, index);
59081         this.fireEvent("selectionchange", this);
59082     },
59083
59084     // private
59085     restoreLast : function(){
59086         if(this._last){
59087             this.last = this._last;
59088         }
59089     },
59090
59091     // private
59092     acceptsNav : function(row, col, cm){
59093         return !cm.isHidden(col) && cm.isCellEditable(col, row);
59094     },
59095
59096     // private
59097     onEditorKey : function(field, e){
59098         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
59099         if(k == e.TAB){
59100             e.stopEvent();
59101             ed.completeEdit();
59102             if(e.shiftKey){
59103                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
59104             }else{
59105                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
59106             }
59107         }else if(k == e.ENTER && !e.ctrlKey){
59108             e.stopEvent();
59109             ed.completeEdit();
59110             if(e.shiftKey){
59111                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
59112             }else{
59113                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
59114             }
59115         }else if(k == e.ESC){
59116             ed.cancelEdit();
59117         }
59118         if(newCell){
59119             g.startEditing(newCell[0], newCell[1]);
59120         }
59121     }
59122 });/*
59123  * Based on:
59124  * Ext JS Library 1.1.1
59125  * Copyright(c) 2006-2007, Ext JS, LLC.
59126  *
59127  * Originally Released Under LGPL - original licence link has changed is not relivant.
59128  *
59129  * Fork - LGPL
59130  * <script type="text/javascript">
59131  */
59132 /**
59133  * @class Roo.grid.CellSelectionModel
59134  * @extends Roo.grid.AbstractSelectionModel
59135  * This class provides the basic implementation for cell selection in a grid.
59136  * @constructor
59137  * @param {Object} config The object containing the configuration of this model.
59138  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
59139  */
59140 Roo.grid.CellSelectionModel = function(config){
59141     Roo.apply(this, config);
59142
59143     this.selection = null;
59144
59145     this.addEvents({
59146         /**
59147              * @event beforerowselect
59148              * Fires before a cell is selected.
59149              * @param {SelectionModel} this
59150              * @param {Number} rowIndex The selected row index
59151              * @param {Number} colIndex The selected cell index
59152              */
59153             "beforecellselect" : true,
59154         /**
59155              * @event cellselect
59156              * Fires when a cell is selected.
59157              * @param {SelectionModel} this
59158              * @param {Number} rowIndex The selected row index
59159              * @param {Number} colIndex The selected cell index
59160              */
59161             "cellselect" : true,
59162         /**
59163              * @event selectionchange
59164              * Fires when the active selection changes.
59165              * @param {SelectionModel} this
59166              * @param {Object} selection null for no selection or an object (o) with two properties
59167                 <ul>
59168                 <li>o.record: the record object for the row the selection is in</li>
59169                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
59170                 </ul>
59171              */
59172             "selectionchange" : true,
59173         /**
59174              * @event tabend
59175              * Fires when the tab (or enter) was pressed on the last editable cell
59176              * You can use this to trigger add new row.
59177              * @param {SelectionModel} this
59178              */
59179             "tabend" : true,
59180          /**
59181              * @event beforeeditnext
59182              * Fires before the next editable sell is made active
59183              * You can use this to skip to another cell or fire the tabend
59184              *    if you set cell to false
59185              * @param {Object} eventdata object : { cell : [ row, col ] } 
59186              */
59187             "beforeeditnext" : true
59188     });
59189     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
59190 };
59191
59192 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
59193     
59194     enter_is_tab: false,
59195
59196     /** @ignore */
59197     initEvents : function(){
59198         this.grid.on("mousedown", this.handleMouseDown, this);
59199         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
59200         var view = this.grid.view;
59201         view.on("refresh", this.onViewChange, this);
59202         view.on("rowupdated", this.onRowUpdated, this);
59203         view.on("beforerowremoved", this.clearSelections, this);
59204         view.on("beforerowsinserted", this.clearSelections, this);
59205         if(this.grid.isEditor){
59206             this.grid.on("beforeedit", this.beforeEdit,  this);
59207         }
59208     },
59209
59210         //private
59211     beforeEdit : function(e){
59212         this.select(e.row, e.column, false, true, e.record);
59213     },
59214
59215         //private
59216     onRowUpdated : function(v, index, r){
59217         if(this.selection && this.selection.record == r){
59218             v.onCellSelect(index, this.selection.cell[1]);
59219         }
59220     },
59221
59222         //private
59223     onViewChange : function(){
59224         this.clearSelections(true);
59225     },
59226
59227         /**
59228          * Returns the currently selected cell,.
59229          * @return {Array} The selected cell (row, column) or null if none selected.
59230          */
59231     getSelectedCell : function(){
59232         return this.selection ? this.selection.cell : null;
59233     },
59234
59235     /**
59236      * Clears all selections.
59237      * @param {Boolean} true to prevent the gridview from being notified about the change.
59238      */
59239     clearSelections : function(preventNotify){
59240         var s = this.selection;
59241         if(s){
59242             if(preventNotify !== true){
59243                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
59244             }
59245             this.selection = null;
59246             this.fireEvent("selectionchange", this, null);
59247         }
59248     },
59249
59250     /**
59251      * Returns true if there is a selection.
59252      * @return {Boolean}
59253      */
59254     hasSelection : function(){
59255         return this.selection ? true : false;
59256     },
59257
59258     /** @ignore */
59259     handleMouseDown : function(e, t){
59260         var v = this.grid.getView();
59261         if(this.isLocked()){
59262             return;
59263         };
59264         var row = v.findRowIndex(t);
59265         var cell = v.findCellIndex(t);
59266         if(row !== false && cell !== false){
59267             this.select(row, cell);
59268         }
59269     },
59270
59271     /**
59272      * Selects a cell.
59273      * @param {Number} rowIndex
59274      * @param {Number} collIndex
59275      */
59276     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
59277         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
59278             this.clearSelections();
59279             r = r || this.grid.dataSource.getAt(rowIndex);
59280             this.selection = {
59281                 record : r,
59282                 cell : [rowIndex, colIndex]
59283             };
59284             if(!preventViewNotify){
59285                 var v = this.grid.getView();
59286                 v.onCellSelect(rowIndex, colIndex);
59287                 if(preventFocus !== true){
59288                     v.focusCell(rowIndex, colIndex);
59289                 }
59290             }
59291             this.fireEvent("cellselect", this, rowIndex, colIndex);
59292             this.fireEvent("selectionchange", this, this.selection);
59293         }
59294     },
59295
59296         //private
59297     isSelectable : function(rowIndex, colIndex, cm){
59298         return !cm.isHidden(colIndex);
59299     },
59300
59301     /** @ignore */
59302     handleKeyDown : function(e){
59303         //Roo.log('Cell Sel Model handleKeyDown');
59304         if(!e.isNavKeyPress()){
59305             return;
59306         }
59307         var g = this.grid, s = this.selection;
59308         if(!s){
59309             e.stopEvent();
59310             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
59311             if(cell){
59312                 this.select(cell[0], cell[1]);
59313             }
59314             return;
59315         }
59316         var sm = this;
59317         var walk = function(row, col, step){
59318             return g.walkCells(row, col, step, sm.isSelectable,  sm);
59319         };
59320         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
59321         var newCell;
59322
59323       
59324
59325         switch(k){
59326             case e.TAB:
59327                 // handled by onEditorKey
59328                 if (g.isEditor && g.editing) {
59329                     return;
59330                 }
59331                 if(e.shiftKey) {
59332                     newCell = walk(r, c-1, -1);
59333                 } else {
59334                     newCell = walk(r, c+1, 1);
59335                 }
59336                 break;
59337             
59338             case e.DOWN:
59339                newCell = walk(r+1, c, 1);
59340                 break;
59341             
59342             case e.UP:
59343                 newCell = walk(r-1, c, -1);
59344                 break;
59345             
59346             case e.RIGHT:
59347                 newCell = walk(r, c+1, 1);
59348                 break;
59349             
59350             case e.LEFT:
59351                 newCell = walk(r, c-1, -1);
59352                 break;
59353             
59354             case e.ENTER:
59355                 
59356                 if(g.isEditor && !g.editing){
59357                    g.startEditing(r, c);
59358                    e.stopEvent();
59359                    return;
59360                 }
59361                 
59362                 
59363              break;
59364         };
59365         if(newCell){
59366             this.select(newCell[0], newCell[1]);
59367             e.stopEvent();
59368             
59369         }
59370     },
59371
59372     acceptsNav : function(row, col, cm){
59373         return !cm.isHidden(col) && cm.isCellEditable(col, row);
59374     },
59375     /**
59376      * Selects a cell.
59377      * @param {Number} field (not used) - as it's normally used as a listener
59378      * @param {Number} e - event - fake it by using
59379      *
59380      * var e = Roo.EventObjectImpl.prototype;
59381      * e.keyCode = e.TAB
59382      *
59383      * 
59384      */
59385     onEditorKey : function(field, e){
59386         
59387         var k = e.getKey(),
59388             newCell,
59389             g = this.grid,
59390             ed = g.activeEditor,
59391             forward = false;
59392         ///Roo.log('onEditorKey' + k);
59393         
59394         
59395         if (this.enter_is_tab && k == e.ENTER) {
59396             k = e.TAB;
59397         }
59398         
59399         if(k == e.TAB){
59400             if(e.shiftKey){
59401                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
59402             }else{
59403                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
59404                 forward = true;
59405             }
59406             
59407             e.stopEvent();
59408             
59409         } else if(k == e.ENTER &&  !e.ctrlKey){
59410             ed.completeEdit();
59411             e.stopEvent();
59412             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
59413         
59414                 } else if(k == e.ESC){
59415             ed.cancelEdit();
59416         }
59417                 
59418         if (newCell) {
59419             var ecall = { cell : newCell, forward : forward };
59420             this.fireEvent('beforeeditnext', ecall );
59421             newCell = ecall.cell;
59422                         forward = ecall.forward;
59423         }
59424                 
59425         if(newCell){
59426             //Roo.log('next cell after edit');
59427             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
59428         } else if (forward) {
59429             // tabbed past last
59430             this.fireEvent.defer(100, this, ['tabend',this]);
59431         }
59432     }
59433 });/*
59434  * Based on:
59435  * Ext JS Library 1.1.1
59436  * Copyright(c) 2006-2007, Ext JS, LLC.
59437  *
59438  * Originally Released Under LGPL - original licence link has changed is not relivant.
59439  *
59440  * Fork - LGPL
59441  * <script type="text/javascript">
59442  */
59443  
59444 /**
59445  * @class Roo.grid.EditorGrid
59446  * @extends Roo.grid.Grid
59447  * Class for creating and editable grid.
59448  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
59449  * The container MUST have some type of size defined for the grid to fill. The container will be 
59450  * automatically set to position relative if it isn't already.
59451  * @param {Object} dataSource The data model to bind to
59452  * @param {Object} colModel The column model with info about this grid's columns
59453  */
59454 Roo.grid.EditorGrid = function(container, config){
59455     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
59456     this.getGridEl().addClass("xedit-grid");
59457
59458     if(!this.selModel){
59459         this.selModel = new Roo.grid.CellSelectionModel();
59460     }
59461
59462     this.activeEditor = null;
59463
59464         this.addEvents({
59465             /**
59466              * @event beforeedit
59467              * Fires before cell editing is triggered. The edit event object has the following properties <br />
59468              * <ul style="padding:5px;padding-left:16px;">
59469              * <li>grid - This grid</li>
59470              * <li>record - The record being edited</li>
59471              * <li>field - The field name being edited</li>
59472              * <li>value - The value for the field being edited.</li>
59473              * <li>row - The grid row index</li>
59474              * <li>column - The grid column index</li>
59475              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
59476              * </ul>
59477              * @param {Object} e An edit event (see above for description)
59478              */
59479             "beforeedit" : true,
59480             /**
59481              * @event afteredit
59482              * Fires after a cell is edited. <br />
59483              * <ul style="padding:5px;padding-left:16px;">
59484              * <li>grid - This grid</li>
59485              * <li>record - The record being edited</li>
59486              * <li>field - The field name being edited</li>
59487              * <li>value - The value being set</li>
59488              * <li>originalValue - The original value for the field, before the edit.</li>
59489              * <li>row - The grid row index</li>
59490              * <li>column - The grid column index</li>
59491              * </ul>
59492              * @param {Object} e An edit event (see above for description)
59493              */
59494             "afteredit" : true,
59495             /**
59496              * @event validateedit
59497              * Fires after a cell is edited, but before the value is set in the record. 
59498          * You can use this to modify the value being set in the field, Return false
59499              * to cancel the change. The edit event object has the following properties <br />
59500              * <ul style="padding:5px;padding-left:16px;">
59501          * <li>editor - This editor</li>
59502              * <li>grid - This grid</li>
59503              * <li>record - The record being edited</li>
59504              * <li>field - The field name being edited</li>
59505              * <li>value - The value being set</li>
59506              * <li>originalValue - The original value for the field, before the edit.</li>
59507              * <li>row - The grid row index</li>
59508              * <li>column - The grid column index</li>
59509              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
59510              * </ul>
59511              * @param {Object} e An edit event (see above for description)
59512              */
59513             "validateedit" : true
59514         });
59515     this.on("bodyscroll", this.stopEditing,  this);
59516     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
59517 };
59518
59519 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
59520     /**
59521      * @cfg {Number} clicksToEdit
59522      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
59523      */
59524     clicksToEdit: 2,
59525
59526     // private
59527     isEditor : true,
59528     // private
59529     trackMouseOver: false, // causes very odd FF errors
59530
59531     onCellDblClick : function(g, row, col){
59532         this.startEditing(row, col);
59533     },
59534
59535     onEditComplete : function(ed, value, startValue){
59536         this.editing = false;
59537         this.activeEditor = null;
59538         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
59539         var r = ed.record;
59540         var field = this.colModel.getDataIndex(ed.col);
59541         var e = {
59542             grid: this,
59543             record: r,
59544             field: field,
59545             originalValue: startValue,
59546             value: value,
59547             row: ed.row,
59548             column: ed.col,
59549             cancel:false,
59550             editor: ed
59551         };
59552         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
59553         cell.show();
59554           
59555         if(String(value) !== String(startValue)){
59556             
59557             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
59558                 r.set(field, e.value);
59559                 // if we are dealing with a combo box..
59560                 // then we also set the 'name' colum to be the displayField
59561                 if (ed.field.displayField && ed.field.name) {
59562                     r.set(ed.field.name, ed.field.el.dom.value);
59563                 }
59564                 
59565                 delete e.cancel; //?? why!!!
59566                 this.fireEvent("afteredit", e);
59567             }
59568         } else {
59569             this.fireEvent("afteredit", e); // always fire it!
59570         }
59571         this.view.focusCell(ed.row, ed.col);
59572     },
59573
59574     /**
59575      * Starts editing the specified for the specified row/column
59576      * @param {Number} rowIndex
59577      * @param {Number} colIndex
59578      */
59579     startEditing : function(row, col){
59580         this.stopEditing();
59581         if(this.colModel.isCellEditable(col, row)){
59582             this.view.ensureVisible(row, col, true);
59583           
59584             var r = this.dataSource.getAt(row);
59585             var field = this.colModel.getDataIndex(col);
59586             var cell = Roo.get(this.view.getCell(row,col));
59587             var e = {
59588                 grid: this,
59589                 record: r,
59590                 field: field,
59591                 value: r.data[field],
59592                 row: row,
59593                 column: col,
59594                 cancel:false 
59595             };
59596             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
59597                 this.editing = true;
59598                 var ed = this.colModel.getCellEditor(col, row);
59599                 
59600                 if (!ed) {
59601                     return;
59602                 }
59603                 if(!ed.rendered){
59604                     ed.render(ed.parentEl || document.body);
59605                 }
59606                 ed.field.reset();
59607                
59608                 cell.hide();
59609                 
59610                 (function(){ // complex but required for focus issues in safari, ie and opera
59611                     ed.row = row;
59612                     ed.col = col;
59613                     ed.record = r;
59614                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
59615                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
59616                     this.activeEditor = ed;
59617                     var v = r.data[field];
59618                     ed.startEdit(this.view.getCell(row, col), v);
59619                     // combo's with 'displayField and name set
59620                     if (ed.field.displayField && ed.field.name) {
59621                         ed.field.el.dom.value = r.data[ed.field.name];
59622                     }
59623                     
59624                     
59625                 }).defer(50, this);
59626             }
59627         }
59628     },
59629         
59630     /**
59631      * Stops any active editing
59632      */
59633     stopEditing : function(){
59634         if(this.activeEditor){
59635             this.activeEditor.completeEdit();
59636         }
59637         this.activeEditor = null;
59638     },
59639         
59640          /**
59641      * Called to get grid's drag proxy text, by default returns this.ddText.
59642      * @return {String}
59643      */
59644     getDragDropText : function(){
59645         var count = this.selModel.getSelectedCell() ? 1 : 0;
59646         return String.format(this.ddText, count, count == 1 ? '' : 's');
59647     }
59648         
59649 });/*
59650  * Based on:
59651  * Ext JS Library 1.1.1
59652  * Copyright(c) 2006-2007, Ext JS, LLC.
59653  *
59654  * Originally Released Under LGPL - original licence link has changed is not relivant.
59655  *
59656  * Fork - LGPL
59657  * <script type="text/javascript">
59658  */
59659
59660 // private - not really -- you end up using it !
59661 // This is a support class used internally by the Grid components
59662
59663 /**
59664  * @class Roo.grid.GridEditor
59665  * @extends Roo.Editor
59666  * Class for creating and editable grid elements.
59667  * @param {Object} config any settings (must include field)
59668  */
59669 Roo.grid.GridEditor = function(field, config){
59670     if (!config && field.field) {
59671         config = field;
59672         field = Roo.factory(config.field, Roo.form);
59673     }
59674     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
59675     field.monitorTab = false;
59676 };
59677
59678 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
59679     
59680     /**
59681      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
59682      */
59683     
59684     alignment: "tl-tl",
59685     autoSize: "width",
59686     hideEl : false,
59687     cls: "x-small-editor x-grid-editor",
59688     shim:false,
59689     shadow:"frame"
59690 });/*
59691  * Based on:
59692  * Ext JS Library 1.1.1
59693  * Copyright(c) 2006-2007, Ext JS, LLC.
59694  *
59695  * Originally Released Under LGPL - original licence link has changed is not relivant.
59696  *
59697  * Fork - LGPL
59698  * <script type="text/javascript">
59699  */
59700   
59701
59702   
59703 Roo.grid.PropertyRecord = Roo.data.Record.create([
59704     {name:'name',type:'string'},  'value'
59705 ]);
59706
59707
59708 Roo.grid.PropertyStore = function(grid, source){
59709     this.grid = grid;
59710     this.store = new Roo.data.Store({
59711         recordType : Roo.grid.PropertyRecord
59712     });
59713     this.store.on('update', this.onUpdate,  this);
59714     if(source){
59715         this.setSource(source);
59716     }
59717     Roo.grid.PropertyStore.superclass.constructor.call(this);
59718 };
59719
59720
59721
59722 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
59723     setSource : function(o){
59724         this.source = o;
59725         this.store.removeAll();
59726         var data = [];
59727         for(var k in o){
59728             if(this.isEditableValue(o[k])){
59729                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
59730             }
59731         }
59732         this.store.loadRecords({records: data}, {}, true);
59733     },
59734
59735     onUpdate : function(ds, record, type){
59736         if(type == Roo.data.Record.EDIT){
59737             var v = record.data['value'];
59738             var oldValue = record.modified['value'];
59739             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
59740                 this.source[record.id] = v;
59741                 record.commit();
59742                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
59743             }else{
59744                 record.reject();
59745             }
59746         }
59747     },
59748
59749     getProperty : function(row){
59750        return this.store.getAt(row);
59751     },
59752
59753     isEditableValue: function(val){
59754         if(val && val instanceof Date){
59755             return true;
59756         }else if(typeof val == 'object' || typeof val == 'function'){
59757             return false;
59758         }
59759         return true;
59760     },
59761
59762     setValue : function(prop, value){
59763         this.source[prop] = value;
59764         this.store.getById(prop).set('value', value);
59765     },
59766
59767     getSource : function(){
59768         return this.source;
59769     }
59770 });
59771
59772 Roo.grid.PropertyColumnModel = function(grid, store){
59773     this.grid = grid;
59774     var g = Roo.grid;
59775     g.PropertyColumnModel.superclass.constructor.call(this, [
59776         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
59777         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
59778     ]);
59779     this.store = store;
59780     this.bselect = Roo.DomHelper.append(document.body, {
59781         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
59782             {tag: 'option', value: 'true', html: 'true'},
59783             {tag: 'option', value: 'false', html: 'false'}
59784         ]
59785     });
59786     Roo.id(this.bselect);
59787     var f = Roo.form;
59788     this.editors = {
59789         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
59790         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
59791         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
59792         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
59793         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
59794     };
59795     this.renderCellDelegate = this.renderCell.createDelegate(this);
59796     this.renderPropDelegate = this.renderProp.createDelegate(this);
59797 };
59798
59799 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
59800     
59801     
59802     nameText : 'Name',
59803     valueText : 'Value',
59804     
59805     dateFormat : 'm/j/Y',
59806     
59807     
59808     renderDate : function(dateVal){
59809         return dateVal.dateFormat(this.dateFormat);
59810     },
59811
59812     renderBool : function(bVal){
59813         return bVal ? 'true' : 'false';
59814     },
59815
59816     isCellEditable : function(colIndex, rowIndex){
59817         return colIndex == 1;
59818     },
59819
59820     getRenderer : function(col){
59821         return col == 1 ?
59822             this.renderCellDelegate : this.renderPropDelegate;
59823     },
59824
59825     renderProp : function(v){
59826         return this.getPropertyName(v);
59827     },
59828
59829     renderCell : function(val){
59830         var rv = val;
59831         if(val instanceof Date){
59832             rv = this.renderDate(val);
59833         }else if(typeof val == 'boolean'){
59834             rv = this.renderBool(val);
59835         }
59836         return Roo.util.Format.htmlEncode(rv);
59837     },
59838
59839     getPropertyName : function(name){
59840         var pn = this.grid.propertyNames;
59841         return pn && pn[name] ? pn[name] : name;
59842     },
59843
59844     getCellEditor : function(colIndex, rowIndex){
59845         var p = this.store.getProperty(rowIndex);
59846         var n = p.data['name'], val = p.data['value'];
59847         
59848         if(typeof(this.grid.customEditors[n]) == 'string'){
59849             return this.editors[this.grid.customEditors[n]];
59850         }
59851         if(typeof(this.grid.customEditors[n]) != 'undefined'){
59852             return this.grid.customEditors[n];
59853         }
59854         if(val instanceof Date){
59855             return this.editors['date'];
59856         }else if(typeof val == 'number'){
59857             return this.editors['number'];
59858         }else if(typeof val == 'boolean'){
59859             return this.editors['boolean'];
59860         }else{
59861             return this.editors['string'];
59862         }
59863     }
59864 });
59865
59866 /**
59867  * @class Roo.grid.PropertyGrid
59868  * @extends Roo.grid.EditorGrid
59869  * This class represents the  interface of a component based property grid control.
59870  * <br><br>Usage:<pre><code>
59871  var grid = new Roo.grid.PropertyGrid("my-container-id", {
59872       
59873  });
59874  // set any options
59875  grid.render();
59876  * </code></pre>
59877   
59878  * @constructor
59879  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
59880  * The container MUST have some type of size defined for the grid to fill. The container will be
59881  * automatically set to position relative if it isn't already.
59882  * @param {Object} config A config object that sets properties on this grid.
59883  */
59884 Roo.grid.PropertyGrid = function(container, config){
59885     config = config || {};
59886     var store = new Roo.grid.PropertyStore(this);
59887     this.store = store;
59888     var cm = new Roo.grid.PropertyColumnModel(this, store);
59889     store.store.sort('name', 'ASC');
59890     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
59891         ds: store.store,
59892         cm: cm,
59893         enableColLock:false,
59894         enableColumnMove:false,
59895         stripeRows:false,
59896         trackMouseOver: false,
59897         clicksToEdit:1
59898     }, config));
59899     this.getGridEl().addClass('x-props-grid');
59900     this.lastEditRow = null;
59901     this.on('columnresize', this.onColumnResize, this);
59902     this.addEvents({
59903          /**
59904              * @event beforepropertychange
59905              * Fires before a property changes (return false to stop?)
59906              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
59907              * @param {String} id Record Id
59908              * @param {String} newval New Value
59909          * @param {String} oldval Old Value
59910              */
59911         "beforepropertychange": true,
59912         /**
59913              * @event propertychange
59914              * Fires after a property changes
59915              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
59916              * @param {String} id Record Id
59917              * @param {String} newval New Value
59918          * @param {String} oldval Old Value
59919              */
59920         "propertychange": true
59921     });
59922     this.customEditors = this.customEditors || {};
59923 };
59924 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
59925     
59926      /**
59927      * @cfg {Object} customEditors map of colnames=> custom editors.
59928      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
59929      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
59930      * false disables editing of the field.
59931          */
59932     
59933       /**
59934      * @cfg {Object} propertyNames map of property Names to their displayed value
59935          */
59936     
59937     render : function(){
59938         Roo.grid.PropertyGrid.superclass.render.call(this);
59939         this.autoSize.defer(100, this);
59940     },
59941
59942     autoSize : function(){
59943         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
59944         if(this.view){
59945             this.view.fitColumns();
59946         }
59947     },
59948
59949     onColumnResize : function(){
59950         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
59951         this.autoSize();
59952     },
59953     /**
59954      * Sets the data for the Grid
59955      * accepts a Key => Value object of all the elements avaiable.
59956      * @param {Object} data  to appear in grid.
59957      */
59958     setSource : function(source){
59959         this.store.setSource(source);
59960         //this.autoSize();
59961     },
59962     /**
59963      * Gets all the data from the grid.
59964      * @return {Object} data  data stored in grid
59965      */
59966     getSource : function(){
59967         return this.store.getSource();
59968     }
59969 });/*
59970   
59971  * Licence LGPL
59972  
59973  */
59974  
59975 /**
59976  * @class Roo.grid.Calendar
59977  * @extends Roo.util.Grid
59978  * This class extends the Grid to provide a calendar widget
59979  * <br><br>Usage:<pre><code>
59980  var grid = new Roo.grid.Calendar("my-container-id", {
59981      ds: myDataStore,
59982      cm: myColModel,
59983      selModel: mySelectionModel,
59984      autoSizeColumns: true,
59985      monitorWindowResize: false,
59986      trackMouseOver: true
59987      eventstore : real data store..
59988  });
59989  // set any options
59990  grid.render();
59991   
59992   * @constructor
59993  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
59994  * The container MUST have some type of size defined for the grid to fill. The container will be
59995  * automatically set to position relative if it isn't already.
59996  * @param {Object} config A config object that sets properties on this grid.
59997  */
59998 Roo.grid.Calendar = function(container, config){
59999         // initialize the container
60000         this.container = Roo.get(container);
60001         this.container.update("");
60002         this.container.setStyle("overflow", "hidden");
60003     this.container.addClass('x-grid-container');
60004
60005     this.id = this.container.id;
60006
60007     Roo.apply(this, config);
60008     // check and correct shorthanded configs
60009     
60010     var rows = [];
60011     var d =1;
60012     for (var r = 0;r < 6;r++) {
60013         
60014         rows[r]=[];
60015         for (var c =0;c < 7;c++) {
60016             rows[r][c]= '';
60017         }
60018     }
60019     if (this.eventStore) {
60020         this.eventStore= Roo.factory(this.eventStore, Roo.data);
60021         this.eventStore.on('load',this.onLoad, this);
60022         this.eventStore.on('beforeload',this.clearEvents, this);
60023          
60024     }
60025     
60026     this.dataSource = new Roo.data.Store({
60027             proxy: new Roo.data.MemoryProxy(rows),
60028             reader: new Roo.data.ArrayReader({}, [
60029                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
60030     });
60031
60032     this.dataSource.load();
60033     this.ds = this.dataSource;
60034     this.ds.xmodule = this.xmodule || false;
60035     
60036     
60037     var cellRender = function(v,x,r)
60038     {
60039         return String.format(
60040             '<div class="fc-day  fc-widget-content"><div>' +
60041                 '<div class="fc-event-container"></div>' +
60042                 '<div class="fc-day-number">{0}</div>'+
60043                 
60044                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
60045             '</div></div>', v);
60046     
60047     }
60048     
60049     
60050     this.colModel = new Roo.grid.ColumnModel( [
60051         {
60052             xtype: 'ColumnModel',
60053             xns: Roo.grid,
60054             dataIndex : 'weekday0',
60055             header : 'Sunday',
60056             renderer : cellRender
60057         },
60058         {
60059             xtype: 'ColumnModel',
60060             xns: Roo.grid,
60061             dataIndex : 'weekday1',
60062             header : 'Monday',
60063             renderer : cellRender
60064         },
60065         {
60066             xtype: 'ColumnModel',
60067             xns: Roo.grid,
60068             dataIndex : 'weekday2',
60069             header : 'Tuesday',
60070             renderer : cellRender
60071         },
60072         {
60073             xtype: 'ColumnModel',
60074             xns: Roo.grid,
60075             dataIndex : 'weekday3',
60076             header : 'Wednesday',
60077             renderer : cellRender
60078         },
60079         {
60080             xtype: 'ColumnModel',
60081             xns: Roo.grid,
60082             dataIndex : 'weekday4',
60083             header : 'Thursday',
60084             renderer : cellRender
60085         },
60086         {
60087             xtype: 'ColumnModel',
60088             xns: Roo.grid,
60089             dataIndex : 'weekday5',
60090             header : 'Friday',
60091             renderer : cellRender
60092         },
60093         {
60094             xtype: 'ColumnModel',
60095             xns: Roo.grid,
60096             dataIndex : 'weekday6',
60097             header : 'Saturday',
60098             renderer : cellRender
60099         }
60100     ]);
60101     this.cm = this.colModel;
60102     this.cm.xmodule = this.xmodule || false;
60103  
60104         
60105           
60106     //this.selModel = new Roo.grid.CellSelectionModel();
60107     //this.sm = this.selModel;
60108     //this.selModel.init(this);
60109     
60110     
60111     if(this.width){
60112         this.container.setWidth(this.width);
60113     }
60114
60115     if(this.height){
60116         this.container.setHeight(this.height);
60117     }
60118     /** @private */
60119         this.addEvents({
60120         // raw events
60121         /**
60122          * @event click
60123          * The raw click event for the entire grid.
60124          * @param {Roo.EventObject} e
60125          */
60126         "click" : true,
60127         /**
60128          * @event dblclick
60129          * The raw dblclick event for the entire grid.
60130          * @param {Roo.EventObject} e
60131          */
60132         "dblclick" : true,
60133         /**
60134          * @event contextmenu
60135          * The raw contextmenu event for the entire grid.
60136          * @param {Roo.EventObject} e
60137          */
60138         "contextmenu" : true,
60139         /**
60140          * @event mousedown
60141          * The raw mousedown event for the entire grid.
60142          * @param {Roo.EventObject} e
60143          */
60144         "mousedown" : true,
60145         /**
60146          * @event mouseup
60147          * The raw mouseup event for the entire grid.
60148          * @param {Roo.EventObject} e
60149          */
60150         "mouseup" : true,
60151         /**
60152          * @event mouseover
60153          * The raw mouseover event for the entire grid.
60154          * @param {Roo.EventObject} e
60155          */
60156         "mouseover" : true,
60157         /**
60158          * @event mouseout
60159          * The raw mouseout event for the entire grid.
60160          * @param {Roo.EventObject} e
60161          */
60162         "mouseout" : true,
60163         /**
60164          * @event keypress
60165          * The raw keypress event for the entire grid.
60166          * @param {Roo.EventObject} e
60167          */
60168         "keypress" : true,
60169         /**
60170          * @event keydown
60171          * The raw keydown event for the entire grid.
60172          * @param {Roo.EventObject} e
60173          */
60174         "keydown" : true,
60175
60176         // custom events
60177
60178         /**
60179          * @event cellclick
60180          * Fires when a cell is clicked
60181          * @param {Grid} this
60182          * @param {Number} rowIndex
60183          * @param {Number} columnIndex
60184          * @param {Roo.EventObject} e
60185          */
60186         "cellclick" : true,
60187         /**
60188          * @event celldblclick
60189          * Fires when a cell is double clicked
60190          * @param {Grid} this
60191          * @param {Number} rowIndex
60192          * @param {Number} columnIndex
60193          * @param {Roo.EventObject} e
60194          */
60195         "celldblclick" : true,
60196         /**
60197          * @event rowclick
60198          * Fires when a row is clicked
60199          * @param {Grid} this
60200          * @param {Number} rowIndex
60201          * @param {Roo.EventObject} e
60202          */
60203         "rowclick" : true,
60204         /**
60205          * @event rowdblclick
60206          * Fires when a row is double clicked
60207          * @param {Grid} this
60208          * @param {Number} rowIndex
60209          * @param {Roo.EventObject} e
60210          */
60211         "rowdblclick" : true,
60212         /**
60213          * @event headerclick
60214          * Fires when a header is clicked
60215          * @param {Grid} this
60216          * @param {Number} columnIndex
60217          * @param {Roo.EventObject} e
60218          */
60219         "headerclick" : true,
60220         /**
60221          * @event headerdblclick
60222          * Fires when a header cell is double clicked
60223          * @param {Grid} this
60224          * @param {Number} columnIndex
60225          * @param {Roo.EventObject} e
60226          */
60227         "headerdblclick" : true,
60228         /**
60229          * @event rowcontextmenu
60230          * Fires when a row is right clicked
60231          * @param {Grid} this
60232          * @param {Number} rowIndex
60233          * @param {Roo.EventObject} e
60234          */
60235         "rowcontextmenu" : true,
60236         /**
60237          * @event cellcontextmenu
60238          * Fires when a cell is right clicked
60239          * @param {Grid} this
60240          * @param {Number} rowIndex
60241          * @param {Number} cellIndex
60242          * @param {Roo.EventObject} e
60243          */
60244          "cellcontextmenu" : true,
60245         /**
60246          * @event headercontextmenu
60247          * Fires when a header is right clicked
60248          * @param {Grid} this
60249          * @param {Number} columnIndex
60250          * @param {Roo.EventObject} e
60251          */
60252         "headercontextmenu" : true,
60253         /**
60254          * @event bodyscroll
60255          * Fires when the body element is scrolled
60256          * @param {Number} scrollLeft
60257          * @param {Number} scrollTop
60258          */
60259         "bodyscroll" : true,
60260         /**
60261          * @event columnresize
60262          * Fires when the user resizes a column
60263          * @param {Number} columnIndex
60264          * @param {Number} newSize
60265          */
60266         "columnresize" : true,
60267         /**
60268          * @event columnmove
60269          * Fires when the user moves a column
60270          * @param {Number} oldIndex
60271          * @param {Number} newIndex
60272          */
60273         "columnmove" : true,
60274         /**
60275          * @event startdrag
60276          * Fires when row(s) start being dragged
60277          * @param {Grid} this
60278          * @param {Roo.GridDD} dd The drag drop object
60279          * @param {event} e The raw browser event
60280          */
60281         "startdrag" : true,
60282         /**
60283          * @event enddrag
60284          * Fires when a drag operation is complete
60285          * @param {Grid} this
60286          * @param {Roo.GridDD} dd The drag drop object
60287          * @param {event} e The raw browser event
60288          */
60289         "enddrag" : true,
60290         /**
60291          * @event dragdrop
60292          * Fires when dragged row(s) are dropped on a valid DD target
60293          * @param {Grid} this
60294          * @param {Roo.GridDD} dd The drag drop object
60295          * @param {String} targetId The target drag drop object
60296          * @param {event} e The raw browser event
60297          */
60298         "dragdrop" : true,
60299         /**
60300          * @event dragover
60301          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
60302          * @param {Grid} this
60303          * @param {Roo.GridDD} dd The drag drop object
60304          * @param {String} targetId The target drag drop object
60305          * @param {event} e The raw browser event
60306          */
60307         "dragover" : true,
60308         /**
60309          * @event dragenter
60310          *  Fires when the dragged row(s) first cross another DD target while being dragged
60311          * @param {Grid} this
60312          * @param {Roo.GridDD} dd The drag drop object
60313          * @param {String} targetId The target drag drop object
60314          * @param {event} e The raw browser event
60315          */
60316         "dragenter" : true,
60317         /**
60318          * @event dragout
60319          * Fires when the dragged row(s) leave another DD target while being dragged
60320          * @param {Grid} this
60321          * @param {Roo.GridDD} dd The drag drop object
60322          * @param {String} targetId The target drag drop object
60323          * @param {event} e The raw browser event
60324          */
60325         "dragout" : true,
60326         /**
60327          * @event rowclass
60328          * Fires when a row is rendered, so you can change add a style to it.
60329          * @param {GridView} gridview   The grid view
60330          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
60331          */
60332         'rowclass' : true,
60333
60334         /**
60335          * @event render
60336          * Fires when the grid is rendered
60337          * @param {Grid} grid
60338          */
60339         'render' : true,
60340             /**
60341              * @event select
60342              * Fires when a date is selected
60343              * @param {DatePicker} this
60344              * @param {Date} date The selected date
60345              */
60346         'select': true,
60347         /**
60348              * @event monthchange
60349              * Fires when the displayed month changes 
60350              * @param {DatePicker} this
60351              * @param {Date} date The selected month
60352              */
60353         'monthchange': true,
60354         /**
60355              * @event evententer
60356              * Fires when mouse over an event
60357              * @param {Calendar} this
60358              * @param {event} Event
60359              */
60360         'evententer': true,
60361         /**
60362              * @event eventleave
60363              * Fires when the mouse leaves an
60364              * @param {Calendar} this
60365              * @param {event}
60366              */
60367         'eventleave': true,
60368         /**
60369              * @event eventclick
60370              * Fires when the mouse click an
60371              * @param {Calendar} this
60372              * @param {event}
60373              */
60374         'eventclick': true,
60375         /**
60376              * @event eventrender
60377              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
60378              * @param {Calendar} this
60379              * @param {data} data to be modified
60380              */
60381         'eventrender': true
60382         
60383     });
60384
60385     Roo.grid.Grid.superclass.constructor.call(this);
60386     this.on('render', function() {
60387         this.view.el.addClass('x-grid-cal'); 
60388         
60389         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
60390
60391     },this);
60392     
60393     if (!Roo.grid.Calendar.style) {
60394         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
60395             
60396             
60397             '.x-grid-cal .x-grid-col' :  {
60398                 height: 'auto !important',
60399                 'vertical-align': 'top'
60400             },
60401             '.x-grid-cal  .fc-event-hori' : {
60402                 height: '14px'
60403             }
60404              
60405             
60406         }, Roo.id());
60407     }
60408
60409     
60410     
60411 };
60412 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
60413     /**
60414      * @cfg {Store} eventStore The store that loads events.
60415      */
60416     eventStore : 25,
60417
60418      
60419     activeDate : false,
60420     startDay : 0,
60421     autoWidth : true,
60422     monitorWindowResize : false,
60423
60424     
60425     resizeColumns : function() {
60426         var col = (this.view.el.getWidth() / 7) - 3;
60427         // loop through cols, and setWidth
60428         for(var i =0 ; i < 7 ; i++){
60429             this.cm.setColumnWidth(i, col);
60430         }
60431     },
60432      setDate :function(date) {
60433         
60434         Roo.log('setDate?');
60435         
60436         this.resizeColumns();
60437         var vd = this.activeDate;
60438         this.activeDate = date;
60439 //        if(vd && this.el){
60440 //            var t = date.getTime();
60441 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
60442 //                Roo.log('using add remove');
60443 //                
60444 //                this.fireEvent('monthchange', this, date);
60445 //                
60446 //                this.cells.removeClass("fc-state-highlight");
60447 //                this.cells.each(function(c){
60448 //                   if(c.dateValue == t){
60449 //                       c.addClass("fc-state-highlight");
60450 //                       setTimeout(function(){
60451 //                            try{c.dom.firstChild.focus();}catch(e){}
60452 //                       }, 50);
60453 //                       return false;
60454 //                   }
60455 //                   return true;
60456 //                });
60457 //                return;
60458 //            }
60459 //        }
60460         
60461         var days = date.getDaysInMonth();
60462         
60463         var firstOfMonth = date.getFirstDateOfMonth();
60464         var startingPos = firstOfMonth.getDay()-this.startDay;
60465         
60466         if(startingPos < this.startDay){
60467             startingPos += 7;
60468         }
60469         
60470         var pm = date.add(Date.MONTH, -1);
60471         var prevStart = pm.getDaysInMonth()-startingPos;
60472 //        
60473         
60474         
60475         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
60476         
60477         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
60478         //this.cells.addClassOnOver('fc-state-hover');
60479         
60480         var cells = this.cells.elements;
60481         var textEls = this.textNodes;
60482         
60483         //Roo.each(cells, function(cell){
60484         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
60485         //});
60486         
60487         days += startingPos;
60488
60489         // convert everything to numbers so it's fast
60490         var day = 86400000;
60491         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
60492         //Roo.log(d);
60493         //Roo.log(pm);
60494         //Roo.log(prevStart);
60495         
60496         var today = new Date().clearTime().getTime();
60497         var sel = date.clearTime().getTime();
60498         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
60499         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
60500         var ddMatch = this.disabledDatesRE;
60501         var ddText = this.disabledDatesText;
60502         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
60503         var ddaysText = this.disabledDaysText;
60504         var format = this.format;
60505         
60506         var setCellClass = function(cal, cell){
60507             
60508             //Roo.log('set Cell Class');
60509             cell.title = "";
60510             var t = d.getTime();
60511             
60512             //Roo.log(d);
60513             
60514             
60515             cell.dateValue = t;
60516             if(t == today){
60517                 cell.className += " fc-today";
60518                 cell.className += " fc-state-highlight";
60519                 cell.title = cal.todayText;
60520             }
60521             if(t == sel){
60522                 // disable highlight in other month..
60523                 cell.className += " fc-state-highlight";
60524                 
60525             }
60526             // disabling
60527             if(t < min) {
60528                 //cell.className = " fc-state-disabled";
60529                 cell.title = cal.minText;
60530                 return;
60531             }
60532             if(t > max) {
60533                 //cell.className = " fc-state-disabled";
60534                 cell.title = cal.maxText;
60535                 return;
60536             }
60537             if(ddays){
60538                 if(ddays.indexOf(d.getDay()) != -1){
60539                     // cell.title = ddaysText;
60540                    // cell.className = " fc-state-disabled";
60541                 }
60542             }
60543             if(ddMatch && format){
60544                 var fvalue = d.dateFormat(format);
60545                 if(ddMatch.test(fvalue)){
60546                     cell.title = ddText.replace("%0", fvalue);
60547                    cell.className = " fc-state-disabled";
60548                 }
60549             }
60550             
60551             if (!cell.initialClassName) {
60552                 cell.initialClassName = cell.dom.className;
60553             }
60554             
60555             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
60556         };
60557
60558         var i = 0;
60559         
60560         for(; i < startingPos; i++) {
60561             cells[i].dayName =  (++prevStart);
60562             Roo.log(textEls[i]);
60563             d.setDate(d.getDate()+1);
60564             
60565             //cells[i].className = "fc-past fc-other-month";
60566             setCellClass(this, cells[i]);
60567         }
60568         
60569         var intDay = 0;
60570         
60571         for(; i < days; i++){
60572             intDay = i - startingPos + 1;
60573             cells[i].dayName =  (intDay);
60574             d.setDate(d.getDate()+1);
60575             
60576             cells[i].className = ''; // "x-date-active";
60577             setCellClass(this, cells[i]);
60578         }
60579         var extraDays = 0;
60580         
60581         for(; i < 42; i++) {
60582             //textEls[i].innerHTML = (++extraDays);
60583             
60584             d.setDate(d.getDate()+1);
60585             cells[i].dayName = (++extraDays);
60586             cells[i].className = "fc-future fc-other-month";
60587             setCellClass(this, cells[i]);
60588         }
60589         
60590         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
60591         
60592         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
60593         
60594         // this will cause all the cells to mis
60595         var rows= [];
60596         var i =0;
60597         for (var r = 0;r < 6;r++) {
60598             for (var c =0;c < 7;c++) {
60599                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
60600             }    
60601         }
60602         
60603         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
60604         for(i=0;i<cells.length;i++) {
60605             
60606             this.cells.elements[i].dayName = cells[i].dayName ;
60607             this.cells.elements[i].className = cells[i].className;
60608             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
60609             this.cells.elements[i].title = cells[i].title ;
60610             this.cells.elements[i].dateValue = cells[i].dateValue ;
60611         }
60612         
60613         
60614         
60615         
60616         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
60617         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
60618         
60619         ////if(totalRows != 6){
60620             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
60621            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
60622        // }
60623         
60624         this.fireEvent('monthchange', this, date);
60625         
60626         
60627     },
60628  /**
60629      * Returns the grid's SelectionModel.
60630      * @return {SelectionModel}
60631      */
60632     getSelectionModel : function(){
60633         if(!this.selModel){
60634             this.selModel = new Roo.grid.CellSelectionModel();
60635         }
60636         return this.selModel;
60637     },
60638
60639     load: function() {
60640         this.eventStore.load()
60641         
60642         
60643         
60644     },
60645     
60646     findCell : function(dt) {
60647         dt = dt.clearTime().getTime();
60648         var ret = false;
60649         this.cells.each(function(c){
60650             //Roo.log("check " +c.dateValue + '?=' + dt);
60651             if(c.dateValue == dt){
60652                 ret = c;
60653                 return false;
60654             }
60655             return true;
60656         });
60657         
60658         return ret;
60659     },
60660     
60661     findCells : function(rec) {
60662         var s = rec.data.start_dt.clone().clearTime().getTime();
60663        // Roo.log(s);
60664         var e= rec.data.end_dt.clone().clearTime().getTime();
60665        // Roo.log(e);
60666         var ret = [];
60667         this.cells.each(function(c){
60668              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
60669             
60670             if(c.dateValue > e){
60671                 return ;
60672             }
60673             if(c.dateValue < s){
60674                 return ;
60675             }
60676             ret.push(c);
60677         });
60678         
60679         return ret;    
60680     },
60681     
60682     findBestRow: function(cells)
60683     {
60684         var ret = 0;
60685         
60686         for (var i =0 ; i < cells.length;i++) {
60687             ret  = Math.max(cells[i].rows || 0,ret);
60688         }
60689         return ret;
60690         
60691     },
60692     
60693     
60694     addItem : function(rec)
60695     {
60696         // look for vertical location slot in
60697         var cells = this.findCells(rec);
60698         
60699         rec.row = this.findBestRow(cells);
60700         
60701         // work out the location.
60702         
60703         var crow = false;
60704         var rows = [];
60705         for(var i =0; i < cells.length; i++) {
60706             if (!crow) {
60707                 crow = {
60708                     start : cells[i],
60709                     end :  cells[i]
60710                 };
60711                 continue;
60712             }
60713             if (crow.start.getY() == cells[i].getY()) {
60714                 // on same row.
60715                 crow.end = cells[i];
60716                 continue;
60717             }
60718             // different row.
60719             rows.push(crow);
60720             crow = {
60721                 start: cells[i],
60722                 end : cells[i]
60723             };
60724             
60725         }
60726         
60727         rows.push(crow);
60728         rec.els = [];
60729         rec.rows = rows;
60730         rec.cells = cells;
60731         for (var i = 0; i < cells.length;i++) {
60732             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
60733             
60734         }
60735         
60736         
60737     },
60738     
60739     clearEvents: function() {
60740         
60741         if (!this.eventStore.getCount()) {
60742             return;
60743         }
60744         // reset number of rows in cells.
60745         Roo.each(this.cells.elements, function(c){
60746             c.rows = 0;
60747         });
60748         
60749         this.eventStore.each(function(e) {
60750             this.clearEvent(e);
60751         },this);
60752         
60753     },
60754     
60755     clearEvent : function(ev)
60756     {
60757         if (ev.els) {
60758             Roo.each(ev.els, function(el) {
60759                 el.un('mouseenter' ,this.onEventEnter, this);
60760                 el.un('mouseleave' ,this.onEventLeave, this);
60761                 el.remove();
60762             },this);
60763             ev.els = [];
60764         }
60765     },
60766     
60767     
60768     renderEvent : function(ev,ctr) {
60769         if (!ctr) {
60770              ctr = this.view.el.select('.fc-event-container',true).first();
60771         }
60772         
60773          
60774         this.clearEvent(ev);
60775             //code
60776        
60777         
60778         
60779         ev.els = [];
60780         var cells = ev.cells;
60781         var rows = ev.rows;
60782         this.fireEvent('eventrender', this, ev);
60783         
60784         for(var i =0; i < rows.length; i++) {
60785             
60786             cls = '';
60787             if (i == 0) {
60788                 cls += ' fc-event-start';
60789             }
60790             if ((i+1) == rows.length) {
60791                 cls += ' fc-event-end';
60792             }
60793             
60794             //Roo.log(ev.data);
60795             // how many rows should it span..
60796             var cg = this.eventTmpl.append(ctr,Roo.apply({
60797                 fccls : cls
60798                 
60799             }, ev.data) , true);
60800             
60801             
60802             cg.on('mouseenter' ,this.onEventEnter, this, ev);
60803             cg.on('mouseleave' ,this.onEventLeave, this, ev);
60804             cg.on('click', this.onEventClick, this, ev);
60805             
60806             ev.els.push(cg);
60807             
60808             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
60809             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
60810             //Roo.log(cg);
60811              
60812             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
60813             cg.setWidth(ebox.right - sbox.x -2);
60814         }
60815     },
60816     
60817     renderEvents: function()
60818     {   
60819         // first make sure there is enough space..
60820         
60821         if (!this.eventTmpl) {
60822             this.eventTmpl = new Roo.Template(
60823                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
60824                     '<div class="fc-event-inner">' +
60825                         '<span class="fc-event-time">{time}</span>' +
60826                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
60827                     '</div>' +
60828                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
60829                 '</div>'
60830             );
60831                 
60832         }
60833                
60834         
60835         
60836         this.cells.each(function(c) {
60837             //Roo.log(c.select('.fc-day-content div',true).first());
60838             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
60839         });
60840         
60841         var ctr = this.view.el.select('.fc-event-container',true).first();
60842         
60843         var cls;
60844         this.eventStore.each(function(ev){
60845             
60846             this.renderEvent(ev);
60847              
60848              
60849         }, this);
60850         this.view.layout();
60851         
60852     },
60853     
60854     onEventEnter: function (e, el,event,d) {
60855         this.fireEvent('evententer', this, el, event);
60856     },
60857     
60858     onEventLeave: function (e, el,event,d) {
60859         this.fireEvent('eventleave', this, el, event);
60860     },
60861     
60862     onEventClick: function (e, el,event,d) {
60863         this.fireEvent('eventclick', this, el, event);
60864     },
60865     
60866     onMonthChange: function () {
60867         this.store.load();
60868     },
60869     
60870     onLoad: function () {
60871         
60872         //Roo.log('calendar onload');
60873 //         
60874         if(this.eventStore.getCount() > 0){
60875             
60876            
60877             
60878             this.eventStore.each(function(d){
60879                 
60880                 
60881                 // FIXME..
60882                 var add =   d.data;
60883                 if (typeof(add.end_dt) == 'undefined')  {
60884                     Roo.log("Missing End time in calendar data: ");
60885                     Roo.log(d);
60886                     return;
60887                 }
60888                 if (typeof(add.start_dt) == 'undefined')  {
60889                     Roo.log("Missing Start time in calendar data: ");
60890                     Roo.log(d);
60891                     return;
60892                 }
60893                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
60894                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
60895                 add.id = add.id || d.id;
60896                 add.title = add.title || '??';
60897                 
60898                 this.addItem(d);
60899                 
60900              
60901             },this);
60902         }
60903         
60904         this.renderEvents();
60905     }
60906     
60907
60908 });
60909 /*
60910  grid : {
60911                 xtype: 'Grid',
60912                 xns: Roo.grid,
60913                 listeners : {
60914                     render : function ()
60915                     {
60916                         _this.grid = this;
60917                         
60918                         if (!this.view.el.hasClass('course-timesheet')) {
60919                             this.view.el.addClass('course-timesheet');
60920                         }
60921                         if (this.tsStyle) {
60922                             this.ds.load({});
60923                             return; 
60924                         }
60925                         Roo.log('width');
60926                         Roo.log(_this.grid.view.el.getWidth());
60927                         
60928                         
60929                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
60930                             '.course-timesheet .x-grid-row' : {
60931                                 height: '80px'
60932                             },
60933                             '.x-grid-row td' : {
60934                                 'vertical-align' : 0
60935                             },
60936                             '.course-edit-link' : {
60937                                 'color' : 'blue',
60938                                 'text-overflow' : 'ellipsis',
60939                                 'overflow' : 'hidden',
60940                                 'white-space' : 'nowrap',
60941                                 'cursor' : 'pointer'
60942                             },
60943                             '.sub-link' : {
60944                                 'color' : 'green'
60945                             },
60946                             '.de-act-sup-link' : {
60947                                 'color' : 'purple',
60948                                 'text-decoration' : 'line-through'
60949                             },
60950                             '.de-act-link' : {
60951                                 'color' : 'red',
60952                                 'text-decoration' : 'line-through'
60953                             },
60954                             '.course-timesheet .course-highlight' : {
60955                                 'border-top-style': 'dashed !important',
60956                                 'border-bottom-bottom': 'dashed !important'
60957                             },
60958                             '.course-timesheet .course-item' : {
60959                                 'font-family'   : 'tahoma, arial, helvetica',
60960                                 'font-size'     : '11px',
60961                                 'overflow'      : 'hidden',
60962                                 'padding-left'  : '10px',
60963                                 'padding-right' : '10px',
60964                                 'padding-top' : '10px' 
60965                             }
60966                             
60967                         }, Roo.id());
60968                                 this.ds.load({});
60969                     }
60970                 },
60971                 autoWidth : true,
60972                 monitorWindowResize : false,
60973                 cellrenderer : function(v,x,r)
60974                 {
60975                     return v;
60976                 },
60977                 sm : {
60978                     xtype: 'CellSelectionModel',
60979                     xns: Roo.grid
60980                 },
60981                 dataSource : {
60982                     xtype: 'Store',
60983                     xns: Roo.data,
60984                     listeners : {
60985                         beforeload : function (_self, options)
60986                         {
60987                             options.params = options.params || {};
60988                             options.params._month = _this.monthField.getValue();
60989                             options.params.limit = 9999;
60990                             options.params['sort'] = 'when_dt';    
60991                             options.params['dir'] = 'ASC';    
60992                             this.proxy.loadResponse = this.loadResponse;
60993                             Roo.log("load?");
60994                             //this.addColumns();
60995                         },
60996                         load : function (_self, records, options)
60997                         {
60998                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
60999                                 // if you click on the translation.. you can edit it...
61000                                 var el = Roo.get(this);
61001                                 var id = el.dom.getAttribute('data-id');
61002                                 var d = el.dom.getAttribute('data-date');
61003                                 var t = el.dom.getAttribute('data-time');
61004                                 //var id = this.child('span').dom.textContent;
61005                                 
61006                                 //Roo.log(this);
61007                                 Pman.Dialog.CourseCalendar.show({
61008                                     id : id,
61009                                     when_d : d,
61010                                     when_t : t,
61011                                     productitem_active : id ? 1 : 0
61012                                 }, function() {
61013                                     _this.grid.ds.load({});
61014                                 });
61015                            
61016                            });
61017                            
61018                            _this.panel.fireEvent('resize', [ '', '' ]);
61019                         }
61020                     },
61021                     loadResponse : function(o, success, response){
61022                             // this is overridden on before load..
61023                             
61024                             Roo.log("our code?");       
61025                             //Roo.log(success);
61026                             //Roo.log(response)
61027                             delete this.activeRequest;
61028                             if(!success){
61029                                 this.fireEvent("loadexception", this, o, response);
61030                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
61031                                 return;
61032                             }
61033                             var result;
61034                             try {
61035                                 result = o.reader.read(response);
61036                             }catch(e){
61037                                 Roo.log("load exception?");
61038                                 this.fireEvent("loadexception", this, o, response, e);
61039                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
61040                                 return;
61041                             }
61042                             Roo.log("ready...");        
61043                             // loop through result.records;
61044                             // and set this.tdate[date] = [] << array of records..
61045                             _this.tdata  = {};
61046                             Roo.each(result.records, function(r){
61047                                 //Roo.log(r.data);
61048                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
61049                                     _this.tdata[r.data.when_dt.format('j')] = [];
61050                                 }
61051                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
61052                             });
61053                             
61054                             //Roo.log(_this.tdata);
61055                             
61056                             result.records = [];
61057                             result.totalRecords = 6;
61058                     
61059                             // let's generate some duumy records for the rows.
61060                             //var st = _this.dateField.getValue();
61061                             
61062                             // work out monday..
61063                             //st = st.add(Date.DAY, -1 * st.format('w'));
61064                             
61065                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
61066                             
61067                             var firstOfMonth = date.getFirstDayOfMonth();
61068                             var days = date.getDaysInMonth();
61069                             var d = 1;
61070                             var firstAdded = false;
61071                             for (var i = 0; i < result.totalRecords ; i++) {
61072                                 //var d= st.add(Date.DAY, i);
61073                                 var row = {};
61074                                 var added = 0;
61075                                 for(var w = 0 ; w < 7 ; w++){
61076                                     if(!firstAdded && firstOfMonth != w){
61077                                         continue;
61078                                     }
61079                                     if(d > days){
61080                                         continue;
61081                                     }
61082                                     firstAdded = true;
61083                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
61084                                     row['weekday'+w] = String.format(
61085                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
61086                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
61087                                                     d,
61088                                                     date.format('Y-m-')+dd
61089                                                 );
61090                                     added++;
61091                                     if(typeof(_this.tdata[d]) != 'undefined'){
61092                                         Roo.each(_this.tdata[d], function(r){
61093                                             var is_sub = '';
61094                                             var deactive = '';
61095                                             var id = r.id;
61096                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
61097                                             if(r.parent_id*1>0){
61098                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
61099                                                 id = r.parent_id;
61100                                             }
61101                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
61102                                                 deactive = 'de-act-link';
61103                                             }
61104                                             
61105                                             row['weekday'+w] += String.format(
61106                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
61107                                                     id, //0
61108                                                     r.product_id_name, //1
61109                                                     r.when_dt.format('h:ia'), //2
61110                                                     is_sub, //3
61111                                                     deactive, //4
61112                                                     desc // 5
61113                                             );
61114                                         });
61115                                     }
61116                                     d++;
61117                                 }
61118                                 
61119                                 // only do this if something added..
61120                                 if(added > 0){ 
61121                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
61122                                 }
61123                                 
61124                                 
61125                                 // push it twice. (second one with an hour..
61126                                 
61127                             }
61128                             //Roo.log(result);
61129                             this.fireEvent("load", this, o, o.request.arg);
61130                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
61131                         },
61132                     sortInfo : {field: 'when_dt', direction : 'ASC' },
61133                     proxy : {
61134                         xtype: 'HttpProxy',
61135                         xns: Roo.data,
61136                         method : 'GET',
61137                         url : baseURL + '/Roo/Shop_course.php'
61138                     },
61139                     reader : {
61140                         xtype: 'JsonReader',
61141                         xns: Roo.data,
61142                         id : 'id',
61143                         fields : [
61144                             {
61145                                 'name': 'id',
61146                                 'type': 'int'
61147                             },
61148                             {
61149                                 'name': 'when_dt',
61150                                 'type': 'string'
61151                             },
61152                             {
61153                                 'name': 'end_dt',
61154                                 'type': 'string'
61155                             },
61156                             {
61157                                 'name': 'parent_id',
61158                                 'type': 'int'
61159                             },
61160                             {
61161                                 'name': 'product_id',
61162                                 'type': 'int'
61163                             },
61164                             {
61165                                 'name': 'productitem_id',
61166                                 'type': 'int'
61167                             },
61168                             {
61169                                 'name': 'guid',
61170                                 'type': 'int'
61171                             }
61172                         ]
61173                     }
61174                 },
61175                 toolbar : {
61176                     xtype: 'Toolbar',
61177                     xns: Roo,
61178                     items : [
61179                         {
61180                             xtype: 'Button',
61181                             xns: Roo.Toolbar,
61182                             listeners : {
61183                                 click : function (_self, e)
61184                                 {
61185                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
61186                                     sd.setMonth(sd.getMonth()-1);
61187                                     _this.monthField.setValue(sd.format('Y-m-d'));
61188                                     _this.grid.ds.load({});
61189                                 }
61190                             },
61191                             text : "Back"
61192                         },
61193                         {
61194                             xtype: 'Separator',
61195                             xns: Roo.Toolbar
61196                         },
61197                         {
61198                             xtype: 'MonthField',
61199                             xns: Roo.form,
61200                             listeners : {
61201                                 render : function (_self)
61202                                 {
61203                                     _this.monthField = _self;
61204                                    // _this.monthField.set  today
61205                                 },
61206                                 select : function (combo, date)
61207                                 {
61208                                     _this.grid.ds.load({});
61209                                 }
61210                             },
61211                             value : (function() { return new Date(); })()
61212                         },
61213                         {
61214                             xtype: 'Separator',
61215                             xns: Roo.Toolbar
61216                         },
61217                         {
61218                             xtype: 'TextItem',
61219                             xns: Roo.Toolbar,
61220                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
61221                         },
61222                         {
61223                             xtype: 'Fill',
61224                             xns: Roo.Toolbar
61225                         },
61226                         {
61227                             xtype: 'Button',
61228                             xns: Roo.Toolbar,
61229                             listeners : {
61230                                 click : function (_self, e)
61231                                 {
61232                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
61233                                     sd.setMonth(sd.getMonth()+1);
61234                                     _this.monthField.setValue(sd.format('Y-m-d'));
61235                                     _this.grid.ds.load({});
61236                                 }
61237                             },
61238                             text : "Next"
61239                         }
61240                     ]
61241                 },
61242                  
61243             }
61244         };
61245         
61246         *//*
61247  * Based on:
61248  * Ext JS Library 1.1.1
61249  * Copyright(c) 2006-2007, Ext JS, LLC.
61250  *
61251  * Originally Released Under LGPL - original licence link has changed is not relivant.
61252  *
61253  * Fork - LGPL
61254  * <script type="text/javascript">
61255  */
61256  
61257 /**
61258  * @class Roo.LoadMask
61259  * A simple utility class for generically masking elements while loading data.  If the element being masked has
61260  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
61261  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
61262  * element's UpdateManager load indicator and will be destroyed after the initial load.
61263  * @constructor
61264  * Create a new LoadMask
61265  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
61266  * @param {Object} config The config object
61267  */
61268 Roo.LoadMask = function(el, config){
61269     this.el = Roo.get(el);
61270     Roo.apply(this, config);
61271     if(this.store){
61272         this.store.on('beforeload', this.onBeforeLoad, this);
61273         this.store.on('load', this.onLoad, this);
61274         this.store.on('loadexception', this.onLoadException, this);
61275         this.removeMask = false;
61276     }else{
61277         var um = this.el.getUpdateManager();
61278         um.showLoadIndicator = false; // disable the default indicator
61279         um.on('beforeupdate', this.onBeforeLoad, this);
61280         um.on('update', this.onLoad, this);
61281         um.on('failure', this.onLoad, this);
61282         this.removeMask = true;
61283     }
61284 };
61285
61286 Roo.LoadMask.prototype = {
61287     /**
61288      * @cfg {Boolean} removeMask
61289      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
61290      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
61291      */
61292     /**
61293      * @cfg {String} msg
61294      * The text to display in a centered loading message box (defaults to 'Loading...')
61295      */
61296     msg : 'Loading...',
61297     /**
61298      * @cfg {String} msgCls
61299      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
61300      */
61301     msgCls : 'x-mask-loading',
61302
61303     /**
61304      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
61305      * @type Boolean
61306      */
61307     disabled: false,
61308
61309     /**
61310      * Disables the mask to prevent it from being displayed
61311      */
61312     disable : function(){
61313        this.disabled = true;
61314     },
61315
61316     /**
61317      * Enables the mask so that it can be displayed
61318      */
61319     enable : function(){
61320         this.disabled = false;
61321     },
61322     
61323     onLoadException : function()
61324     {
61325         Roo.log(arguments);
61326         
61327         if (typeof(arguments[3]) != 'undefined') {
61328             Roo.MessageBox.alert("Error loading",arguments[3]);
61329         } 
61330         /*
61331         try {
61332             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
61333                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
61334             }   
61335         } catch(e) {
61336             
61337         }
61338         */
61339     
61340         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
61341     },
61342     // private
61343     onLoad : function()
61344     {
61345         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
61346     },
61347
61348     // private
61349     onBeforeLoad : function(){
61350         if(!this.disabled){
61351             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
61352         }
61353     },
61354
61355     // private
61356     destroy : function(){
61357         if(this.store){
61358             this.store.un('beforeload', this.onBeforeLoad, this);
61359             this.store.un('load', this.onLoad, this);
61360             this.store.un('loadexception', this.onLoadException, this);
61361         }else{
61362             var um = this.el.getUpdateManager();
61363             um.un('beforeupdate', this.onBeforeLoad, this);
61364             um.un('update', this.onLoad, this);
61365             um.un('failure', this.onLoad, this);
61366         }
61367     }
61368 };/*
61369  * Based on:
61370  * Ext JS Library 1.1.1
61371  * Copyright(c) 2006-2007, Ext JS, LLC.
61372  *
61373  * Originally Released Under LGPL - original licence link has changed is not relivant.
61374  *
61375  * Fork - LGPL
61376  * <script type="text/javascript">
61377  */
61378
61379
61380 /**
61381  * @class Roo.XTemplate
61382  * @extends Roo.Template
61383  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
61384 <pre><code>
61385 var t = new Roo.XTemplate(
61386         '&lt;select name="{name}"&gt;',
61387                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
61388         '&lt;/select&gt;'
61389 );
61390  
61391 // then append, applying the master template values
61392  </code></pre>
61393  *
61394  * Supported features:
61395  *
61396  *  Tags:
61397
61398 <pre><code>
61399       {a_variable} - output encoded.
61400       {a_variable.format:("Y-m-d")} - call a method on the variable
61401       {a_variable:raw} - unencoded output
61402       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
61403       {a_variable:this.method_on_template(...)} - call a method on the template object.
61404  
61405 </code></pre>
61406  *  The tpl tag:
61407 <pre><code>
61408         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
61409         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
61410         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
61411         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
61412   
61413         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
61414         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
61415 </code></pre>
61416  *      
61417  */
61418 Roo.XTemplate = function()
61419 {
61420     Roo.XTemplate.superclass.constructor.apply(this, arguments);
61421     if (this.html) {
61422         this.compile();
61423     }
61424 };
61425
61426
61427 Roo.extend(Roo.XTemplate, Roo.Template, {
61428
61429     /**
61430      * The various sub templates
61431      */
61432     tpls : false,
61433     /**
61434      *
61435      * basic tag replacing syntax
61436      * WORD:WORD()
61437      *
61438      * // you can fake an object call by doing this
61439      *  x.t:(test,tesT) 
61440      * 
61441      */
61442     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
61443
61444     /**
61445      * compile the template
61446      *
61447      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
61448      *
61449      */
61450     compile: function()
61451     {
61452         var s = this.html;
61453      
61454         s = ['<tpl>', s, '</tpl>'].join('');
61455     
61456         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
61457             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
61458             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
61459             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
61460             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
61461             m,
61462             id     = 0,
61463             tpls   = [];
61464     
61465         while(true == !!(m = s.match(re))){
61466             var forMatch   = m[0].match(nameRe),
61467                 ifMatch   = m[0].match(ifRe),
61468                 execMatch   = m[0].match(execRe),
61469                 namedMatch   = m[0].match(namedRe),
61470                 
61471                 exp  = null, 
61472                 fn   = null,
61473                 exec = null,
61474                 name = forMatch && forMatch[1] ? forMatch[1] : '';
61475                 
61476             if (ifMatch) {
61477                 // if - puts fn into test..
61478                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
61479                 if(exp){
61480                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
61481                 }
61482             }
61483             
61484             if (execMatch) {
61485                 // exec - calls a function... returns empty if true is  returned.
61486                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
61487                 if(exp){
61488                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
61489                 }
61490             }
61491             
61492             
61493             if (name) {
61494                 // for = 
61495                 switch(name){
61496                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
61497                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
61498                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
61499                 }
61500             }
61501             var uid = namedMatch ? namedMatch[1] : id;
61502             
61503             
61504             tpls.push({
61505                 id:     namedMatch ? namedMatch[1] : id,
61506                 target: name,
61507                 exec:   exec,
61508                 test:   fn,
61509                 body:   m[1] || ''
61510             });
61511             if (namedMatch) {
61512                 s = s.replace(m[0], '');
61513             } else { 
61514                 s = s.replace(m[0], '{xtpl'+ id + '}');
61515             }
61516             ++id;
61517         }
61518         this.tpls = [];
61519         for(var i = tpls.length-1; i >= 0; --i){
61520             this.compileTpl(tpls[i]);
61521             this.tpls[tpls[i].id] = tpls[i];
61522         }
61523         this.master = tpls[tpls.length-1];
61524         return this;
61525     },
61526     /**
61527      * same as applyTemplate, except it's done to one of the subTemplates
61528      * when using named templates, you can do:
61529      *
61530      * var str = pl.applySubTemplate('your-name', values);
61531      *
61532      * 
61533      * @param {Number} id of the template
61534      * @param {Object} values to apply to template
61535      * @param {Object} parent (normaly the instance of this object)
61536      */
61537     applySubTemplate : function(id, values, parent)
61538     {
61539         
61540         
61541         var t = this.tpls[id];
61542         
61543         
61544         try { 
61545             if(t.test && !t.test.call(this, values, parent)){
61546                 return '';
61547             }
61548         } catch(e) {
61549             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
61550             Roo.log(e.toString());
61551             Roo.log(t.test);
61552             return ''
61553         }
61554         try { 
61555             
61556             if(t.exec && t.exec.call(this, values, parent)){
61557                 return '';
61558             }
61559         } catch(e) {
61560             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
61561             Roo.log(e.toString());
61562             Roo.log(t.exec);
61563             return ''
61564         }
61565         try {
61566             var vs = t.target ? t.target.call(this, values, parent) : values;
61567             parent = t.target ? values : parent;
61568             if(t.target && vs instanceof Array){
61569                 var buf = [];
61570                 for(var i = 0, len = vs.length; i < len; i++){
61571                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
61572                 }
61573                 return buf.join('');
61574             }
61575             return t.compiled.call(this, vs, parent);
61576         } catch (e) {
61577             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
61578             Roo.log(e.toString());
61579             Roo.log(t.compiled);
61580             return '';
61581         }
61582     },
61583
61584     compileTpl : function(tpl)
61585     {
61586         var fm = Roo.util.Format;
61587         var useF = this.disableFormats !== true;
61588         var sep = Roo.isGecko ? "+" : ",";
61589         var undef = function(str) {
61590             Roo.log("Property not found :"  + str);
61591             return '';
61592         };
61593         
61594         var fn = function(m, name, format, args)
61595         {
61596             //Roo.log(arguments);
61597             args = args ? args.replace(/\\'/g,"'") : args;
61598             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
61599             if (typeof(format) == 'undefined') {
61600                 format= 'htmlEncode';
61601             }
61602             if (format == 'raw' ) {
61603                 format = false;
61604             }
61605             
61606             if(name.substr(0, 4) == 'xtpl'){
61607                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
61608             }
61609             
61610             // build an array of options to determine if value is undefined..
61611             
61612             // basically get 'xxxx.yyyy' then do
61613             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
61614             //    (function () { Roo.log("Property not found"); return ''; })() :
61615             //    ......
61616             
61617             var udef_ar = [];
61618             var lookfor = '';
61619             Roo.each(name.split('.'), function(st) {
61620                 lookfor += (lookfor.length ? '.': '') + st;
61621                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
61622             });
61623             
61624             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
61625             
61626             
61627             if(format && useF){
61628                 
61629                 args = args ? ',' + args : "";
61630                  
61631                 if(format.substr(0, 5) != "this."){
61632                     format = "fm." + format + '(';
61633                 }else{
61634                     format = 'this.call("'+ format.substr(5) + '", ';
61635                     args = ", values";
61636                 }
61637                 
61638                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
61639             }
61640              
61641             if (args.length) {
61642                 // called with xxyx.yuu:(test,test)
61643                 // change to ()
61644                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
61645             }
61646             // raw.. - :raw modifier..
61647             return "'"+ sep + udef_st  + name + ")"+sep+"'";
61648             
61649         };
61650         var body;
61651         // branched to use + in gecko and [].join() in others
61652         if(Roo.isGecko){
61653             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
61654                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
61655                     "';};};";
61656         }else{
61657             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
61658             body.push(tpl.body.replace(/(\r\n|\n)/g,
61659                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
61660             body.push("'].join('');};};");
61661             body = body.join('');
61662         }
61663         
61664         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
61665        
61666         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
61667         eval(body);
61668         
61669         return this;
61670     },
61671
61672     applyTemplate : function(values){
61673         return this.master.compiled.call(this, values, {});
61674         //var s = this.subs;
61675     },
61676
61677     apply : function(){
61678         return this.applyTemplate.apply(this, arguments);
61679     }
61680
61681  });
61682
61683 Roo.XTemplate.from = function(el){
61684     el = Roo.getDom(el);
61685     return new Roo.XTemplate(el.value || el.innerHTML);
61686 };