Fix #6430 - messing arouds with cards BS4
[roojs1] / roojs-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11  
12
13
14
15
16 // for old browsers
17 window["undefined"] = window["undefined"];
18
19 /**
20  * @class Roo
21  * Roo core utilities and functions.
22  * @singleton
23  */
24 var Roo = {}; 
25 /**
26  * Copies all the properties of config to obj.
27  * @param {Object} obj The receiver of the properties
28  * @param {Object} config The source of the properties
29  * @param {Object} defaults A different object that will also be applied for default values
30  * @return {Object} returns obj
31  * @member Roo apply
32  */
33
34  
35 Roo.apply = function(o, c, defaults){
36     if(defaults){
37         // no "this" reference for friendly out of scope calls
38         Roo.apply(o, defaults);
39     }
40     if(o && c && typeof c == 'object'){
41         for(var p in c){
42             o[p] = c[p];
43         }
44     }
45     return o;
46 };
47
48
49 (function(){
50     var idSeed = 0;
51     var ua = navigator.userAgent.toLowerCase();
52
53     var isStrict = document.compatMode == "CSS1Compat",
54         isOpera = ua.indexOf("opera") > -1,
55         isSafari = (/webkit|khtml/).test(ua),
56         isFirefox = ua.indexOf("firefox") > -1,
57         isIE = ua.indexOf("msie") > -1,
58         isIE7 = ua.indexOf("msie 7") > -1,
59         isIE11 = /trident.*rv\:11\./.test(ua),
60         isEdge = ua.indexOf("edge") > -1,
61         isGecko = !isSafari && ua.indexOf("gecko") > -1,
62         isBorderBox = isIE && !isStrict,
63         isWindows = (ua.indexOf("windows") != -1 || ua.indexOf("win32") != -1),
64         isMac = (ua.indexOf("macintosh") != -1 || ua.indexOf("mac os x") != -1),
65         isLinux = (ua.indexOf("linux") != -1),
66         isSecure = window.location.href.toLowerCase().indexOf("https") === 0,
67         isIOS = /iphone|ipad/.test(ua),
68         isAndroid = /android/.test(ua),
69         isTouch =  (function() {
70             try {
71                 if (ua.indexOf('chrome') != -1 && ua.indexOf('android') == -1) {
72                     window.addEventListener('touchstart', function __set_has_touch__ () {
73                         Roo.isTouch = true;
74                         window.removeEventListener('touchstart', __set_has_touch__);
75                     });
76                     return false; // no touch on chrome!?
77                 }
78                 document.createEvent("TouchEvent");  
79                 return true;  
80             } catch (e) {  
81                 return false;  
82             } 
83             
84         })();
85     // remove css image flicker
86         if(isIE && !isIE7){
87         try{
88             document.execCommand("BackgroundImageCache", false, true);
89         }catch(e){}
90     }
91     
92     Roo.apply(Roo, {
93         /**
94          * True if the browser is in strict mode
95          * @type Boolean
96          */
97         isStrict : isStrict,
98         /**
99          * True if the page is running over SSL
100          * @type Boolean
101          */
102         isSecure : isSecure,
103         /**
104          * True when the document is fully initialized and ready for action
105          * @type Boolean
106          */
107         isReady : false,
108         /**
109          * Turn on debugging output (currently only the factory uses this)
110          * @type Boolean
111          */
112         
113         debug: false,
114
115         /**
116          * True to automatically uncache orphaned Roo.Elements periodically (defaults to true)
117          * @type Boolean
118          */
119         enableGarbageCollector : true,
120
121         /**
122          * True to automatically purge event listeners after uncaching an element (defaults to false).
123          * Note: this only happens if enableGarbageCollector is true.
124          * @type Boolean
125          */
126         enableListenerCollection:false,
127
128         /**
129          * URL to a blank file used by Roo when in secure mode for iframe src and onReady src to prevent
130          * the IE insecure content warning (defaults to javascript:false).
131          * @type String
132          */
133         SSL_SECURE_URL : "javascript:false",
134
135         /**
136          * URL to a 1x1 transparent gif image used by Roo to create inline icons with CSS background images. (Defaults to
137          * "http://Roojs.com/s.gif" and you should change this to a URL on your server).
138          * @type String
139          */
140         BLANK_IMAGE_URL : "http:/"+"/localhost/s.gif",
141
142         emptyFn : function(){},
143         
144         /**
145          * Copies all the properties of config to obj if they don't already exist.
146          * @param {Object} obj The receiver of the properties
147          * @param {Object} config The source of the properties
148          * @return {Object} returns obj
149          */
150         applyIf : function(o, c){
151             if(o && c){
152                 for(var p in c){
153                     if(typeof o[p] == "undefined"){ o[p] = c[p]; }
154                 }
155             }
156             return o;
157         },
158
159         /**
160          * Applies event listeners to elements by selectors when the document is ready.
161          * The event name is specified with an @ suffix.
162 <pre><code>
163 Roo.addBehaviors({
164    // add a listener for click on all anchors in element with id foo
165    '#foo a@click' : function(e, t){
166        // do something
167    },
168
169    // add the same listener to multiple selectors (separated by comma BEFORE the @)
170    '#foo a, #bar span.some-class@mouseover' : function(){
171        // do something
172    }
173 });
174 </code></pre>
175          * @param {Object} obj The list of behaviors to apply
176          */
177         addBehaviors : function(o){
178             if(!Roo.isReady){
179                 Roo.onReady(function(){
180                     Roo.addBehaviors(o);
181                 });
182                 return;
183             }
184             var cache = {}; // simple cache for applying multiple behaviors to same selector does query multiple times
185             for(var b in o){
186                 var parts = b.split('@');
187                 if(parts[1]){ // for Object prototype breakers
188                     var s = parts[0];
189                     if(!cache[s]){
190                         cache[s] = Roo.select(s);
191                     }
192                     cache[s].on(parts[1], o[b]);
193                 }
194             }
195             cache = null;
196         },
197
198         /**
199          * Generates unique ids. If the element already has an id, it is unchanged
200          * @param {String/HTMLElement/Element} el (optional) The element to generate an id for
201          * @param {String} prefix (optional) Id prefix (defaults "Roo-gen")
202          * @return {String} The generated Id.
203          */
204         id : function(el, prefix){
205             prefix = prefix || "roo-gen";
206             el = Roo.getDom(el);
207             var id = prefix + (++idSeed);
208             return el ? (el.id ? el.id : (el.id = id)) : id;
209         },
210          
211        
212         /**
213          * Extends one class with another class and optionally overrides members with the passed literal. This class
214          * also adds the function "override()" to the class that can be used to override
215          * members on an instance.
216          * @param {Object} subclass The class inheriting the functionality
217          * @param {Object} superclass The class being extended
218          * @param {Object} overrides (optional) A literal with members
219          * @method extend
220          */
221         extend : function(){
222             // inline overrides
223             var io = function(o){
224                 for(var m in o){
225                     this[m] = o[m];
226                 }
227             };
228             return function(sb, sp, overrides){
229                 if(typeof sp == 'object'){ // eg. prototype, rather than function constructor..
230                     overrides = sp;
231                     sp = sb;
232                     sb = function(){sp.apply(this, arguments);};
233                 }
234                 var F = function(){}, sbp, spp = sp.prototype;
235                 F.prototype = spp;
236                 sbp = sb.prototype = new F();
237                 sbp.constructor=sb;
238                 sb.superclass=spp;
239                 
240                 if(spp.constructor == Object.prototype.constructor){
241                     spp.constructor=sp;
242                    
243                 }
244                 
245                 sb.override = function(o){
246                     Roo.override(sb, o);
247                 };
248                 sbp.override = io;
249                 Roo.override(sb, overrides);
250                 return sb;
251             };
252         }(),
253
254         /**
255          * Adds a list of functions to the prototype of an existing class, overwriting any existing methods with the same name.
256          * Usage:<pre><code>
257 Roo.override(MyClass, {
258     newMethod1: function(){
259         // etc.
260     },
261     newMethod2: function(foo){
262         // etc.
263     }
264 });
265  </code></pre>
266          * @param {Object} origclass The class to override
267          * @param {Object} overrides The list of functions to add to origClass.  This should be specified as an object literal
268          * containing one or more methods.
269          * @method override
270          */
271         override : function(origclass, overrides){
272             if(overrides){
273                 var p = origclass.prototype;
274                 for(var method in overrides){
275                     p[method] = overrides[method];
276                 }
277             }
278         },
279         /**
280          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
281          * <pre><code>
282 Roo.namespace('Company', 'Company.data');
283 Company.Widget = function() { ... }
284 Company.data.CustomStore = function(config) { ... }
285 </code></pre>
286          * @param {String} namespace1
287          * @param {String} namespace2
288          * @param {String} etc
289          * @method namespace
290          */
291         namespace : function(){
292             var a=arguments, o=null, i, j, d, rt;
293             for (i=0; i<a.length; ++i) {
294                 d=a[i].split(".");
295                 rt = d[0];
296                 /** eval:var:o */
297                 eval('if (typeof ' + rt + ' == "undefined"){' + rt + ' = {};} o = ' + rt + ';');
298                 for (j=1; j<d.length; ++j) {
299                     o[d[j]]=o[d[j]] || {};
300                     o=o[d[j]];
301                 }
302             }
303         },
304         /**
305          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
306          * <pre><code>
307 Roo.factory({ xns: Roo.data, xtype : 'Store', .....});
308 Roo.factory(conf, Roo.data);
309 </code></pre>
310          * @param {String} classname
311          * @param {String} namespace (optional)
312          * @method factory
313          */
314          
315         factory : function(c, ns)
316         {
317             // no xtype, no ns or c.xns - or forced off by c.xns
318             if (!c.xtype   || (!ns && !c.xns) ||  (c.xns === false)) { // not enough info...
319                 return c;
320             }
321             ns = c.xns ? c.xns : ns; // if c.xns is set, then use that..
322             if (c.constructor == ns[c.xtype]) {// already created...
323                 return c;
324             }
325             if (ns[c.xtype]) {
326                 if (Roo.debug) { Roo.log("Roo.Factory(" + c.xtype + ")"); }
327                 var ret = new ns[c.xtype](c);
328                 ret.xns = false;
329                 return ret;
330             }
331             c.xns = false; // prevent recursion..
332             return c;
333         },
334          /**
335          * Logs to console if it can.
336          *
337          * @param {String|Object} string
338          * @method log
339          */
340         log : function(s)
341         {
342             if ((typeof(console) == 'undefined') || (typeof(console.log) == 'undefined')) {
343                 return; // alerT?
344             }
345             
346             console.log(s);
347         },
348         /**
349          * Takes an object and converts it to an encoded URL. e.g. Roo.urlEncode({foo: 1, bar: 2}); would return "foo=1&bar=2".  Optionally, property values can be arrays, instead of keys and the resulting string that's returned will contain a name/value pair for each array value.
350          * @param {Object} o
351          * @return {String}
352          */
353         urlEncode : function(o){
354             if(!o){
355                 return "";
356             }
357             var buf = [];
358             for(var key in o){
359                 var ov = o[key], k = Roo.encodeURIComponent(key);
360                 var type = typeof ov;
361                 if(type == 'undefined'){
362                     buf.push(k, "=&");
363                 }else if(type != "function" && type != "object"){
364                     buf.push(k, "=", Roo.encodeURIComponent(ov), "&");
365                 }else if(ov instanceof Array){
366                     if (ov.length) {
367                             for(var i = 0, len = ov.length; i < len; i++) {
368                                 buf.push(k, "=", Roo.encodeURIComponent(ov[i] === undefined ? '' : ov[i]), "&");
369                             }
370                         } else {
371                             buf.push(k, "=&");
372                         }
373                 }
374             }
375             buf.pop();
376             return buf.join("");
377         },
378          /**
379          * Safe version of encodeURIComponent
380          * @param {String} data 
381          * @return {String} 
382          */
383         
384         encodeURIComponent : function (data)
385         {
386             try {
387                 return encodeURIComponent(data);
388             } catch(e) {} // should be an uri encode error.
389             
390             if (data == '' || data == null){
391                return '';
392             }
393             // http://stackoverflow.com/questions/2596483/unicode-and-uri-encoding-decoding-and-escaping-in-javascript
394             function nibble_to_hex(nibble){
395                 var chars = '0123456789ABCDEF';
396                 return chars.charAt(nibble);
397             }
398             data = data.toString();
399             var buffer = '';
400             for(var i=0; i<data.length; i++){
401                 var c = data.charCodeAt(i);
402                 var bs = new Array();
403                 if (c > 0x10000){
404                         // 4 bytes
405                     bs[0] = 0xF0 | ((c & 0x1C0000) >>> 18);
406                     bs[1] = 0x80 | ((c & 0x3F000) >>> 12);
407                     bs[2] = 0x80 | ((c & 0xFC0) >>> 6);
408                     bs[3] = 0x80 | (c & 0x3F);
409                 }else if (c > 0x800){
410                          // 3 bytes
411                     bs[0] = 0xE0 | ((c & 0xF000) >>> 12);
412                     bs[1] = 0x80 | ((c & 0xFC0) >>> 6);
413                     bs[2] = 0x80 | (c & 0x3F);
414                 }else if (c > 0x80){
415                        // 2 bytes
416                     bs[0] = 0xC0 | ((c & 0x7C0) >>> 6);
417                     bs[1] = 0x80 | (c & 0x3F);
418                 }else{
419                         // 1 byte
420                     bs[0] = c;
421                 }
422                 for(var j=0; j<bs.length; j++){
423                     var b = bs[j];
424                     var hex = nibble_to_hex((b & 0xF0) >>> 4) 
425                             + nibble_to_hex(b &0x0F);
426                     buffer += '%'+hex;
427                }
428             }
429             return buffer;    
430              
431         },
432
433         /**
434          * Takes an encoded URL and and converts it to an object. e.g. Roo.urlDecode("foo=1&bar=2"); would return {foo: 1, bar: 2} or Roo.urlDecode("foo=1&bar=2&bar=3&bar=4", true); would return {foo: 1, bar: [2, 3, 4]}.
435          * @param {String} string
436          * @param {Boolean} overwrite (optional) Items of the same name will overwrite previous values instead of creating an an array (Defaults to false).
437          * @return {Object} A literal with members
438          */
439         urlDecode : function(string, overwrite){
440             if(!string || !string.length){
441                 return {};
442             }
443             var obj = {};
444             var pairs = string.split('&');
445             var pair, name, value;
446             for(var i = 0, len = pairs.length; i < len; i++){
447                 pair = pairs[i].split('=');
448                 name = decodeURIComponent(pair[0]);
449                 value = decodeURIComponent(pair[1]);
450                 if(overwrite !== true){
451                     if(typeof obj[name] == "undefined"){
452                         obj[name] = value;
453                     }else if(typeof obj[name] == "string"){
454                         obj[name] = [obj[name]];
455                         obj[name].push(value);
456                     }else{
457                         obj[name].push(value);
458                     }
459                 }else{
460                     obj[name] = value;
461                 }
462             }
463             return obj;
464         },
465
466         /**
467          * Iterates an array calling the passed function with each item, stopping if your function returns false. If the
468          * passed array is not really an array, your function is called once with it.
469          * The supplied function is called with (Object item, Number index, Array allItems).
470          * @param {Array/NodeList/Mixed} array
471          * @param {Function} fn
472          * @param {Object} scope
473          */
474         each : function(array, fn, scope){
475             if(typeof array.length == "undefined" || typeof array == "string"){
476                 array = [array];
477             }
478             for(var i = 0, len = array.length; i < len; i++){
479                 if(fn.call(scope || array[i], array[i], i, array) === false){ return i; };
480             }
481         },
482
483         // deprecated
484         combine : function(){
485             var as = arguments, l = as.length, r = [];
486             for(var i = 0; i < l; i++){
487                 var a = as[i];
488                 if(a instanceof Array){
489                     r = r.concat(a);
490                 }else if(a.length !== undefined && !a.substr){
491                     r = r.concat(Array.prototype.slice.call(a, 0));
492                 }else{
493                     r.push(a);
494                 }
495             }
496             return r;
497         },
498
499         /**
500          * Escapes the passed string for use in a regular expression
501          * @param {String} str
502          * @return {String}
503          */
504         escapeRe : function(s) {
505             return s.replace(/([.*+?^${}()|[\]\/\\])/g, "\\$1");
506         },
507
508         // internal
509         callback : function(cb, scope, args, delay){
510             if(typeof cb == "function"){
511                 if(delay){
512                     cb.defer(delay, scope, args || []);
513                 }else{
514                     cb.apply(scope, args || []);
515                 }
516             }
517         },
518
519         /**
520          * Return the dom node for the passed string (id), dom node, or Roo.Element
521          * @param {String/HTMLElement/Roo.Element} el
522          * @return HTMLElement
523          */
524         getDom : function(el){
525             if(!el){
526                 return null;
527             }
528             return el.dom ? el.dom : (typeof el == 'string' ? document.getElementById(el) : el);
529         },
530
531         /**
532         * Shorthand for {@link Roo.ComponentMgr#get}
533         * @param {String} id
534         * @return Roo.Component
535         */
536         getCmp : function(id){
537             return Roo.ComponentMgr.get(id);
538         },
539          
540         num : function(v, defaultValue){
541             if(typeof v != 'number'){
542                 return defaultValue;
543             }
544             return v;
545         },
546
547         destroy : function(){
548             for(var i = 0, a = arguments, len = a.length; i < len; i++) {
549                 var as = a[i];
550                 if(as){
551                     if(as.dom){
552                         as.removeAllListeners();
553                         as.remove();
554                         continue;
555                     }
556                     if(typeof as.purgeListeners == 'function'){
557                         as.purgeListeners();
558                     }
559                     if(typeof as.destroy == 'function'){
560                         as.destroy();
561                     }
562                 }
563             }
564         },
565
566         // inpired by a similar function in mootools library
567         /**
568          * Returns the type of object that is passed in. If the object passed in is null or undefined it
569          * return false otherwise it returns one of the following values:<ul>
570          * <li><b>string</b>: If the object passed is a string</li>
571          * <li><b>number</b>: If the object passed is a number</li>
572          * <li><b>boolean</b>: If the object passed is a boolean value</li>
573          * <li><b>function</b>: If the object passed is a function reference</li>
574          * <li><b>object</b>: If the object passed is an object</li>
575          * <li><b>array</b>: If the object passed is an array</li>
576          * <li><b>regexp</b>: If the object passed is a regular expression</li>
577          * <li><b>element</b>: If the object passed is a DOM Element</li>
578          * <li><b>nodelist</b>: If the object passed is a DOM NodeList</li>
579          * <li><b>textnode</b>: If the object passed is a DOM text node and contains something other than whitespace</li>
580          * <li><b>whitespace</b>: If the object passed is a DOM text node and contains only whitespace</li>
581          * @param {Mixed} object
582          * @return {String}
583          */
584         type : function(o){
585             if(o === undefined || o === null){
586                 return false;
587             }
588             if(o.htmlElement){
589                 return 'element';
590             }
591             var t = typeof o;
592             if(t == 'object' && o.nodeName) {
593                 switch(o.nodeType) {
594                     case 1: return 'element';
595                     case 3: return (/\S/).test(o.nodeValue) ? 'textnode' : 'whitespace';
596                 }
597             }
598             if(t == 'object' || t == 'function') {
599                 switch(o.constructor) {
600                     case Array: return 'array';
601                     case RegExp: return 'regexp';
602                 }
603                 if(typeof o.length == 'number' && typeof o.item == 'function') {
604                     return 'nodelist';
605                 }
606             }
607             return t;
608         },
609
610         /**
611          * Returns true if the passed value is null, undefined or an empty string (optional).
612          * @param {Mixed} value The value to test
613          * @param {Boolean} allowBlank (optional) Pass true if an empty string is not considered empty
614          * @return {Boolean}
615          */
616         isEmpty : function(v, allowBlank){
617             return v === null || v === undefined || (!allowBlank ? v === '' : false);
618         },
619         
620         /** @type Boolean */
621         isOpera : isOpera,
622         /** @type Boolean */
623         isSafari : isSafari,
624         /** @type Boolean */
625         isFirefox : isFirefox,
626         /** @type Boolean */
627         isIE : isIE,
628         /** @type Boolean */
629         isIE7 : isIE7,
630         /** @type Boolean */
631         isIE11 : isIE11,
632         /** @type Boolean */
633         isEdge : isEdge,
634         /** @type Boolean */
635         isGecko : isGecko,
636         /** @type Boolean */
637         isBorderBox : isBorderBox,
638         /** @type Boolean */
639         isWindows : isWindows,
640         /** @type Boolean */
641         isLinux : isLinux,
642         /** @type Boolean */
643         isMac : isMac,
644         /** @type Boolean */
645         isIOS : isIOS,
646         /** @type Boolean */
647         isAndroid : isAndroid,
648         /** @type Boolean */
649         isTouch : isTouch,
650
651         /**
652          * By default, Ext intelligently decides whether floating elements should be shimmed. If you are using flash,
653          * you may want to set this to true.
654          * @type Boolean
655          */
656         useShims : ((isIE && !isIE7) || (isGecko && isMac)),
657         
658         
659                 
660         /**
661          * Selects a single element as a Roo Element
662          * This is about as close as you can get to jQuery's $('do crazy stuff')
663          * @param {String} selector The selector/xpath query
664          * @param {Node} root (optional) The start of the query (defaults to document).
665          * @return {Roo.Element}
666          */
667         selectNode : function(selector, root) 
668         {
669             var node = Roo.DomQuery.selectNode(selector,root);
670             return node ? Roo.get(node) : new Roo.Element(false);
671         }
672         
673     });
674
675
676 })();
677
678 Roo.namespace("Roo", "Roo.util", "Roo.grid", "Roo.dd", "Roo.tree", "Roo.data",
679                 "Roo.form", "Roo.menu", "Roo.state", "Roo.lib", "Roo.layout",
680                 "Roo.app", "Roo.ux",
681                 "Roo.bootstrap",
682                 "Roo.bootstrap.dash");
683 /*
684  * Based on:
685  * Ext JS Library 1.1.1
686  * Copyright(c) 2006-2007, Ext JS, LLC.
687  *
688  * Originally Released Under LGPL - original licence link has changed is not relivant.
689  *
690  * Fork - LGPL
691  * <script type="text/javascript">
692  */
693
694 (function() {    
695     // wrappedn so fnCleanup is not in global scope...
696     if(Roo.isIE) {
697         function fnCleanUp() {
698             var p = Function.prototype;
699             delete p.createSequence;
700             delete p.defer;
701             delete p.createDelegate;
702             delete p.createCallback;
703             delete p.createInterceptor;
704
705             window.detachEvent("onunload", fnCleanUp);
706         }
707         window.attachEvent("onunload", fnCleanUp);
708     }
709 })();
710
711
712 /**
713  * @class Function
714  * These functions are available on every Function object (any JavaScript function).
715  */
716 Roo.apply(Function.prototype, {
717      /**
718      * Creates a callback that passes arguments[0], arguments[1], arguments[2], ...
719      * Call directly on any function. Example: <code>myFunction.createCallback(myarg, myarg2)</code>
720      * Will create a function that is bound to those 2 args.
721      * @return {Function} The new function
722     */
723     createCallback : function(/*args...*/){
724         // make args available, in function below
725         var args = arguments;
726         var method = this;
727         return function() {
728             return method.apply(window, args);
729         };
730     },
731
732     /**
733      * Creates a delegate (callback) that sets the scope to obj.
734      * Call directly on any function. Example: <code>this.myFunction.createDelegate(this)</code>
735      * Will create a function that is automatically scoped to this.
736      * @param {Object} obj (optional) The object for which the scope is set
737      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
738      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
739      *                                             if a number the args are inserted at the specified position
740      * @return {Function} The new function
741      */
742     createDelegate : function(obj, args, appendArgs){
743         var method = this;
744         return function() {
745             var callArgs = args || arguments;
746             if(appendArgs === true){
747                 callArgs = Array.prototype.slice.call(arguments, 0);
748                 callArgs = callArgs.concat(args);
749             }else if(typeof appendArgs == "number"){
750                 callArgs = Array.prototype.slice.call(arguments, 0); // copy arguments first
751                 var applyArgs = [appendArgs, 0].concat(args); // create method call params
752                 Array.prototype.splice.apply(callArgs, applyArgs); // splice them in
753             }
754             return method.apply(obj || window, callArgs);
755         };
756     },
757
758     /**
759      * Calls this function after the number of millseconds specified.
760      * @param {Number} millis The number of milliseconds for the setTimeout call (if 0 the function is executed immediately)
761      * @param {Object} obj (optional) The object for which the scope is set
762      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
763      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
764      *                                             if a number the args are inserted at the specified position
765      * @return {Number} The timeout id that can be used with clearTimeout
766      */
767     defer : function(millis, obj, args, appendArgs){
768         var fn = this.createDelegate(obj, args, appendArgs);
769         if(millis){
770             return setTimeout(fn, millis);
771         }
772         fn();
773         return 0;
774     },
775     /**
776      * Create a combined function call sequence of the original function + the passed function.
777      * The resulting function returns the results of the original function.
778      * The passed fcn is called with the parameters of the original function
779      * @param {Function} fcn The function to sequence
780      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
781      * @return {Function} The new function
782      */
783     createSequence : function(fcn, scope){
784         if(typeof fcn != "function"){
785             return this;
786         }
787         var method = this;
788         return function() {
789             var retval = method.apply(this || window, arguments);
790             fcn.apply(scope || this || window, arguments);
791             return retval;
792         };
793     },
794
795     /**
796      * Creates an interceptor function. The passed fcn is called before the original one. If it returns false, the original one is not called.
797      * The resulting function returns the results of the original function.
798      * The passed fcn is called with the parameters of the original function.
799      * @addon
800      * @param {Function} fcn The function to call before the original
801      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
802      * @return {Function} The new function
803      */
804     createInterceptor : function(fcn, scope){
805         if(typeof fcn != "function"){
806             return this;
807         }
808         var method = this;
809         return function() {
810             fcn.target = this;
811             fcn.method = method;
812             if(fcn.apply(scope || this || window, arguments) === false){
813                 return;
814             }
815             return method.apply(this || window, arguments);
816         };
817     }
818 });
819 /*
820  * Based on:
821  * Ext JS Library 1.1.1
822  * Copyright(c) 2006-2007, Ext JS, LLC.
823  *
824  * Originally Released Under LGPL - original licence link has changed is not relivant.
825  *
826  * Fork - LGPL
827  * <script type="text/javascript">
828  */
829
830 Roo.applyIf(String, {
831     
832     /** @scope String */
833     
834     /**
835      * Escapes the passed string for ' and \
836      * @param {String} string The string to escape
837      * @return {String} The escaped string
838      * @static
839      */
840     escape : function(string) {
841         return string.replace(/('|\\)/g, "\\$1");
842     },
843
844     /**
845      * Pads the left side of a string with a specified character.  This is especially useful
846      * for normalizing number and date strings.  Example usage:
847      * <pre><code>
848 var s = String.leftPad('123', 5, '0');
849 // s now contains the string: '00123'
850 </code></pre>
851      * @param {String} string The original string
852      * @param {Number} size The total length of the output string
853      * @param {String} char (optional) The character with which to pad the original string (defaults to empty string " ")
854      * @return {String} The padded string
855      * @static
856      */
857     leftPad : function (val, size, ch) {
858         var result = new String(val);
859         if(ch === null || ch === undefined || ch === '') {
860             ch = " ";
861         }
862         while (result.length < size) {
863             result = ch + result;
864         }
865         return result;
866     },
867
868     /**
869      * Allows you to define a tokenized string and pass an arbitrary number of arguments to replace the tokens.  Each
870      * token must be unique, and must increment in the format {0}, {1}, etc.  Example usage:
871      * <pre><code>
872 var cls = 'my-class', text = 'Some text';
873 var s = String.format('<div class="{0}">{1}</div>', cls, text);
874 // s now contains the string: '<div class="my-class">Some text</div>'
875 </code></pre>
876      * @param {String} string The tokenized string to be formatted
877      * @param {String} value1 The value to replace token {0}
878      * @param {String} value2 Etc...
879      * @return {String} The formatted string
880      * @static
881      */
882     format : function(format){
883         var args = Array.prototype.slice.call(arguments, 1);
884         return format.replace(/\{(\d+)\}/g, function(m, i){
885             return Roo.util.Format.htmlEncode(args[i]);
886         });
887     }
888   
889     
890 });
891
892 /**
893  * Utility function that allows you to easily switch a string between two alternating values.  The passed value
894  * is compared to the current string, and if they are equal, the other value that was passed in is returned.  If
895  * they are already different, the first value passed in is returned.  Note that this method returns the new value
896  * but does not change the current string.
897  * <pre><code>
898 // alternate sort directions
899 sort = sort.toggle('ASC', 'DESC');
900
901 // instead of conditional logic:
902 sort = (sort == 'ASC' ? 'DESC' : 'ASC');
903 </code></pre>
904  * @param {String} value The value to compare to the current string
905  * @param {String} other The new value to use if the string already equals the first value passed in
906  * @return {String} The new value
907  */
908  
909 String.prototype.toggle = function(value, other){
910     return this == value ? other : value;
911 };
912
913
914 /**
915   * Remove invalid unicode characters from a string 
916   *
917   * @return {String} The clean string
918   */
919 String.prototype.unicodeClean = function () {
920     return this.replace(/[\s\S]/g,
921         function(character) {
922             if (character.charCodeAt()< 256) {
923               return character;
924            }
925            try {
926                 encodeURIComponent(character);
927            } catch(e) { 
928               return '';
929            }
930            return character;
931         }
932     );
933 };
934   
935 /*
936  * Based on:
937  * Ext JS Library 1.1.1
938  * Copyright(c) 2006-2007, Ext JS, LLC.
939  *
940  * Originally Released Under LGPL - original licence link has changed is not relivant.
941  *
942  * Fork - LGPL
943  * <script type="text/javascript">
944  */
945
946  /**
947  * @class Number
948  */
949 Roo.applyIf(Number.prototype, {
950     /**
951      * Checks whether or not the current number is within a desired range.  If the number is already within the
952      * range it is returned, otherwise the min or max value is returned depending on which side of the range is
953      * exceeded.  Note that this method returns the constrained value but does not change the current number.
954      * @param {Number} min The minimum number in the range
955      * @param {Number} max The maximum number in the range
956      * @return {Number} The constrained value if outside the range, otherwise the current value
957      */
958     constrain : function(min, max){
959         return Math.min(Math.max(this, min), max);
960     }
961 });/*
962  * Based on:
963  * Ext JS Library 1.1.1
964  * Copyright(c) 2006-2007, Ext JS, LLC.
965  *
966  * Originally Released Under LGPL - original licence link has changed is not relivant.
967  *
968  * Fork - LGPL
969  * <script type="text/javascript">
970  */
971  /**
972  * @class Array
973  */
974 Roo.applyIf(Array.prototype, {
975     /**
976      * 
977      * Checks whether or not the specified object exists in the array.
978      * @param {Object} o The object to check for
979      * @return {Number} The index of o in the array (or -1 if it is not found)
980      */
981     indexOf : function(o){
982        for (var i = 0, len = this.length; i < len; i++){
983               if(this[i] == o) { return i; }
984        }
985            return -1;
986     },
987
988     /**
989      * Removes the specified object from the array.  If the object is not found nothing happens.
990      * @param {Object} o The object to remove
991      */
992     remove : function(o){
993        var index = this.indexOf(o);
994        if(index != -1){
995            this.splice(index, 1);
996        }
997     },
998     /**
999      * Map (JS 1.6 compatibility)
1000      * @param {Function} function  to call
1001      */
1002     map : function(fun )
1003     {
1004         var len = this.length >>> 0;
1005         if (typeof fun != "function") {
1006             throw new TypeError();
1007         }
1008         var res = new Array(len);
1009         var thisp = arguments[1];
1010         for (var i = 0; i < len; i++)
1011         {
1012             if (i in this) {
1013                 res[i] = fun.call(thisp, this[i], i, this);
1014             }
1015         }
1016
1017         return res;
1018     }
1019     
1020 });
1021
1022
1023  
1024 /*
1025  * Based on:
1026  * Ext JS Library 1.1.1
1027  * Copyright(c) 2006-2007, Ext JS, LLC.
1028  *
1029  * Originally Released Under LGPL - original licence link has changed is not relivant.
1030  *
1031  * Fork - LGPL
1032  * <script type="text/javascript">
1033  */
1034
1035 /**
1036  * @class Date
1037  *
1038  * The date parsing and format syntax is a subset of
1039  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
1040  * supported will provide results equivalent to their PHP versions.
1041  *
1042  * Following is the list of all currently supported formats:
1043  *<pre>
1044 Sample date:
1045 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
1046
1047 Format  Output      Description
1048 ------  ----------  --------------------------------------------------------------
1049   d      10         Day of the month, 2 digits with leading zeros
1050   D      Wed        A textual representation of a day, three letters
1051   j      10         Day of the month without leading zeros
1052   l      Wednesday  A full textual representation of the day of the week
1053   S      th         English ordinal day of month suffix, 2 chars (use with j)
1054   w      3          Numeric representation of the day of the week
1055   z      9          The julian date, or day of the year (0-365)
1056   W      01         ISO-8601 2-digit week number of year, weeks starting on Monday (00-52)
1057   F      January    A full textual representation of the month
1058   m      01         Numeric representation of a month, with leading zeros
1059   M      Jan        Month name abbreviation, three letters
1060   n      1          Numeric representation of a month, without leading zeros
1061   t      31         Number of days in the given month
1062   L      0          Whether it's a leap year (1 if it is a leap year, else 0)
1063   Y      2007       A full numeric representation of a year, 4 digits
1064   y      07         A two digit representation of a year
1065   a      pm         Lowercase Ante meridiem and Post meridiem
1066   A      PM         Uppercase Ante meridiem and Post meridiem
1067   g      3          12-hour format of an hour without leading zeros
1068   G      15         24-hour format of an hour without leading zeros
1069   h      03         12-hour format of an hour with leading zeros
1070   H      15         24-hour format of an hour with leading zeros
1071   i      05         Minutes with leading zeros
1072   s      01         Seconds, with leading zeros
1073   O      -0600      Difference to Greenwich time (GMT) in hours (Allows +08, without minutes)
1074   P      -06:00     Difference to Greenwich time (GMT) with colon between hours and minutes
1075   T      CST        Timezone setting of the machine running the code
1076   Z      -21600     Timezone offset in seconds (negative if west of UTC, positive if east)
1077 </pre>
1078  *
1079  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
1080  * <pre><code>
1081 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
1082 document.write(dt.format('Y-m-d'));                         //2007-01-10
1083 document.write(dt.format('F j, Y, g:i a'));                 //January 10, 2007, 3:05 pm
1084 document.write(dt.format('l, \\t\\he dS of F Y h:i:s A'));  //Wednesday, the 10th of January 2007 03:05:01 PM
1085  </code></pre>
1086  *
1087  * Here are some standard date/time patterns that you might find helpful.  They
1088  * are not part of the source of Date.js, but to use them you can simply copy this
1089  * block of code into any script that is included after Date.js and they will also become
1090  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
1091  * <pre><code>
1092 Date.patterns = {
1093     ISO8601Long:"Y-m-d H:i:s",
1094     ISO8601Short:"Y-m-d",
1095     ShortDate: "n/j/Y",
1096     LongDate: "l, F d, Y",
1097     FullDateTime: "l, F d, Y g:i:s A",
1098     MonthDay: "F d",
1099     ShortTime: "g:i A",
1100     LongTime: "g:i:s A",
1101     SortableDateTime: "Y-m-d\\TH:i:s",
1102     UniversalSortableDateTime: "Y-m-d H:i:sO",
1103     YearMonth: "F, Y"
1104 };
1105 </code></pre>
1106  *
1107  * Example usage:
1108  * <pre><code>
1109 var dt = new Date();
1110 document.write(dt.format(Date.patterns.ShortDate));
1111  </code></pre>
1112  */
1113
1114 /*
1115  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
1116  * They generate precompiled functions from date formats instead of parsing and
1117  * processing the pattern every time you format a date.  These functions are available
1118  * on every Date object (any javascript function).
1119  *
1120  * The original article and download are here:
1121  * http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/
1122  *
1123  */
1124  
1125  
1126  // was in core
1127 /**
1128  Returns the number of milliseconds between this date and date
1129  @param {Date} date (optional) Defaults to now
1130  @return {Number} The diff in milliseconds
1131  @member Date getElapsed
1132  */
1133 Date.prototype.getElapsed = function(date) {
1134         return Math.abs((date || new Date()).getTime()-this.getTime());
1135 };
1136 // was in date file..
1137
1138
1139 // private
1140 Date.parseFunctions = {count:0};
1141 // private
1142 Date.parseRegexes = [];
1143 // private
1144 Date.formatFunctions = {count:0};
1145
1146 // private
1147 Date.prototype.dateFormat = function(format) {
1148     if (Date.formatFunctions[format] == null) {
1149         Date.createNewFormat(format);
1150     }
1151     var func = Date.formatFunctions[format];
1152     return this[func]();
1153 };
1154
1155
1156 /**
1157  * Formats a date given the supplied format string
1158  * @param {String} format The format string
1159  * @return {String} The formatted date
1160  * @method
1161  */
1162 Date.prototype.format = Date.prototype.dateFormat;
1163
1164 // private
1165 Date.createNewFormat = function(format) {
1166     var funcName = "format" + Date.formatFunctions.count++;
1167     Date.formatFunctions[format] = funcName;
1168     var code = "Date.prototype." + funcName + " = function(){return ";
1169     var special = false;
1170     var ch = '';
1171     for (var i = 0; i < format.length; ++i) {
1172         ch = format.charAt(i);
1173         if (!special && ch == "\\") {
1174             special = true;
1175         }
1176         else if (special) {
1177             special = false;
1178             code += "'" + String.escape(ch) + "' + ";
1179         }
1180         else {
1181             code += Date.getFormatCode(ch);
1182         }
1183     }
1184     /** eval:var:zzzzzzzzzzzzz */
1185     eval(code.substring(0, code.length - 3) + ";}");
1186 };
1187
1188 // private
1189 Date.getFormatCode = function(character) {
1190     switch (character) {
1191     case "d":
1192         return "String.leftPad(this.getDate(), 2, '0') + ";
1193     case "D":
1194         return "Date.dayNames[this.getDay()].substring(0, 3) + ";
1195     case "j":
1196         return "this.getDate() + ";
1197     case "l":
1198         return "Date.dayNames[this.getDay()] + ";
1199     case "S":
1200         return "this.getSuffix() + ";
1201     case "w":
1202         return "this.getDay() + ";
1203     case "z":
1204         return "this.getDayOfYear() + ";
1205     case "W":
1206         return "this.getWeekOfYear() + ";
1207     case "F":
1208         return "Date.monthNames[this.getMonth()] + ";
1209     case "m":
1210         return "String.leftPad(this.getMonth() + 1, 2, '0') + ";
1211     case "M":
1212         return "Date.monthNames[this.getMonth()].substring(0, 3) + ";
1213     case "n":
1214         return "(this.getMonth() + 1) + ";
1215     case "t":
1216         return "this.getDaysInMonth() + ";
1217     case "L":
1218         return "(this.isLeapYear() ? 1 : 0) + ";
1219     case "Y":
1220         return "this.getFullYear() + ";
1221     case "y":
1222         return "('' + this.getFullYear()).substring(2, 4) + ";
1223     case "a":
1224         return "(this.getHours() < 12 ? 'am' : 'pm') + ";
1225     case "A":
1226         return "(this.getHours() < 12 ? 'AM' : 'PM') + ";
1227     case "g":
1228         return "((this.getHours() % 12) ? this.getHours() % 12 : 12) + ";
1229     case "G":
1230         return "this.getHours() + ";
1231     case "h":
1232         return "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0') + ";
1233     case "H":
1234         return "String.leftPad(this.getHours(), 2, '0') + ";
1235     case "i":
1236         return "String.leftPad(this.getMinutes(), 2, '0') + ";
1237     case "s":
1238         return "String.leftPad(this.getSeconds(), 2, '0') + ";
1239     case "O":
1240         return "this.getGMTOffset() + ";
1241     case "P":
1242         return "this.getGMTColonOffset() + ";
1243     case "T":
1244         return "this.getTimezone() + ";
1245     case "Z":
1246         return "(this.getTimezoneOffset() * -60) + ";
1247     default:
1248         return "'" + String.escape(character) + "' + ";
1249     }
1250 };
1251
1252 /**
1253  * Parses the passed string using the specified format. Note that this function expects dates in normal calendar
1254  * format, meaning that months are 1-based (1 = January) and not zero-based like in JavaScript dates.  Any part of
1255  * the date format that is not specified will default to the current date value for that part.  Time parts can also
1256  * be specified, but default to 0.  Keep in mind that the input date string must precisely match the specified format
1257  * string or the parse operation will fail.
1258  * Example Usage:
1259 <pre><code>
1260 //dt = Fri May 25 2007 (current date)
1261 var dt = new Date();
1262
1263 //dt = Thu May 25 2006 (today's month/day in 2006)
1264 dt = Date.parseDate("2006", "Y");
1265
1266 //dt = Sun Jan 15 2006 (all date parts specified)
1267 dt = Date.parseDate("2006-1-15", "Y-m-d");
1268
1269 //dt = Sun Jan 15 2006 15:20:01 GMT-0600 (CST)
1270 dt = Date.parseDate("2006-1-15 3:20:01 PM", "Y-m-d h:i:s A" );
1271 </code></pre>
1272  * @param {String} input The unparsed date as a string
1273  * @param {String} format The format the date is in
1274  * @return {Date} The parsed date
1275  * @static
1276  */
1277 Date.parseDate = function(input, format) {
1278     if (Date.parseFunctions[format] == null) {
1279         Date.createParser(format);
1280     }
1281     var func = Date.parseFunctions[format];
1282     return Date[func](input);
1283 };
1284 /**
1285  * @private
1286  */
1287
1288 Date.createParser = function(format) {
1289     var funcName = "parse" + Date.parseFunctions.count++;
1290     var regexNum = Date.parseRegexes.length;
1291     var currentGroup = 1;
1292     Date.parseFunctions[format] = funcName;
1293
1294     var code = "Date." + funcName + " = function(input){\n"
1295         + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, o, z, v;\n"
1296         + "var d = new Date();\n"
1297         + "y = d.getFullYear();\n"
1298         + "m = d.getMonth();\n"
1299         + "d = d.getDate();\n"
1300         + "if (typeof(input) !== 'string') { input = input.toString(); }\n"
1301         + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
1302         + "if (results && results.length > 0) {";
1303     var regex = "";
1304
1305     var special = false;
1306     var ch = '';
1307     for (var i = 0; i < format.length; ++i) {
1308         ch = format.charAt(i);
1309         if (!special && ch == "\\") {
1310             special = true;
1311         }
1312         else if (special) {
1313             special = false;
1314             regex += String.escape(ch);
1315         }
1316         else {
1317             var obj = Date.formatCodeToRegex(ch, currentGroup);
1318             currentGroup += obj.g;
1319             regex += obj.s;
1320             if (obj.g && obj.c) {
1321                 code += obj.c;
1322             }
1323         }
1324     }
1325
1326     code += "if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
1327         + "{v = new Date(y, m, d, h, i, s);}\n"
1328         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
1329         + "{v = new Date(y, m, d, h, i);}\n"
1330         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
1331         + "{v = new Date(y, m, d, h);}\n"
1332         + "else if (y >= 0 && m >= 0 && d > 0)\n"
1333         + "{v = new Date(y, m, d);}\n"
1334         + "else if (y >= 0 && m >= 0)\n"
1335         + "{v = new Date(y, m);}\n"
1336         + "else if (y >= 0)\n"
1337         + "{v = new Date(y);}\n"
1338         + "}return (v && (z || o))?\n" // favour UTC offset over GMT offset
1339         + "    ((z)? v.add(Date.SECOND, (v.getTimezoneOffset() * 60) + (z*1)) :\n" // reset to UTC, then add offset
1340         + "        v.add(Date.HOUR, (v.getGMTOffset() / 100) + (o / -100))) : v\n" // reset to GMT, then add offset
1341         + ";}";
1342
1343     Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$");
1344     /** eval:var:zzzzzzzzzzzzz */
1345     eval(code);
1346 };
1347
1348 // private
1349 Date.formatCodeToRegex = function(character, currentGroup) {
1350     switch (character) {
1351     case "D":
1352         return {g:0,
1353         c:null,
1354         s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};
1355     case "j":
1356         return {g:1,
1357             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1358             s:"(\\d{1,2})"}; // day of month without leading zeroes
1359     case "d":
1360         return {g:1,
1361             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1362             s:"(\\d{2})"}; // day of month with leading zeroes
1363     case "l":
1364         return {g:0,
1365             c:null,
1366             s:"(?:" + Date.dayNames.join("|") + ")"};
1367     case "S":
1368         return {g:0,
1369             c:null,
1370             s:"(?:st|nd|rd|th)"};
1371     case "w":
1372         return {g:0,
1373             c:null,
1374             s:"\\d"};
1375     case "z":
1376         return {g:0,
1377             c:null,
1378             s:"(?:\\d{1,3})"};
1379     case "W":
1380         return {g:0,
1381             c:null,
1382             s:"(?:\\d{2})"};
1383     case "F":
1384         return {g:1,
1385             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n",
1386             s:"(" + Date.monthNames.join("|") + ")"};
1387     case "M":
1388         return {g:1,
1389             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n",
1390             s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};
1391     case "n":
1392         return {g:1,
1393             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1394             s:"(\\d{1,2})"}; // Numeric representation of a month, without leading zeros
1395     case "m":
1396         return {g:1,
1397             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1398             s:"(\\d{2})"}; // Numeric representation of a month, with leading zeros
1399     case "t":
1400         return {g:0,
1401             c:null,
1402             s:"\\d{1,2}"};
1403     case "L":
1404         return {g:0,
1405             c:null,
1406             s:"(?:1|0)"};
1407     case "Y":
1408         return {g:1,
1409             c:"y = parseInt(results[" + currentGroup + "], 10);\n",
1410             s:"(\\d{4})"};
1411     case "y":
1412         return {g:1,
1413             c:"var ty = parseInt(results[" + currentGroup + "], 10);\n"
1414                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
1415             s:"(\\d{1,2})"};
1416     case "a":
1417         return {g:1,
1418             c:"if (results[" + currentGroup + "] == 'am') {\n"
1419                 + "if (h == 12) { h = 0; }\n"
1420                 + "} else { if (h < 12) { h += 12; }}",
1421             s:"(am|pm)"};
1422     case "A":
1423         return {g:1,
1424             c:"if (results[" + currentGroup + "] == 'AM') {\n"
1425                 + "if (h == 12) { h = 0; }\n"
1426                 + "} else { if (h < 12) { h += 12; }}",
1427             s:"(AM|PM)"};
1428     case "g":
1429     case "G":
1430         return {g:1,
1431             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1432             s:"(\\d{1,2})"}; // 12/24-hr format  format of an hour without leading zeroes
1433     case "h":
1434     case "H":
1435         return {g:1,
1436             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1437             s:"(\\d{2})"}; //  12/24-hr format  format of an hour with leading zeroes
1438     case "i":
1439         return {g:1,
1440             c:"i = parseInt(results[" + currentGroup + "], 10);\n",
1441             s:"(\\d{2})"};
1442     case "s":
1443         return {g:1,
1444             c:"s = parseInt(results[" + currentGroup + "], 10);\n",
1445             s:"(\\d{2})"};
1446     case "O":
1447         return {g:1,
1448             c:[
1449                 "o = results[", currentGroup, "];\n",
1450                 "var sn = o.substring(0,1);\n", // get + / - sign
1451                 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60);\n", // get hours (performs minutes-to-hour conversion also)
1452                 "var mn = o.substring(3,5) % 60;\n", // get minutes
1453                 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n", // -12hrs <= GMT offset <= 14hrs
1454                 "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1455             ].join(""),
1456             s:"([+\-]\\d{2,4})"};
1457     
1458     
1459     case "P":
1460         return {g:1,
1461                 c:[
1462                    "o = results[", currentGroup, "];\n",
1463                    "var sn = o.substring(0,1);\n",
1464                    "var hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60);\n",
1465                    "var mn = o.substring(4,6) % 60;\n",
1466                    "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n",
1467                         "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1468             ].join(""),
1469             s:"([+\-]\\d{4})"};
1470     case "T":
1471         return {g:0,
1472             c:null,
1473             s:"[A-Z]{1,4}"}; // timezone abbrev. may be between 1 - 4 chars
1474     case "Z":
1475         return {g:1,
1476             c:"z = results[" + currentGroup + "];\n" // -43200 <= UTC offset <= 50400
1477                   + "z = (-43200 <= z*1 && z*1 <= 50400)? z : null;\n",
1478             s:"([+\-]?\\d{1,5})"}; // leading '+' sign is optional for UTC offset
1479     default:
1480         return {g:0,
1481             c:null,
1482             s:String.escape(character)};
1483     }
1484 };
1485
1486 /**
1487  * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1488  * @return {String} The abbreviated timezone name (e.g. 'CST')
1489  */
1490 Date.prototype.getTimezone = function() {
1491     return this.toString().replace(/^.*? ([A-Z]{1,4})[\-+][0-9]{4} .*$/, "$1");
1492 };
1493
1494 /**
1495  * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1496  * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600')
1497  */
1498 Date.prototype.getGMTOffset = function() {
1499     return (this.getTimezoneOffset() > 0 ? "-" : "+")
1500         + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1501         + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
1502 };
1503
1504 /**
1505  * Get the offset from GMT of the current date (equivalent to the format specifier 'P').
1506  * @return {String} 2-characters representing hours and 2-characters representing minutes
1507  * seperated by a colon and prefixed with + or - (e.g. '-06:00')
1508  */
1509 Date.prototype.getGMTColonOffset = function() {
1510         return (this.getTimezoneOffset() > 0 ? "-" : "+")
1511                 + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1512                 + ":"
1513                 + String.leftPad(this.getTimezoneOffset() %60, 2, "0");
1514 }
1515
1516 /**
1517  * Get the numeric day number of the year, adjusted for leap year.
1518  * @return {Number} 0 through 364 (365 in leap years)
1519  */
1520 Date.prototype.getDayOfYear = function() {
1521     var num = 0;
1522     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1523     for (var i = 0; i < this.getMonth(); ++i) {
1524         num += Date.daysInMonth[i];
1525     }
1526     return num + this.getDate() - 1;
1527 };
1528
1529 /**
1530  * Get the string representation of the numeric week number of the year
1531  * (equivalent to the format specifier 'W').
1532  * @return {String} '00' through '52'
1533  */
1534 Date.prototype.getWeekOfYear = function() {
1535     // Skip to Thursday of this week
1536     var now = this.getDayOfYear() + (4 - this.getDay());
1537     // Find the first Thursday of the year
1538     var jan1 = new Date(this.getFullYear(), 0, 1);
1539     var then = (7 - jan1.getDay() + 4);
1540     return String.leftPad(((now - then) / 7) + 1, 2, "0");
1541 };
1542
1543 /**
1544  * Whether or not the current date is in a leap year.
1545  * @return {Boolean} True if the current date is in a leap year, else false
1546  */
1547 Date.prototype.isLeapYear = function() {
1548     var year = this.getFullYear();
1549     return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1550 };
1551
1552 /**
1553  * Get the first day of the current month, adjusted for leap year.  The returned value
1554  * is the numeric day index within the week (0-6) which can be used in conjunction with
1555  * the {@link #monthNames} array to retrieve the textual day name.
1556  * Example:
1557  *<pre><code>
1558 var dt = new Date('1/10/2007');
1559 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
1560 </code></pre>
1561  * @return {Number} The day number (0-6)
1562  */
1563 Date.prototype.getFirstDayOfMonth = function() {
1564     var day = (this.getDay() - (this.getDate() - 1)) % 7;
1565     return (day < 0) ? (day + 7) : day;
1566 };
1567
1568 /**
1569  * Get the last day of the current month, adjusted for leap year.  The returned value
1570  * is the numeric day index within the week (0-6) which can be used in conjunction with
1571  * the {@link #monthNames} array to retrieve the textual day name.
1572  * Example:
1573  *<pre><code>
1574 var dt = new Date('1/10/2007');
1575 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
1576 </code></pre>
1577  * @return {Number} The day number (0-6)
1578  */
1579 Date.prototype.getLastDayOfMonth = function() {
1580     var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
1581     return (day < 0) ? (day + 7) : day;
1582 };
1583
1584
1585 /**
1586  * Get the first date of this date's month
1587  * @return {Date}
1588  */
1589 Date.prototype.getFirstDateOfMonth = function() {
1590     return new Date(this.getFullYear(), this.getMonth(), 1);
1591 };
1592
1593 /**
1594  * Get the last date of this date's month
1595  * @return {Date}
1596  */
1597 Date.prototype.getLastDateOfMonth = function() {
1598     return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
1599 };
1600 /**
1601  * Get the number of days in the current month, adjusted for leap year.
1602  * @return {Number} The number of days in the month
1603  */
1604 Date.prototype.getDaysInMonth = function() {
1605     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1606     return Date.daysInMonth[this.getMonth()];
1607 };
1608
1609 /**
1610  * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1611  * @return {String} 'st, 'nd', 'rd' or 'th'
1612  */
1613 Date.prototype.getSuffix = function() {
1614     switch (this.getDate()) {
1615         case 1:
1616         case 21:
1617         case 31:
1618             return "st";
1619         case 2:
1620         case 22:
1621             return "nd";
1622         case 3:
1623         case 23:
1624             return "rd";
1625         default:
1626             return "th";
1627     }
1628 };
1629
1630 // private
1631 Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
1632
1633 /**
1634  * An array of textual month names.
1635  * Override these values for international dates, for example...
1636  * Date.monthNames = ['JanInYourLang', 'FebInYourLang', ...];
1637  * @type Array
1638  * @static
1639  */
1640 Date.monthNames =
1641    ["January",
1642     "February",
1643     "March",
1644     "April",
1645     "May",
1646     "June",
1647     "July",
1648     "August",
1649     "September",
1650     "October",
1651     "November",
1652     "December"];
1653
1654 /**
1655  * An array of textual day names.
1656  * Override these values for international dates, for example...
1657  * Date.dayNames = ['SundayInYourLang', 'MondayInYourLang', ...];
1658  * @type Array
1659  * @static
1660  */
1661 Date.dayNames =
1662    ["Sunday",
1663     "Monday",
1664     "Tuesday",
1665     "Wednesday",
1666     "Thursday",
1667     "Friday",
1668     "Saturday"];
1669
1670 // private
1671 Date.y2kYear = 50;
1672 // private
1673 Date.monthNumbers = {
1674     Jan:0,
1675     Feb:1,
1676     Mar:2,
1677     Apr:3,
1678     May:4,
1679     Jun:5,
1680     Jul:6,
1681     Aug:7,
1682     Sep:8,
1683     Oct:9,
1684     Nov:10,
1685     Dec:11};
1686
1687 /**
1688  * Creates and returns a new Date instance with the exact same date value as the called instance.
1689  * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1690  * variable will also be changed.  When the intention is to create a new variable that will not
1691  * modify the original instance, you should create a clone.
1692  *
1693  * Example of correctly cloning a date:
1694  * <pre><code>
1695 //wrong way:
1696 var orig = new Date('10/1/2006');
1697 var copy = orig;
1698 copy.setDate(5);
1699 document.write(orig);  //returns 'Thu Oct 05 2006'!
1700
1701 //correct way:
1702 var orig = new Date('10/1/2006');
1703 var copy = orig.clone();
1704 copy.setDate(5);
1705 document.write(orig);  //returns 'Thu Oct 01 2006'
1706 </code></pre>
1707  * @return {Date} The new Date instance
1708  */
1709 Date.prototype.clone = function() {
1710         return new Date(this.getTime());
1711 };
1712
1713 /**
1714  * Clears any time information from this date
1715  @param {Boolean} clone true to create a clone of this date, clear the time and return it
1716  @return {Date} this or the clone
1717  */
1718 Date.prototype.clearTime = function(clone){
1719     if(clone){
1720         return this.clone().clearTime();
1721     }
1722     this.setHours(0);
1723     this.setMinutes(0);
1724     this.setSeconds(0);
1725     this.setMilliseconds(0);
1726     return this;
1727 };
1728
1729 // private
1730 // safari setMonth is broken -- check that this is only donw once...
1731 if(Roo.isSafari && typeof(Date.brokenSetMonth) == 'undefined'){
1732     Date.brokenSetMonth = Date.prototype.setMonth;
1733         Date.prototype.setMonth = function(num){
1734                 if(num <= -1){
1735                         var n = Math.ceil(-num);
1736                         var back_year = Math.ceil(n/12);
1737                         var month = (n % 12) ? 12 - n % 12 : 0 ;
1738                         this.setFullYear(this.getFullYear() - back_year);
1739                         return Date.brokenSetMonth.call(this, month);
1740                 } else {
1741                         return Date.brokenSetMonth.apply(this, arguments);
1742                 }
1743         };
1744 }
1745
1746 /** Date interval constant 
1747 * @static 
1748 * @type String */
1749 Date.MILLI = "ms";
1750 /** Date interval constant 
1751 * @static 
1752 * @type String */
1753 Date.SECOND = "s";
1754 /** Date interval constant 
1755 * @static 
1756 * @type String */
1757 Date.MINUTE = "mi";
1758 /** Date interval constant 
1759 * @static 
1760 * @type String */
1761 Date.HOUR = "h";
1762 /** Date interval constant 
1763 * @static 
1764 * @type String */
1765 Date.DAY = "d";
1766 /** Date interval constant 
1767 * @static 
1768 * @type String */
1769 Date.MONTH = "mo";
1770 /** Date interval constant 
1771 * @static 
1772 * @type String */
1773 Date.YEAR = "y";
1774
1775 /**
1776  * Provides a convenient method of performing basic date arithmetic.  This method
1777  * does not modify the Date instance being called - it creates and returns
1778  * a new Date instance containing the resulting date value.
1779  *
1780  * Examples:
1781  * <pre><code>
1782 //Basic usage:
1783 var dt = new Date('10/29/2006').add(Date.DAY, 5);
1784 document.write(dt); //returns 'Fri Oct 06 2006 00:00:00'
1785
1786 //Negative values will subtract correctly:
1787 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
1788 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
1789
1790 //You can even chain several calls together in one line!
1791 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
1792 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
1793  </code></pre>
1794  *
1795  * @param {String} interval   A valid date interval enum value
1796  * @param {Number} value      The amount to add to the current date
1797  * @return {Date} The new Date instance
1798  */
1799 Date.prototype.add = function(interval, value){
1800   var d = this.clone();
1801   if (!interval || value === 0) { return d; }
1802   switch(interval.toLowerCase()){
1803     case Date.MILLI:
1804       d.setMilliseconds(this.getMilliseconds() + value);
1805       break;
1806     case Date.SECOND:
1807       d.setSeconds(this.getSeconds() + value);
1808       break;
1809     case Date.MINUTE:
1810       d.setMinutes(this.getMinutes() + value);
1811       break;
1812     case Date.HOUR:
1813       d.setHours(this.getHours() + value);
1814       break;
1815     case Date.DAY:
1816       d.setDate(this.getDate() + value);
1817       break;
1818     case Date.MONTH:
1819       var day = this.getDate();
1820       if(day > 28){
1821           day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
1822       }
1823       d.setDate(day);
1824       d.setMonth(this.getMonth() + value);
1825       break;
1826     case Date.YEAR:
1827       d.setFullYear(this.getFullYear() + value);
1828       break;
1829   }
1830   return d;
1831 };
1832 /*
1833  * Based on:
1834  * Ext JS Library 1.1.1
1835  * Copyright(c) 2006-2007, Ext JS, LLC.
1836  *
1837  * Originally Released Under LGPL - original licence link has changed is not relivant.
1838  *
1839  * Fork - LGPL
1840  * <script type="text/javascript">
1841  */
1842
1843 /**
1844  * @class Roo.lib.Dom
1845  * @static
1846  * 
1847  * Dom utils (from YIU afaik)
1848  * 
1849  **/
1850 Roo.lib.Dom = {
1851     /**
1852      * Get the view width
1853      * @param {Boolean} full True will get the full document, otherwise it's the view width
1854      * @return {Number} The width
1855      */
1856      
1857     getViewWidth : function(full) {
1858         return full ? this.getDocumentWidth() : this.getViewportWidth();
1859     },
1860     /**
1861      * Get the view height
1862      * @param {Boolean} full True will get the full document, otherwise it's the view height
1863      * @return {Number} The height
1864      */
1865     getViewHeight : function(full) {
1866         return full ? this.getDocumentHeight() : this.getViewportHeight();
1867     },
1868
1869     getDocumentHeight: function() {
1870         var scrollHeight = (document.compatMode != "CSS1Compat") ? document.body.scrollHeight : document.documentElement.scrollHeight;
1871         return Math.max(scrollHeight, this.getViewportHeight());
1872     },
1873
1874     getDocumentWidth: function() {
1875         var scrollWidth = (document.compatMode != "CSS1Compat") ? document.body.scrollWidth : document.documentElement.scrollWidth;
1876         return Math.max(scrollWidth, this.getViewportWidth());
1877     },
1878
1879     getViewportHeight: function() {
1880         var height = self.innerHeight;
1881         var mode = document.compatMode;
1882
1883         if ((mode || Roo.isIE) && !Roo.isOpera) {
1884             height = (mode == "CSS1Compat") ?
1885                      document.documentElement.clientHeight :
1886                      document.body.clientHeight;
1887         }
1888
1889         return height;
1890     },
1891
1892     getViewportWidth: function() {
1893         var width = self.innerWidth;
1894         var mode = document.compatMode;
1895
1896         if (mode || Roo.isIE) {
1897             width = (mode == "CSS1Compat") ?
1898                     document.documentElement.clientWidth :
1899                     document.body.clientWidth;
1900         }
1901         return width;
1902     },
1903
1904     isAncestor : function(p, c) {
1905         p = Roo.getDom(p);
1906         c = Roo.getDom(c);
1907         if (!p || !c) {
1908             return false;
1909         }
1910
1911         if (p.contains && !Roo.isSafari) {
1912             return p.contains(c);
1913         } else if (p.compareDocumentPosition) {
1914             return !!(p.compareDocumentPosition(c) & 16);
1915         } else {
1916             var parent = c.parentNode;
1917             while (parent) {
1918                 if (parent == p) {
1919                     return true;
1920                 }
1921                 else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
1922                     return false;
1923                 }
1924                 parent = parent.parentNode;
1925             }
1926             return false;
1927         }
1928     },
1929
1930     getRegion : function(el) {
1931         return Roo.lib.Region.getRegion(el);
1932     },
1933
1934     getY : function(el) {
1935         return this.getXY(el)[1];
1936     },
1937
1938     getX : function(el) {
1939         return this.getXY(el)[0];
1940     },
1941
1942     getXY : function(el) {
1943         var p, pe, b, scroll, bd = document.body;
1944         el = Roo.getDom(el);
1945         var fly = Roo.lib.AnimBase.fly;
1946         if (el.getBoundingClientRect) {
1947             b = el.getBoundingClientRect();
1948             scroll = fly(document).getScroll();
1949             return [b.left + scroll.left, b.top + scroll.top];
1950         }
1951         var x = 0, y = 0;
1952
1953         p = el;
1954
1955         var hasAbsolute = fly(el).getStyle("position") == "absolute";
1956
1957         while (p) {
1958
1959             x += p.offsetLeft;
1960             y += p.offsetTop;
1961
1962             if (!hasAbsolute && fly(p).getStyle("position") == "absolute") {
1963                 hasAbsolute = true;
1964             }
1965
1966             if (Roo.isGecko) {
1967                 pe = fly(p);
1968
1969                 var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
1970                 var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
1971
1972
1973                 x += bl;
1974                 y += bt;
1975
1976
1977                 if (p != el && pe.getStyle('overflow') != 'visible') {
1978                     x += bl;
1979                     y += bt;
1980                 }
1981             }
1982             p = p.offsetParent;
1983         }
1984
1985         if (Roo.isSafari && hasAbsolute) {
1986             x -= bd.offsetLeft;
1987             y -= bd.offsetTop;
1988         }
1989
1990         if (Roo.isGecko && !hasAbsolute) {
1991             var dbd = fly(bd);
1992             x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
1993             y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
1994         }
1995
1996         p = el.parentNode;
1997         while (p && p != bd) {
1998             if (!Roo.isOpera || (p.tagName != 'TR' && fly(p).getStyle("display") != "inline")) {
1999                 x -= p.scrollLeft;
2000                 y -= p.scrollTop;
2001             }
2002             p = p.parentNode;
2003         }
2004         return [x, y];
2005     },
2006  
2007   
2008
2009
2010     setXY : function(el, xy) {
2011         el = Roo.fly(el, '_setXY');
2012         el.position();
2013         var pts = el.translatePoints(xy);
2014         if (xy[0] !== false) {
2015             el.dom.style.left = pts.left + "px";
2016         }
2017         if (xy[1] !== false) {
2018             el.dom.style.top = pts.top + "px";
2019         }
2020     },
2021
2022     setX : function(el, x) {
2023         this.setXY(el, [x, false]);
2024     },
2025
2026     setY : function(el, y) {
2027         this.setXY(el, [false, y]);
2028     }
2029 };
2030 /*
2031  * Portions of this file are based on pieces of Yahoo User Interface Library
2032  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2033  * YUI licensed under the BSD License:
2034  * http://developer.yahoo.net/yui/license.txt
2035  * <script type="text/javascript">
2036  *
2037  */
2038
2039 Roo.lib.Event = function() {
2040     var loadComplete = false;
2041     var listeners = [];
2042     var unloadListeners = [];
2043     var retryCount = 0;
2044     var onAvailStack = [];
2045     var counter = 0;
2046     var lastError = null;
2047
2048     return {
2049         POLL_RETRYS: 200,
2050         POLL_INTERVAL: 20,
2051         EL: 0,
2052         TYPE: 1,
2053         FN: 2,
2054         WFN: 3,
2055         OBJ: 3,
2056         ADJ_SCOPE: 4,
2057         _interval: null,
2058
2059         startInterval: function() {
2060             if (!this._interval) {
2061                 var self = this;
2062                 var callback = function() {
2063                     self._tryPreloadAttach();
2064                 };
2065                 this._interval = setInterval(callback, this.POLL_INTERVAL);
2066
2067             }
2068         },
2069
2070         onAvailable: function(p_id, p_fn, p_obj, p_override) {
2071             onAvailStack.push({ id:         p_id,
2072                 fn:         p_fn,
2073                 obj:        p_obj,
2074                 override:   p_override,
2075                 checkReady: false    });
2076
2077             retryCount = this.POLL_RETRYS;
2078             this.startInterval();
2079         },
2080
2081
2082         addListener: function(el, eventName, fn) {
2083             el = Roo.getDom(el);
2084             if (!el || !fn) {
2085                 return false;
2086             }
2087
2088             if ("unload" == eventName) {
2089                 unloadListeners[unloadListeners.length] =
2090                 [el, eventName, fn];
2091                 return true;
2092             }
2093
2094             var wrappedFn = function(e) {
2095                 return fn(Roo.lib.Event.getEvent(e));
2096             };
2097
2098             var li = [el, eventName, fn, wrappedFn];
2099
2100             var index = listeners.length;
2101             listeners[index] = li;
2102
2103             this.doAdd(el, eventName, wrappedFn, false);
2104             return true;
2105
2106         },
2107
2108
2109         removeListener: function(el, eventName, fn) {
2110             var i, len;
2111
2112             el = Roo.getDom(el);
2113
2114             if(!fn) {
2115                 return this.purgeElement(el, false, eventName);
2116             }
2117
2118
2119             if ("unload" == eventName) {
2120
2121                 for (i = 0,len = unloadListeners.length; i < len; i++) {
2122                     var li = unloadListeners[i];
2123                     if (li &&
2124                         li[0] == el &&
2125                         li[1] == eventName &&
2126                         li[2] == fn) {
2127                         unloadListeners.splice(i, 1);
2128                         return true;
2129                     }
2130                 }
2131
2132                 return false;
2133             }
2134
2135             var cacheItem = null;
2136
2137
2138             var index = arguments[3];
2139
2140             if ("undefined" == typeof index) {
2141                 index = this._getCacheIndex(el, eventName, fn);
2142             }
2143
2144             if (index >= 0) {
2145                 cacheItem = listeners[index];
2146             }
2147
2148             if (!el || !cacheItem) {
2149                 return false;
2150             }
2151
2152             this.doRemove(el, eventName, cacheItem[this.WFN], false);
2153
2154             delete listeners[index][this.WFN];
2155             delete listeners[index][this.FN];
2156             listeners.splice(index, 1);
2157
2158             return true;
2159
2160         },
2161
2162
2163         getTarget: function(ev, resolveTextNode) {
2164             ev = ev.browserEvent || ev;
2165             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2166             var t = ev.target || ev.srcElement;
2167             return this.resolveTextNode(t);
2168         },
2169
2170
2171         resolveTextNode: function(node) {
2172             if (Roo.isSafari && node && 3 == node.nodeType) {
2173                 return node.parentNode;
2174             } else {
2175                 return node;
2176             }
2177         },
2178
2179
2180         getPageX: function(ev) {
2181             ev = ev.browserEvent || ev;
2182             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2183             var x = ev.pageX;
2184             if (!x && 0 !== x) {
2185                 x = ev.clientX || 0;
2186
2187                 if (Roo.isIE) {
2188                     x += this.getScroll()[1];
2189                 }
2190             }
2191
2192             return x;
2193         },
2194
2195
2196         getPageY: function(ev) {
2197             ev = ev.browserEvent || ev;
2198             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2199             var y = ev.pageY;
2200             if (!y && 0 !== y) {
2201                 y = ev.clientY || 0;
2202
2203                 if (Roo.isIE) {
2204                     y += this.getScroll()[0];
2205                 }
2206             }
2207
2208
2209             return y;
2210         },
2211
2212
2213         getXY: function(ev) {
2214             ev = ev.browserEvent || ev;
2215             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2216             return [this.getPageX(ev), this.getPageY(ev)];
2217         },
2218
2219
2220         getRelatedTarget: function(ev) {
2221             ev = ev.browserEvent || ev;
2222             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2223             var t = ev.relatedTarget;
2224             if (!t) {
2225                 if (ev.type == "mouseout") {
2226                     t = ev.toElement;
2227                 } else if (ev.type == "mouseover") {
2228                     t = ev.fromElement;
2229                 }
2230             }
2231
2232             return this.resolveTextNode(t);
2233         },
2234
2235
2236         getTime: function(ev) {
2237             ev = ev.browserEvent || ev;
2238             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2239             if (!ev.time) {
2240                 var t = new Date().getTime();
2241                 try {
2242                     ev.time = t;
2243                 } catch(ex) {
2244                     this.lastError = ex;
2245                     return t;
2246                 }
2247             }
2248
2249             return ev.time;
2250         },
2251
2252
2253         stopEvent: function(ev) {
2254             this.stopPropagation(ev);
2255             this.preventDefault(ev);
2256         },
2257
2258
2259         stopPropagation: function(ev) {
2260             ev = ev.browserEvent || ev;
2261             if (ev.stopPropagation) {
2262                 ev.stopPropagation();
2263             } else {
2264                 ev.cancelBubble = true;
2265             }
2266         },
2267
2268
2269         preventDefault: function(ev) {
2270             ev = ev.browserEvent || ev;
2271             if(ev.preventDefault) {
2272                 ev.preventDefault();
2273             } else {
2274                 ev.returnValue = false;
2275             }
2276         },
2277
2278
2279         getEvent: function(e) {
2280             var ev = e || window.event;
2281             if (!ev) {
2282                 var c = this.getEvent.caller;
2283                 while (c) {
2284                     ev = c.arguments[0];
2285                     if (ev && Event == ev.constructor) {
2286                         break;
2287                     }
2288                     c = c.caller;
2289                 }
2290             }
2291             return ev;
2292         },
2293
2294
2295         getCharCode: function(ev) {
2296             ev = ev.browserEvent || ev;
2297             return ev.charCode || ev.keyCode || 0;
2298         },
2299
2300
2301         _getCacheIndex: function(el, eventName, fn) {
2302             for (var i = 0,len = listeners.length; i < len; ++i) {
2303                 var li = listeners[i];
2304                 if (li &&
2305                     li[this.FN] == fn &&
2306                     li[this.EL] == el &&
2307                     li[this.TYPE] == eventName) {
2308                     return i;
2309                 }
2310             }
2311
2312             return -1;
2313         },
2314
2315
2316         elCache: {},
2317
2318
2319         getEl: function(id) {
2320             return document.getElementById(id);
2321         },
2322
2323
2324         clearCache: function() {
2325         },
2326
2327
2328         _load: function(e) {
2329             loadComplete = true;
2330             var EU = Roo.lib.Event;
2331
2332
2333             if (Roo.isIE) {
2334                 EU.doRemove(window, "load", EU._load);
2335             }
2336         },
2337
2338
2339         _tryPreloadAttach: function() {
2340
2341             if (this.locked) {
2342                 return false;
2343             }
2344
2345             this.locked = true;
2346
2347
2348             var tryAgain = !loadComplete;
2349             if (!tryAgain) {
2350                 tryAgain = (retryCount > 0);
2351             }
2352
2353
2354             var notAvail = [];
2355             for (var i = 0,len = onAvailStack.length; i < len; ++i) {
2356                 var item = onAvailStack[i];
2357                 if (item) {
2358                     var el = this.getEl(item.id);
2359
2360                     if (el) {
2361                         if (!item.checkReady ||
2362                             loadComplete ||
2363                             el.nextSibling ||
2364                             (document && document.body)) {
2365
2366                             var scope = el;
2367                             if (item.override) {
2368                                 if (item.override === true) {
2369                                     scope = item.obj;
2370                                 } else {
2371                                     scope = item.override;
2372                                 }
2373                             }
2374                             item.fn.call(scope, item.obj);
2375                             onAvailStack[i] = null;
2376                         }
2377                     } else {
2378                         notAvail.push(item);
2379                     }
2380                 }
2381             }
2382
2383             retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
2384
2385             if (tryAgain) {
2386
2387                 this.startInterval();
2388             } else {
2389                 clearInterval(this._interval);
2390                 this._interval = null;
2391             }
2392
2393             this.locked = false;
2394
2395             return true;
2396
2397         },
2398
2399
2400         purgeElement: function(el, recurse, eventName) {
2401             var elListeners = this.getListeners(el, eventName);
2402             if (elListeners) {
2403                 for (var i = 0,len = elListeners.length; i < len; ++i) {
2404                     var l = elListeners[i];
2405                     this.removeListener(el, l.type, l.fn);
2406                 }
2407             }
2408
2409             if (recurse && el && el.childNodes) {
2410                 for (i = 0,len = el.childNodes.length; i < len; ++i) {
2411                     this.purgeElement(el.childNodes[i], recurse, eventName);
2412                 }
2413             }
2414         },
2415
2416
2417         getListeners: function(el, eventName) {
2418             var results = [], searchLists;
2419             if (!eventName) {
2420                 searchLists = [listeners, unloadListeners];
2421             } else if (eventName == "unload") {
2422                 searchLists = [unloadListeners];
2423             } else {
2424                 searchLists = [listeners];
2425             }
2426
2427             for (var j = 0; j < searchLists.length; ++j) {
2428                 var searchList = searchLists[j];
2429                 if (searchList && searchList.length > 0) {
2430                     for (var i = 0,len = searchList.length; i < len; ++i) {
2431                         var l = searchList[i];
2432                         if (l && l[this.EL] === el &&
2433                             (!eventName || eventName === l[this.TYPE])) {
2434                             results.push({
2435                                 type:   l[this.TYPE],
2436                                 fn:     l[this.FN],
2437                                 obj:    l[this.OBJ],
2438                                 adjust: l[this.ADJ_SCOPE],
2439                                 index:  i
2440                             });
2441                         }
2442                     }
2443                 }
2444             }
2445
2446             return (results.length) ? results : null;
2447         },
2448
2449
2450         _unload: function(e) {
2451
2452             var EU = Roo.lib.Event, i, j, l, len, index;
2453
2454             for (i = 0,len = unloadListeners.length; i < len; ++i) {
2455                 l = unloadListeners[i];
2456                 if (l) {
2457                     var scope = window;
2458                     if (l[EU.ADJ_SCOPE]) {
2459                         if (l[EU.ADJ_SCOPE] === true) {
2460                             scope = l[EU.OBJ];
2461                         } else {
2462                             scope = l[EU.ADJ_SCOPE];
2463                         }
2464                     }
2465                     l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
2466                     unloadListeners[i] = null;
2467                     l = null;
2468                     scope = null;
2469                 }
2470             }
2471
2472             unloadListeners = null;
2473
2474             if (listeners && listeners.length > 0) {
2475                 j = listeners.length;
2476                 while (j) {
2477                     index = j - 1;
2478                     l = listeners[index];
2479                     if (l) {
2480                         EU.removeListener(l[EU.EL], l[EU.TYPE],
2481                                 l[EU.FN], index);
2482                     }
2483                     j = j - 1;
2484                 }
2485                 l = null;
2486
2487                 EU.clearCache();
2488             }
2489
2490             EU.doRemove(window, "unload", EU._unload);
2491
2492         },
2493
2494
2495         getScroll: function() {
2496             var dd = document.documentElement, db = document.body;
2497             if (dd && (dd.scrollTop || dd.scrollLeft)) {
2498                 return [dd.scrollTop, dd.scrollLeft];
2499             } else if (db) {
2500                 return [db.scrollTop, db.scrollLeft];
2501             } else {
2502                 return [0, 0];
2503             }
2504         },
2505
2506
2507         doAdd: function () {
2508             if (window.addEventListener) {
2509                 return function(el, eventName, fn, capture) {
2510                     el.addEventListener(eventName, fn, (capture));
2511                 };
2512             } else if (window.attachEvent) {
2513                 return function(el, eventName, fn, capture) {
2514                     el.attachEvent("on" + eventName, fn);
2515                 };
2516             } else {
2517                 return function() {
2518                 };
2519             }
2520         }(),
2521
2522
2523         doRemove: function() {
2524             if (window.removeEventListener) {
2525                 return function (el, eventName, fn, capture) {
2526                     el.removeEventListener(eventName, fn, (capture));
2527                 };
2528             } else if (window.detachEvent) {
2529                 return function (el, eventName, fn) {
2530                     el.detachEvent("on" + eventName, fn);
2531                 };
2532             } else {
2533                 return function() {
2534                 };
2535             }
2536         }()
2537     };
2538     
2539 }();
2540 (function() {     
2541    
2542     var E = Roo.lib.Event;
2543     E.on = E.addListener;
2544     E.un = E.removeListener;
2545
2546     if (document && document.body) {
2547         E._load();
2548     } else {
2549         E.doAdd(window, "load", E._load);
2550     }
2551     E.doAdd(window, "unload", E._unload);
2552     E._tryPreloadAttach();
2553 })();
2554
2555 /*
2556  * Portions of this file are based on pieces of Yahoo User Interface Library
2557  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2558  * YUI licensed under the BSD License:
2559  * http://developer.yahoo.net/yui/license.txt
2560  * <script type="text/javascript">
2561  *
2562  */
2563
2564 (function() {
2565     /**
2566      * @class Roo.lib.Ajax
2567      *
2568      */
2569     Roo.lib.Ajax = {
2570         /**
2571          * @static 
2572          */
2573         request : function(method, uri, cb, data, options) {
2574             if(options){
2575                 var hs = options.headers;
2576                 if(hs){
2577                     for(var h in hs){
2578                         if(hs.hasOwnProperty(h)){
2579                             this.initHeader(h, hs[h], false);
2580                         }
2581                     }
2582                 }
2583                 if(options.xmlData){
2584                     this.initHeader('Content-Type', 'text/xml', false);
2585                     method = 'POST';
2586                     data = options.xmlData;
2587                 }
2588             }
2589
2590             return this.asyncRequest(method, uri, cb, data);
2591         },
2592
2593         serializeForm : function(form) {
2594             if(typeof form == 'string') {
2595                 form = (document.getElementById(form) || document.forms[form]);
2596             }
2597
2598             var el, name, val, disabled, data = '', hasSubmit = false;
2599             for (var i = 0; i < form.elements.length; i++) {
2600                 el = form.elements[i];
2601                 disabled = form.elements[i].disabled;
2602                 name = form.elements[i].name;
2603                 val = form.elements[i].value;
2604
2605                 if (!disabled && name){
2606                     switch (el.type)
2607                             {
2608                         case 'select-one':
2609                         case 'select-multiple':
2610                             for (var j = 0; j < el.options.length; j++) {
2611                                 if (el.options[j].selected) {
2612                                     if (Roo.isIE) {
2613                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2614                                     }
2615                                     else {
2616                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2617                                     }
2618                                 }
2619                             }
2620                             break;
2621                         case 'radio':
2622                         case 'checkbox':
2623                             if (el.checked) {
2624                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2625                             }
2626                             break;
2627                         case 'file':
2628
2629                         case undefined:
2630
2631                         case 'reset':
2632
2633                         case 'button':
2634
2635                             break;
2636                         case 'submit':
2637                             if(hasSubmit == false) {
2638                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2639                                 hasSubmit = true;
2640                             }
2641                             break;
2642                         default:
2643                             data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2644                             break;
2645                     }
2646                 }
2647             }
2648             data = data.substr(0, data.length - 1);
2649             return data;
2650         },
2651
2652         headers:{},
2653
2654         hasHeaders:false,
2655
2656         useDefaultHeader:true,
2657
2658         defaultPostHeader:'application/x-www-form-urlencoded',
2659
2660         useDefaultXhrHeader:true,
2661
2662         defaultXhrHeader:'XMLHttpRequest',
2663
2664         hasDefaultHeaders:true,
2665
2666         defaultHeaders:{},
2667
2668         poll:{},
2669
2670         timeout:{},
2671
2672         pollInterval:50,
2673
2674         transactionId:0,
2675
2676         setProgId:function(id)
2677         {
2678             this.activeX.unshift(id);
2679         },
2680
2681         setDefaultPostHeader:function(b)
2682         {
2683             this.useDefaultHeader = b;
2684         },
2685
2686         setDefaultXhrHeader:function(b)
2687         {
2688             this.useDefaultXhrHeader = b;
2689         },
2690
2691         setPollingInterval:function(i)
2692         {
2693             if (typeof i == 'number' && isFinite(i)) {
2694                 this.pollInterval = i;
2695             }
2696         },
2697
2698         createXhrObject:function(transactionId)
2699         {
2700             var obj,http;
2701             try
2702             {
2703
2704                 http = new XMLHttpRequest();
2705
2706                 obj = { conn:http, tId:transactionId };
2707             }
2708             catch(e)
2709             {
2710                 for (var i = 0; i < this.activeX.length; ++i) {
2711                     try
2712                     {
2713
2714                         http = new ActiveXObject(this.activeX[i]);
2715
2716                         obj = { conn:http, tId:transactionId };
2717                         break;
2718                     }
2719                     catch(e) {
2720                     }
2721                 }
2722             }
2723             finally
2724             {
2725                 return obj;
2726             }
2727         },
2728
2729         getConnectionObject:function()
2730         {
2731             var o;
2732             var tId = this.transactionId;
2733
2734             try
2735             {
2736                 o = this.createXhrObject(tId);
2737                 if (o) {
2738                     this.transactionId++;
2739                 }
2740             }
2741             catch(e) {
2742             }
2743             finally
2744             {
2745                 return o;
2746             }
2747         },
2748
2749         asyncRequest:function(method, uri, callback, postData)
2750         {
2751             var o = this.getConnectionObject();
2752
2753             if (!o) {
2754                 return null;
2755             }
2756             else {
2757                 o.conn.open(method, uri, true);
2758
2759                 if (this.useDefaultXhrHeader) {
2760                     if (!this.defaultHeaders['X-Requested-With']) {
2761                         this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2762                     }
2763                 }
2764
2765                 if(postData && this.useDefaultHeader){
2766                     this.initHeader('Content-Type', this.defaultPostHeader);
2767                 }
2768
2769                  if (this.hasDefaultHeaders || this.hasHeaders) {
2770                     this.setHeader(o);
2771                 }
2772
2773                 this.handleReadyState(o, callback);
2774                 o.conn.send(postData || null);
2775
2776                 return o;
2777             }
2778         },
2779
2780         handleReadyState:function(o, callback)
2781         {
2782             var oConn = this;
2783
2784             if (callback && callback.timeout) {
2785                 
2786                 this.timeout[o.tId] = window.setTimeout(function() {
2787                     oConn.abort(o, callback, true);
2788                 }, callback.timeout);
2789             }
2790
2791             this.poll[o.tId] = window.setInterval(
2792                     function() {
2793                         if (o.conn && o.conn.readyState == 4) {
2794                             window.clearInterval(oConn.poll[o.tId]);
2795                             delete oConn.poll[o.tId];
2796
2797                             if(callback && callback.timeout) {
2798                                 window.clearTimeout(oConn.timeout[o.tId]);
2799                                 delete oConn.timeout[o.tId];
2800                             }
2801
2802                             oConn.handleTransactionResponse(o, callback);
2803                         }
2804                     }
2805                     , this.pollInterval);
2806         },
2807
2808         handleTransactionResponse:function(o, callback, isAbort)
2809         {
2810
2811             if (!callback) {
2812                 this.releaseObject(o);
2813                 return;
2814             }
2815
2816             var httpStatus, responseObject;
2817
2818             try
2819             {
2820                 if (o.conn.status !== undefined && o.conn.status != 0) {
2821                     httpStatus = o.conn.status;
2822                 }
2823                 else {
2824                     httpStatus = 13030;
2825                 }
2826             }
2827             catch(e) {
2828
2829
2830                 httpStatus = 13030;
2831             }
2832
2833             if (httpStatus >= 200 && httpStatus < 300) {
2834                 responseObject = this.createResponseObject(o, callback.argument);
2835                 if (callback.success) {
2836                     if (!callback.scope) {
2837                         callback.success(responseObject);
2838                     }
2839                     else {
2840
2841
2842                         callback.success.apply(callback.scope, [responseObject]);
2843                     }
2844                 }
2845             }
2846             else {
2847                 switch (httpStatus) {
2848
2849                     case 12002:
2850                     case 12029:
2851                     case 12030:
2852                     case 12031:
2853                     case 12152:
2854                     case 13030:
2855                         responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
2856                         if (callback.failure) {
2857                             if (!callback.scope) {
2858                                 callback.failure(responseObject);
2859                             }
2860                             else {
2861                                 callback.failure.apply(callback.scope, [responseObject]);
2862                             }
2863                         }
2864                         break;
2865                     default:
2866                         responseObject = this.createResponseObject(o, callback.argument);
2867                         if (callback.failure) {
2868                             if (!callback.scope) {
2869                                 callback.failure(responseObject);
2870                             }
2871                             else {
2872                                 callback.failure.apply(callback.scope, [responseObject]);
2873                             }
2874                         }
2875                 }
2876             }
2877
2878             this.releaseObject(o);
2879             responseObject = null;
2880         },
2881
2882         createResponseObject:function(o, callbackArg)
2883         {
2884             var obj = {};
2885             var headerObj = {};
2886
2887             try
2888             {
2889                 var headerStr = o.conn.getAllResponseHeaders();
2890                 var header = headerStr.split('\n');
2891                 for (var i = 0; i < header.length; i++) {
2892                     var delimitPos = header[i].indexOf(':');
2893                     if (delimitPos != -1) {
2894                         headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
2895                     }
2896                 }
2897             }
2898             catch(e) {
2899             }
2900
2901             obj.tId = o.tId;
2902             obj.status = o.conn.status;
2903             obj.statusText = o.conn.statusText;
2904             obj.getResponseHeader = headerObj;
2905             obj.getAllResponseHeaders = headerStr;
2906             obj.responseText = o.conn.responseText;
2907             obj.responseXML = o.conn.responseXML;
2908
2909             if (typeof callbackArg !== undefined) {
2910                 obj.argument = callbackArg;
2911             }
2912
2913             return obj;
2914         },
2915
2916         createExceptionObject:function(tId, callbackArg, isAbort)
2917         {
2918             var COMM_CODE = 0;
2919             var COMM_ERROR = 'communication failure';
2920             var ABORT_CODE = -1;
2921             var ABORT_ERROR = 'transaction aborted';
2922
2923             var obj = {};
2924
2925             obj.tId = tId;
2926             if (isAbort) {
2927                 obj.status = ABORT_CODE;
2928                 obj.statusText = ABORT_ERROR;
2929             }
2930             else {
2931                 obj.status = COMM_CODE;
2932                 obj.statusText = COMM_ERROR;
2933             }
2934
2935             if (callbackArg) {
2936                 obj.argument = callbackArg;
2937             }
2938
2939             return obj;
2940         },
2941
2942         initHeader:function(label, value, isDefault)
2943         {
2944             var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
2945
2946             if (headerObj[label] === undefined) {
2947                 headerObj[label] = value;
2948             }
2949             else {
2950
2951
2952                 headerObj[label] = value + "," + headerObj[label];
2953             }
2954
2955             if (isDefault) {
2956                 this.hasDefaultHeaders = true;
2957             }
2958             else {
2959                 this.hasHeaders = true;
2960             }
2961         },
2962
2963
2964         setHeader:function(o)
2965         {
2966             if (this.hasDefaultHeaders) {
2967                 for (var prop in this.defaultHeaders) {
2968                     if (this.defaultHeaders.hasOwnProperty(prop)) {
2969                         o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
2970                     }
2971                 }
2972             }
2973
2974             if (this.hasHeaders) {
2975                 for (var prop in this.headers) {
2976                     if (this.headers.hasOwnProperty(prop)) {
2977                         o.conn.setRequestHeader(prop, this.headers[prop]);
2978                     }
2979                 }
2980                 this.headers = {};
2981                 this.hasHeaders = false;
2982             }
2983         },
2984
2985         resetDefaultHeaders:function() {
2986             delete this.defaultHeaders;
2987             this.defaultHeaders = {};
2988             this.hasDefaultHeaders = false;
2989         },
2990
2991         abort:function(o, callback, isTimeout)
2992         {
2993             if(this.isCallInProgress(o)) {
2994                 o.conn.abort();
2995                 window.clearInterval(this.poll[o.tId]);
2996                 delete this.poll[o.tId];
2997                 if (isTimeout) {
2998                     delete this.timeout[o.tId];
2999                 }
3000
3001                 this.handleTransactionResponse(o, callback, true);
3002
3003                 return true;
3004             }
3005             else {
3006                 return false;
3007             }
3008         },
3009
3010
3011         isCallInProgress:function(o)
3012         {
3013             if (o && o.conn) {
3014                 return o.conn.readyState != 4 && o.conn.readyState != 0;
3015             }
3016             else {
3017
3018                 return false;
3019             }
3020         },
3021
3022
3023         releaseObject:function(o)
3024         {
3025
3026             o.conn = null;
3027
3028             o = null;
3029         },
3030
3031         activeX:[
3032         'MSXML2.XMLHTTP.3.0',
3033         'MSXML2.XMLHTTP',
3034         'Microsoft.XMLHTTP'
3035         ]
3036
3037
3038     };
3039 })();/*
3040  * Portions of this file are based on pieces of Yahoo User Interface Library
3041  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3042  * YUI licensed under the BSD License:
3043  * http://developer.yahoo.net/yui/license.txt
3044  * <script type="text/javascript">
3045  *
3046  */
3047
3048 Roo.lib.Region = function(t, r, b, l) {
3049     this.top = t;
3050     this[1] = t;
3051     this.right = r;
3052     this.bottom = b;
3053     this.left = l;
3054     this[0] = l;
3055 };
3056
3057
3058 Roo.lib.Region.prototype = {
3059     contains : function(region) {
3060         return ( region.left >= this.left &&
3061                  region.right <= this.right &&
3062                  region.top >= this.top &&
3063                  region.bottom <= this.bottom    );
3064
3065     },
3066
3067     getArea : function() {
3068         return ( (this.bottom - this.top) * (this.right - this.left) );
3069     },
3070
3071     intersect : function(region) {
3072         var t = Math.max(this.top, region.top);
3073         var r = Math.min(this.right, region.right);
3074         var b = Math.min(this.bottom, region.bottom);
3075         var l = Math.max(this.left, region.left);
3076
3077         if (b >= t && r >= l) {
3078             return new Roo.lib.Region(t, r, b, l);
3079         } else {
3080             return null;
3081         }
3082     },
3083     union : function(region) {
3084         var t = Math.min(this.top, region.top);
3085         var r = Math.max(this.right, region.right);
3086         var b = Math.max(this.bottom, region.bottom);
3087         var l = Math.min(this.left, region.left);
3088
3089         return new Roo.lib.Region(t, r, b, l);
3090     },
3091
3092     adjust : function(t, l, b, r) {
3093         this.top += t;
3094         this.left += l;
3095         this.right += r;
3096         this.bottom += b;
3097         return this;
3098     }
3099 };
3100
3101 Roo.lib.Region.getRegion = function(el) {
3102     var p = Roo.lib.Dom.getXY(el);
3103
3104     var t = p[1];
3105     var r = p[0] + el.offsetWidth;
3106     var b = p[1] + el.offsetHeight;
3107     var l = p[0];
3108
3109     return new Roo.lib.Region(t, r, b, l);
3110 };
3111 /*
3112  * Portions of this file are based on pieces of Yahoo User Interface Library
3113  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3114  * YUI licensed under the BSD License:
3115  * http://developer.yahoo.net/yui/license.txt
3116  * <script type="text/javascript">
3117  *
3118  */
3119 //@@dep Roo.lib.Region
3120
3121
3122 Roo.lib.Point = function(x, y) {
3123     if (x instanceof Array) {
3124         y = x[1];
3125         x = x[0];
3126     }
3127     this.x = this.right = this.left = this[0] = x;
3128     this.y = this.top = this.bottom = this[1] = y;
3129 };
3130
3131 Roo.lib.Point.prototype = new Roo.lib.Region();
3132 /*
3133  * Portions of this file are based on pieces of Yahoo User Interface Library
3134  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3135  * YUI licensed under the BSD License:
3136  * http://developer.yahoo.net/yui/license.txt
3137  * <script type="text/javascript">
3138  *
3139  */
3140  
3141 (function() {   
3142
3143     Roo.lib.Anim = {
3144         scroll : function(el, args, duration, easing, cb, scope) {
3145             this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
3146         },
3147
3148         motion : function(el, args, duration, easing, cb, scope) {
3149             this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
3150         },
3151
3152         color : function(el, args, duration, easing, cb, scope) {
3153             this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
3154         },
3155
3156         run : function(el, args, duration, easing, cb, scope, type) {
3157             type = type || Roo.lib.AnimBase;
3158             if (typeof easing == "string") {
3159                 easing = Roo.lib.Easing[easing];
3160             }
3161             var anim = new type(el, args, duration, easing);
3162             anim.animateX(function() {
3163                 Roo.callback(cb, scope);
3164             });
3165             return anim;
3166         }
3167     };
3168 })();/*
3169  * Portions of this file are based on pieces of Yahoo User Interface Library
3170  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3171  * YUI licensed under the BSD License:
3172  * http://developer.yahoo.net/yui/license.txt
3173  * <script type="text/javascript">
3174  *
3175  */
3176
3177 (function() {    
3178     var libFlyweight;
3179     
3180     function fly(el) {
3181         if (!libFlyweight) {
3182             libFlyweight = new Roo.Element.Flyweight();
3183         }
3184         libFlyweight.dom = el;
3185         return libFlyweight;
3186     }
3187
3188     // since this uses fly! - it cant be in DOM (which does not have fly yet..)
3189     
3190    
3191     
3192     Roo.lib.AnimBase = function(el, attributes, duration, method) {
3193         if (el) {
3194             this.init(el, attributes, duration, method);
3195         }
3196     };
3197
3198     Roo.lib.AnimBase.fly = fly;
3199     
3200     
3201     
3202     Roo.lib.AnimBase.prototype = {
3203
3204         toString: function() {
3205             var el = this.getEl();
3206             var id = el.id || el.tagName;
3207             return ("Anim " + id);
3208         },
3209
3210         patterns: {
3211             noNegatives:        /width|height|opacity|padding/i,
3212             offsetAttribute:  /^((width|height)|(top|left))$/,
3213             defaultUnit:        /width|height|top$|bottom$|left$|right$/i,
3214             offsetUnit:         /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
3215         },
3216
3217
3218         doMethod: function(attr, start, end) {
3219             return this.method(this.currentFrame, start, end - start, this.totalFrames);
3220         },
3221
3222
3223         setAttribute: function(attr, val, unit) {
3224             if (this.patterns.noNegatives.test(attr)) {
3225                 val = (val > 0) ? val : 0;
3226             }
3227
3228             Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
3229         },
3230
3231
3232         getAttribute: function(attr) {
3233             var el = this.getEl();
3234             var val = fly(el).getStyle(attr);
3235
3236             if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
3237                 return parseFloat(val);
3238             }
3239
3240             var a = this.patterns.offsetAttribute.exec(attr) || [];
3241             var pos = !!( a[3] );
3242             var box = !!( a[2] );
3243
3244
3245             if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3246                 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3247             } else {
3248                 val = 0;
3249             }
3250
3251             return val;
3252         },
3253
3254
3255         getDefaultUnit: function(attr) {
3256             if (this.patterns.defaultUnit.test(attr)) {
3257                 return 'px';
3258             }
3259
3260             return '';
3261         },
3262
3263         animateX : function(callback, scope) {
3264             var f = function() {
3265                 this.onComplete.removeListener(f);
3266                 if (typeof callback == "function") {
3267                     callback.call(scope || this, this);
3268                 }
3269             };
3270             this.onComplete.addListener(f, this);
3271             this.animate();
3272         },
3273
3274
3275         setRuntimeAttribute: function(attr) {
3276             var start;
3277             var end;
3278             var attributes = this.attributes;
3279
3280             this.runtimeAttributes[attr] = {};
3281
3282             var isset = function(prop) {
3283                 return (typeof prop !== 'undefined');
3284             };
3285
3286             if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3287                 return false;
3288             }
3289
3290             start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3291
3292
3293             if (isset(attributes[attr]['to'])) {
3294                 end = attributes[attr]['to'];
3295             } else if (isset(attributes[attr]['by'])) {
3296                 if (start.constructor == Array) {
3297                     end = [];
3298                     for (var i = 0, len = start.length; i < len; ++i) {
3299                         end[i] = start[i] + attributes[attr]['by'][i];
3300                     }
3301                 } else {
3302                     end = start + attributes[attr]['by'];
3303                 }
3304             }
3305
3306             this.runtimeAttributes[attr].start = start;
3307             this.runtimeAttributes[attr].end = end;
3308
3309
3310             this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3311         },
3312
3313
3314         init: function(el, attributes, duration, method) {
3315
3316             var isAnimated = false;
3317
3318
3319             var startTime = null;
3320
3321
3322             var actualFrames = 0;
3323
3324
3325             el = Roo.getDom(el);
3326
3327
3328             this.attributes = attributes || {};
3329
3330
3331             this.duration = duration || 1;
3332
3333
3334             this.method = method || Roo.lib.Easing.easeNone;
3335
3336
3337             this.useSeconds = true;
3338
3339
3340             this.currentFrame = 0;
3341
3342
3343             this.totalFrames = Roo.lib.AnimMgr.fps;
3344
3345
3346             this.getEl = function() {
3347                 return el;
3348             };
3349
3350
3351             this.isAnimated = function() {
3352                 return isAnimated;
3353             };
3354
3355
3356             this.getStartTime = function() {
3357                 return startTime;
3358             };
3359
3360             this.runtimeAttributes = {};
3361
3362
3363             this.animate = function() {
3364                 if (this.isAnimated()) {
3365                     return false;
3366                 }
3367
3368                 this.currentFrame = 0;
3369
3370                 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3371
3372                 Roo.lib.AnimMgr.registerElement(this);
3373             };
3374
3375
3376             this.stop = function(finish) {
3377                 if (finish) {
3378                     this.currentFrame = this.totalFrames;
3379                     this._onTween.fire();
3380                 }
3381                 Roo.lib.AnimMgr.stop(this);
3382             };
3383
3384             var onStart = function() {
3385                 this.onStart.fire();
3386
3387                 this.runtimeAttributes = {};
3388                 for (var attr in this.attributes) {
3389                     this.setRuntimeAttribute(attr);
3390                 }
3391
3392                 isAnimated = true;
3393                 actualFrames = 0;
3394                 startTime = new Date();
3395             };
3396
3397
3398             var onTween = function() {
3399                 var data = {
3400                     duration: new Date() - this.getStartTime(),
3401                     currentFrame: this.currentFrame
3402                 };
3403
3404                 data.toString = function() {
3405                     return (
3406                             'duration: ' + data.duration +
3407                             ', currentFrame: ' + data.currentFrame
3408                             );
3409                 };
3410
3411                 this.onTween.fire(data);
3412
3413                 var runtimeAttributes = this.runtimeAttributes;
3414
3415                 for (var attr in runtimeAttributes) {
3416                     this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3417                 }
3418
3419                 actualFrames += 1;
3420             };
3421
3422             var onComplete = function() {
3423                 var actual_duration = (new Date() - startTime) / 1000 ;
3424
3425                 var data = {
3426                     duration: actual_duration,
3427                     frames: actualFrames,
3428                     fps: actualFrames / actual_duration
3429                 };
3430
3431                 data.toString = function() {
3432                     return (
3433                             'duration: ' + data.duration +
3434                             ', frames: ' + data.frames +
3435                             ', fps: ' + data.fps
3436                             );
3437                 };
3438
3439                 isAnimated = false;
3440                 actualFrames = 0;
3441                 this.onComplete.fire(data);
3442             };
3443
3444
3445             this._onStart = new Roo.util.Event(this);
3446             this.onStart = new Roo.util.Event(this);
3447             this.onTween = new Roo.util.Event(this);
3448             this._onTween = new Roo.util.Event(this);
3449             this.onComplete = new Roo.util.Event(this);
3450             this._onComplete = new Roo.util.Event(this);
3451             this._onStart.addListener(onStart);
3452             this._onTween.addListener(onTween);
3453             this._onComplete.addListener(onComplete);
3454         }
3455     };
3456 })();
3457 /*
3458  * Portions of this file are based on pieces of Yahoo User Interface Library
3459  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3460  * YUI licensed under the BSD License:
3461  * http://developer.yahoo.net/yui/license.txt
3462  * <script type="text/javascript">
3463  *
3464  */
3465
3466 Roo.lib.AnimMgr = new function() {
3467
3468     var thread = null;
3469
3470
3471     var queue = [];
3472
3473
3474     var tweenCount = 0;
3475
3476
3477     this.fps = 1000;
3478
3479
3480     this.delay = 1;
3481
3482
3483     this.registerElement = function(tween) {
3484         queue[queue.length] = tween;
3485         tweenCount += 1;
3486         tween._onStart.fire();
3487         this.start();
3488     };
3489
3490
3491     this.unRegister = function(tween, index) {
3492         tween._onComplete.fire();
3493         index = index || getIndex(tween);
3494         if (index != -1) {
3495             queue.splice(index, 1);
3496         }
3497
3498         tweenCount -= 1;
3499         if (tweenCount <= 0) {
3500             this.stop();
3501         }
3502     };
3503
3504
3505     this.start = function() {
3506         if (thread === null) {
3507             thread = setInterval(this.run, this.delay);
3508         }
3509     };
3510
3511
3512     this.stop = function(tween) {
3513         if (!tween) {
3514             clearInterval(thread);
3515
3516             for (var i = 0, len = queue.length; i < len; ++i) {
3517                 if (queue[0].isAnimated()) {
3518                     this.unRegister(queue[0], 0);
3519                 }
3520             }
3521
3522             queue = [];
3523             thread = null;
3524             tweenCount = 0;
3525         }
3526         else {
3527             this.unRegister(tween);
3528         }
3529     };
3530
3531
3532     this.run = function() {
3533         for (var i = 0, len = queue.length; i < len; ++i) {
3534             var tween = queue[i];
3535             if (!tween || !tween.isAnimated()) {
3536                 continue;
3537             }
3538
3539             if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3540             {
3541                 tween.currentFrame += 1;
3542
3543                 if (tween.useSeconds) {
3544                     correctFrame(tween);
3545                 }
3546                 tween._onTween.fire();
3547             }
3548             else {
3549                 Roo.lib.AnimMgr.stop(tween, i);
3550             }
3551         }
3552     };
3553
3554     var getIndex = function(anim) {
3555         for (var i = 0, len = queue.length; i < len; ++i) {
3556             if (queue[i] == anim) {
3557                 return i;
3558             }
3559         }
3560         return -1;
3561     };
3562
3563
3564     var correctFrame = function(tween) {
3565         var frames = tween.totalFrames;
3566         var frame = tween.currentFrame;
3567         var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3568         var elapsed = (new Date() - tween.getStartTime());
3569         var tweak = 0;
3570
3571         if (elapsed < tween.duration * 1000) {
3572             tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3573         } else {
3574             tweak = frames - (frame + 1);
3575         }
3576         if (tweak > 0 && isFinite(tweak)) {
3577             if (tween.currentFrame + tweak >= frames) {
3578                 tweak = frames - (frame + 1);
3579             }
3580
3581             tween.currentFrame += tweak;
3582         }
3583     };
3584 };
3585
3586     /*
3587  * Portions of this file are based on pieces of Yahoo User Interface Library
3588  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3589  * YUI licensed under the BSD License:
3590  * http://developer.yahoo.net/yui/license.txt
3591  * <script type="text/javascript">
3592  *
3593  */
3594 Roo.lib.Bezier = new function() {
3595
3596         this.getPosition = function(points, t) {
3597             var n = points.length;
3598             var tmp = [];
3599
3600             for (var i = 0; i < n; ++i) {
3601                 tmp[i] = [points[i][0], points[i][1]];
3602             }
3603
3604             for (var j = 1; j < n; ++j) {
3605                 for (i = 0; i < n - j; ++i) {
3606                     tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3607                     tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3608                 }
3609             }
3610
3611             return [ tmp[0][0], tmp[0][1] ];
3612
3613         };
3614     };/*
3615  * Portions of this file are based on pieces of Yahoo User Interface Library
3616  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3617  * YUI licensed under the BSD License:
3618  * http://developer.yahoo.net/yui/license.txt
3619  * <script type="text/javascript">
3620  *
3621  */
3622 (function() {
3623
3624     Roo.lib.ColorAnim = function(el, attributes, duration, method) {
3625         Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
3626     };
3627
3628     Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
3629
3630     var fly = Roo.lib.AnimBase.fly;
3631     var Y = Roo.lib;
3632     var superclass = Y.ColorAnim.superclass;
3633     var proto = Y.ColorAnim.prototype;
3634
3635     proto.toString = function() {
3636         var el = this.getEl();
3637         var id = el.id || el.tagName;
3638         return ("ColorAnim " + id);
3639     };
3640
3641     proto.patterns.color = /color$/i;
3642     proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
3643     proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
3644     proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
3645     proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
3646
3647
3648     proto.parseColor = function(s) {
3649         if (s.length == 3) {
3650             return s;
3651         }
3652
3653         var c = this.patterns.hex.exec(s);
3654         if (c && c.length == 4) {
3655             return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
3656         }
3657
3658         c = this.patterns.rgb.exec(s);
3659         if (c && c.length == 4) {
3660             return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
3661         }
3662
3663         c = this.patterns.hex3.exec(s);
3664         if (c && c.length == 4) {
3665             return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
3666         }
3667
3668         return null;
3669     };
3670     // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
3671     proto.getAttribute = function(attr) {
3672         var el = this.getEl();
3673         if (this.patterns.color.test(attr)) {
3674             var val = fly(el).getStyle(attr);
3675
3676             if (this.patterns.transparent.test(val)) {
3677                 var parent = el.parentNode;
3678                 val = fly(parent).getStyle(attr);
3679
3680                 while (parent && this.patterns.transparent.test(val)) {
3681                     parent = parent.parentNode;
3682                     val = fly(parent).getStyle(attr);
3683                     if (parent.tagName.toUpperCase() == 'HTML') {
3684                         val = '#fff';
3685                     }
3686                 }
3687             }
3688         } else {
3689             val = superclass.getAttribute.call(this, attr);
3690         }
3691
3692         return val;
3693     };
3694     proto.getAttribute = function(attr) {
3695         var el = this.getEl();
3696         if (this.patterns.color.test(attr)) {
3697             var val = fly(el).getStyle(attr);
3698
3699             if (this.patterns.transparent.test(val)) {
3700                 var parent = el.parentNode;
3701                 val = fly(parent).getStyle(attr);
3702
3703                 while (parent && this.patterns.transparent.test(val)) {
3704                     parent = parent.parentNode;
3705                     val = fly(parent).getStyle(attr);
3706                     if (parent.tagName.toUpperCase() == 'HTML') {
3707                         val = '#fff';
3708                     }
3709                 }
3710             }
3711         } else {
3712             val = superclass.getAttribute.call(this, attr);
3713         }
3714
3715         return val;
3716     };
3717
3718     proto.doMethod = function(attr, start, end) {
3719         var val;
3720
3721         if (this.patterns.color.test(attr)) {
3722             val = [];
3723             for (var i = 0, len = start.length; i < len; ++i) {
3724                 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
3725             }
3726
3727             val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
3728         }
3729         else {
3730             val = superclass.doMethod.call(this, attr, start, end);
3731         }
3732
3733         return val;
3734     };
3735
3736     proto.setRuntimeAttribute = function(attr) {
3737         superclass.setRuntimeAttribute.call(this, attr);
3738
3739         if (this.patterns.color.test(attr)) {
3740             var attributes = this.attributes;
3741             var start = this.parseColor(this.runtimeAttributes[attr].start);
3742             var end = this.parseColor(this.runtimeAttributes[attr].end);
3743
3744             if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
3745                 end = this.parseColor(attributes[attr].by);
3746
3747                 for (var i = 0, len = start.length; i < len; ++i) {
3748                     end[i] = start[i] + end[i];
3749                 }
3750             }
3751
3752             this.runtimeAttributes[attr].start = start;
3753             this.runtimeAttributes[attr].end = end;
3754         }
3755     };
3756 })();
3757
3758 /*
3759  * Portions of this file are based on pieces of Yahoo User Interface Library
3760  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3761  * YUI licensed under the BSD License:
3762  * http://developer.yahoo.net/yui/license.txt
3763  * <script type="text/javascript">
3764  *
3765  */
3766 Roo.lib.Easing = {
3767
3768
3769     easeNone: function (t, b, c, d) {
3770         return c * t / d + b;
3771     },
3772
3773
3774     easeIn: function (t, b, c, d) {
3775         return c * (t /= d) * t + b;
3776     },
3777
3778
3779     easeOut: function (t, b, c, d) {
3780         return -c * (t /= d) * (t - 2) + b;
3781     },
3782
3783
3784     easeBoth: function (t, b, c, d) {
3785         if ((t /= d / 2) < 1) {
3786             return c / 2 * t * t + b;
3787         }
3788
3789         return -c / 2 * ((--t) * (t - 2) - 1) + b;
3790     },
3791
3792
3793     easeInStrong: function (t, b, c, d) {
3794         return c * (t /= d) * t * t * t + b;
3795     },
3796
3797
3798     easeOutStrong: function (t, b, c, d) {
3799         return -c * ((t = t / d - 1) * t * t * t - 1) + b;
3800     },
3801
3802
3803     easeBothStrong: function (t, b, c, d) {
3804         if ((t /= d / 2) < 1) {
3805             return c / 2 * t * t * t * t + b;
3806         }
3807
3808         return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
3809     },
3810
3811
3812
3813     elasticIn: function (t, b, c, d, a, p) {
3814         if (t == 0) {
3815             return b;
3816         }
3817         if ((t /= d) == 1) {
3818             return b + c;
3819         }
3820         if (!p) {
3821             p = d * .3;
3822         }
3823
3824         if (!a || a < Math.abs(c)) {
3825             a = c;
3826             var s = p / 4;
3827         }
3828         else {
3829             var s = p / (2 * Math.PI) * Math.asin(c / a);
3830         }
3831
3832         return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3833     },
3834
3835
3836     elasticOut: function (t, b, c, d, a, p) {
3837         if (t == 0) {
3838             return b;
3839         }
3840         if ((t /= d) == 1) {
3841             return b + c;
3842         }
3843         if (!p) {
3844             p = d * .3;
3845         }
3846
3847         if (!a || a < Math.abs(c)) {
3848             a = c;
3849             var s = p / 4;
3850         }
3851         else {
3852             var s = p / (2 * Math.PI) * Math.asin(c / a);
3853         }
3854
3855         return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
3856     },
3857
3858
3859     elasticBoth: function (t, b, c, d, a, p) {
3860         if (t == 0) {
3861             return b;
3862         }
3863
3864         if ((t /= d / 2) == 2) {
3865             return b + c;
3866         }
3867
3868         if (!p) {
3869             p = d * (.3 * 1.5);
3870         }
3871
3872         if (!a || a < Math.abs(c)) {
3873             a = c;
3874             var s = p / 4;
3875         }
3876         else {
3877             var s = p / (2 * Math.PI) * Math.asin(c / a);
3878         }
3879
3880         if (t < 1) {
3881             return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
3882                           Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3883         }
3884         return a * Math.pow(2, -10 * (t -= 1)) *
3885                Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
3886     },
3887
3888
3889
3890     backIn: function (t, b, c, d, s) {
3891         if (typeof s == 'undefined') {
3892             s = 1.70158;
3893         }
3894         return c * (t /= d) * t * ((s + 1) * t - s) + b;
3895     },
3896
3897
3898     backOut: function (t, b, c, d, s) {
3899         if (typeof s == 'undefined') {
3900             s = 1.70158;
3901         }
3902         return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
3903     },
3904
3905
3906     backBoth: function (t, b, c, d, s) {
3907         if (typeof s == 'undefined') {
3908             s = 1.70158;
3909         }
3910
3911         if ((t /= d / 2 ) < 1) {
3912             return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
3913         }
3914         return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
3915     },
3916
3917
3918     bounceIn: function (t, b, c, d) {
3919         return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
3920     },
3921
3922
3923     bounceOut: function (t, b, c, d) {
3924         if ((t /= d) < (1 / 2.75)) {
3925             return c * (7.5625 * t * t) + b;
3926         } else if (t < (2 / 2.75)) {
3927             return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
3928         } else if (t < (2.5 / 2.75)) {
3929             return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
3930         }
3931         return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
3932     },
3933
3934
3935     bounceBoth: function (t, b, c, d) {
3936         if (t < d / 2) {
3937             return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
3938         }
3939         return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
3940     }
3941 };/*
3942  * Portions of this file are based on pieces of Yahoo User Interface Library
3943  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3944  * YUI licensed under the BSD License:
3945  * http://developer.yahoo.net/yui/license.txt
3946  * <script type="text/javascript">
3947  *
3948  */
3949     (function() {
3950         Roo.lib.Motion = function(el, attributes, duration, method) {
3951             if (el) {
3952                 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
3953             }
3954         };
3955
3956         Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
3957
3958
3959         var Y = Roo.lib;
3960         var superclass = Y.Motion.superclass;
3961         var proto = Y.Motion.prototype;
3962
3963         proto.toString = function() {
3964             var el = this.getEl();
3965             var id = el.id || el.tagName;
3966             return ("Motion " + id);
3967         };
3968
3969         proto.patterns.points = /^points$/i;
3970
3971         proto.setAttribute = function(attr, val, unit) {
3972             if (this.patterns.points.test(attr)) {
3973                 unit = unit || 'px';
3974                 superclass.setAttribute.call(this, 'left', val[0], unit);
3975                 superclass.setAttribute.call(this, 'top', val[1], unit);
3976             } else {
3977                 superclass.setAttribute.call(this, attr, val, unit);
3978             }
3979         };
3980
3981         proto.getAttribute = function(attr) {
3982             if (this.patterns.points.test(attr)) {
3983                 var val = [
3984                         superclass.getAttribute.call(this, 'left'),
3985                         superclass.getAttribute.call(this, 'top')
3986                         ];
3987             } else {
3988                 val = superclass.getAttribute.call(this, attr);
3989             }
3990
3991             return val;
3992         };
3993
3994         proto.doMethod = function(attr, start, end) {
3995             var val = null;
3996
3997             if (this.patterns.points.test(attr)) {
3998                 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
3999                 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
4000             } else {
4001                 val = superclass.doMethod.call(this, attr, start, end);
4002             }
4003             return val;
4004         };
4005
4006         proto.setRuntimeAttribute = function(attr) {
4007             if (this.patterns.points.test(attr)) {
4008                 var el = this.getEl();
4009                 var attributes = this.attributes;
4010                 var start;
4011                 var control = attributes['points']['control'] || [];
4012                 var end;
4013                 var i, len;
4014
4015                 if (control.length > 0 && !(control[0] instanceof Array)) {
4016                     control = [control];
4017                 } else {
4018                     var tmp = [];
4019                     for (i = 0,len = control.length; i < len; ++i) {
4020                         tmp[i] = control[i];
4021                     }
4022                     control = tmp;
4023                 }
4024
4025                 Roo.fly(el).position();
4026
4027                 if (isset(attributes['points']['from'])) {
4028                     Roo.lib.Dom.setXY(el, attributes['points']['from']);
4029                 }
4030                 else {
4031                     Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
4032                 }
4033
4034                 start = this.getAttribute('points');
4035
4036
4037                 if (isset(attributes['points']['to'])) {
4038                     end = translateValues.call(this, attributes['points']['to'], start);
4039
4040                     var pageXY = Roo.lib.Dom.getXY(this.getEl());
4041                     for (i = 0,len = control.length; i < len; ++i) {
4042                         control[i] = translateValues.call(this, control[i], start);
4043                     }
4044
4045
4046                 } else if (isset(attributes['points']['by'])) {
4047                     end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
4048
4049                     for (i = 0,len = control.length; i < len; ++i) {
4050                         control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
4051                     }
4052                 }
4053
4054                 this.runtimeAttributes[attr] = [start];
4055
4056                 if (control.length > 0) {
4057                     this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
4058                 }
4059
4060                 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
4061             }
4062             else {
4063                 superclass.setRuntimeAttribute.call(this, attr);
4064             }
4065         };
4066
4067         var translateValues = function(val, start) {
4068             var pageXY = Roo.lib.Dom.getXY(this.getEl());
4069             val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
4070
4071             return val;
4072         };
4073
4074         var isset = function(prop) {
4075             return (typeof prop !== 'undefined');
4076         };
4077     })();
4078 /*
4079  * Portions of this file are based on pieces of Yahoo User Interface Library
4080  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4081  * YUI licensed under the BSD License:
4082  * http://developer.yahoo.net/yui/license.txt
4083  * <script type="text/javascript">
4084  *
4085  */
4086     (function() {
4087         Roo.lib.Scroll = function(el, attributes, duration, method) {
4088             if (el) {
4089                 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
4090             }
4091         };
4092
4093         Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
4094
4095
4096         var Y = Roo.lib;
4097         var superclass = Y.Scroll.superclass;
4098         var proto = Y.Scroll.prototype;
4099
4100         proto.toString = function() {
4101             var el = this.getEl();
4102             var id = el.id || el.tagName;
4103             return ("Scroll " + id);
4104         };
4105
4106         proto.doMethod = function(attr, start, end) {
4107             var val = null;
4108
4109             if (attr == 'scroll') {
4110                 val = [
4111                         this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
4112                         this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
4113                         ];
4114
4115             } else {
4116                 val = superclass.doMethod.call(this, attr, start, end);
4117             }
4118             return val;
4119         };
4120
4121         proto.getAttribute = function(attr) {
4122             var val = null;
4123             var el = this.getEl();
4124
4125             if (attr == 'scroll') {
4126                 val = [ el.scrollLeft, el.scrollTop ];
4127             } else {
4128                 val = superclass.getAttribute.call(this, attr);
4129             }
4130
4131             return val;
4132         };
4133
4134         proto.setAttribute = function(attr, val, unit) {
4135             var el = this.getEl();
4136
4137             if (attr == 'scroll') {
4138                 el.scrollLeft = val[0];
4139                 el.scrollTop = val[1];
4140             } else {
4141                 superclass.setAttribute.call(this, attr, val, unit);
4142             }
4143         };
4144     })();
4145 /*
4146  * Based on:
4147  * Ext JS Library 1.1.1
4148  * Copyright(c) 2006-2007, Ext JS, LLC.
4149  *
4150  * Originally Released Under LGPL - original licence link has changed is not relivant.
4151  *
4152  * Fork - LGPL
4153  * <script type="text/javascript">
4154  */
4155
4156
4157 // nasty IE9 hack - what a pile of crap that is..
4158
4159  if (typeof Range != "undefined" && typeof Range.prototype.createContextualFragment == "undefined") {
4160     Range.prototype.createContextualFragment = function (html) {
4161         var doc = window.document;
4162         var container = doc.createElement("div");
4163         container.innerHTML = html;
4164         var frag = doc.createDocumentFragment(), n;
4165         while ((n = container.firstChild)) {
4166             frag.appendChild(n);
4167         }
4168         return frag;
4169     };
4170 }
4171
4172 /**
4173  * @class Roo.DomHelper
4174  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
4175  * For more information see <a href="http://web.archive.org/web/20071221063734/http://www.jackslocum.com/blog/2006/10/06/domhelper-create-elements-using-dom-html-fragments-or-templates/">this blog post with examples</a>.
4176  * @singleton
4177  */
4178 Roo.DomHelper = function(){
4179     var tempTableEl = null;
4180     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
4181     var tableRe = /^table|tbody|tr|td$/i;
4182     var xmlns = {};
4183     // build as innerHTML where available
4184     /** @ignore */
4185     var createHtml = function(o){
4186         if(typeof o == 'string'){
4187             return o;
4188         }
4189         var b = "";
4190         if(!o.tag){
4191             o.tag = "div";
4192         }
4193         b += "<" + o.tag;
4194         for(var attr in o){
4195             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") { continue; }
4196             if(attr == "style"){
4197                 var s = o["style"];
4198                 if(typeof s == "function"){
4199                     s = s.call();
4200                 }
4201                 if(typeof s == "string"){
4202                     b += ' style="' + s + '"';
4203                 }else if(typeof s == "object"){
4204                     b += ' style="';
4205                     for(var key in s){
4206                         if(typeof s[key] != "function"){
4207                             b += key + ":" + s[key] + ";";
4208                         }
4209                     }
4210                     b += '"';
4211                 }
4212             }else{
4213                 if(attr == "cls"){
4214                     b += ' class="' + o["cls"] + '"';
4215                 }else if(attr == "htmlFor"){
4216                     b += ' for="' + o["htmlFor"] + '"';
4217                 }else{
4218                     b += " " + attr + '="' + o[attr] + '"';
4219                 }
4220             }
4221         }
4222         if(emptyTags.test(o.tag)){
4223             b += "/>";
4224         }else{
4225             b += ">";
4226             var cn = o.children || o.cn;
4227             if(cn){
4228                 //http://bugs.kde.org/show_bug.cgi?id=71506
4229                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4230                     for(var i = 0, len = cn.length; i < len; i++) {
4231                         b += createHtml(cn[i], b);
4232                     }
4233                 }else{
4234                     b += createHtml(cn, b);
4235                 }
4236             }
4237             if(o.html){
4238                 b += o.html;
4239             }
4240             b += "</" + o.tag + ">";
4241         }
4242         return b;
4243     };
4244
4245     // build as dom
4246     /** @ignore */
4247     var createDom = function(o, parentNode){
4248          
4249         // defininition craeted..
4250         var ns = false;
4251         if (o.ns && o.ns != 'html') {
4252                
4253             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
4254                 xmlns[o.ns] = o.xmlns;
4255                 ns = o.xmlns;
4256             }
4257             if (typeof(xmlns[o.ns]) == 'undefined') {
4258                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
4259             }
4260             ns = xmlns[o.ns];
4261         }
4262         
4263         
4264         if (typeof(o) == 'string') {
4265             return parentNode.appendChild(document.createTextNode(o));
4266         }
4267         o.tag = o.tag || div;
4268         if (o.ns && Roo.isIE) {
4269             ns = false;
4270             o.tag = o.ns + ':' + o.tag;
4271             
4272         }
4273         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
4274         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
4275         for(var attr in o){
4276             
4277             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
4278                     attr == "style" || typeof o[attr] == "function") { continue; }
4279                     
4280             if(attr=="cls" && Roo.isIE){
4281                 el.className = o["cls"];
4282             }else{
4283                 if(useSet) { el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);}
4284                 else { 
4285                     el[attr] = o[attr];
4286                 }
4287             }
4288         }
4289         Roo.DomHelper.applyStyles(el, o.style);
4290         var cn = o.children || o.cn;
4291         if(cn){
4292             //http://bugs.kde.org/show_bug.cgi?id=71506
4293              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4294                 for(var i = 0, len = cn.length; i < len; i++) {
4295                     createDom(cn[i], el);
4296                 }
4297             }else{
4298                 createDom(cn, el);
4299             }
4300         }
4301         if(o.html){
4302             el.innerHTML = o.html;
4303         }
4304         if(parentNode){
4305            parentNode.appendChild(el);
4306         }
4307         return el;
4308     };
4309
4310     var ieTable = function(depth, s, h, e){
4311         tempTableEl.innerHTML = [s, h, e].join('');
4312         var i = -1, el = tempTableEl;
4313         while(++i < depth){
4314             el = el.firstChild;
4315         }
4316         return el;
4317     };
4318
4319     // kill repeat to save bytes
4320     var ts = '<table>',
4321         te = '</table>',
4322         tbs = ts+'<tbody>',
4323         tbe = '</tbody>'+te,
4324         trs = tbs + '<tr>',
4325         tre = '</tr>'+tbe;
4326
4327     /**
4328      * @ignore
4329      * Nasty code for IE's broken table implementation
4330      */
4331     var insertIntoTable = function(tag, where, el, html){
4332         if(!tempTableEl){
4333             tempTableEl = document.createElement('div');
4334         }
4335         var node;
4336         var before = null;
4337         if(tag == 'td'){
4338             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
4339                 return;
4340             }
4341             if(where == 'beforebegin'){
4342                 before = el;
4343                 el = el.parentNode;
4344             } else{
4345                 before = el.nextSibling;
4346                 el = el.parentNode;
4347             }
4348             node = ieTable(4, trs, html, tre);
4349         }
4350         else if(tag == 'tr'){
4351             if(where == 'beforebegin'){
4352                 before = el;
4353                 el = el.parentNode;
4354                 node = ieTable(3, tbs, html, tbe);
4355             } else if(where == 'afterend'){
4356                 before = el.nextSibling;
4357                 el = el.parentNode;
4358                 node = ieTable(3, tbs, html, tbe);
4359             } else{ // INTO a TR
4360                 if(where == 'afterbegin'){
4361                     before = el.firstChild;
4362                 }
4363                 node = ieTable(4, trs, html, tre);
4364             }
4365         } else if(tag == 'tbody'){
4366             if(where == 'beforebegin'){
4367                 before = el;
4368                 el = el.parentNode;
4369                 node = ieTable(2, ts, html, te);
4370             } else if(where == 'afterend'){
4371                 before = el.nextSibling;
4372                 el = el.parentNode;
4373                 node = ieTable(2, ts, html, te);
4374             } else{
4375                 if(where == 'afterbegin'){
4376                     before = el.firstChild;
4377                 }
4378                 node = ieTable(3, tbs, html, tbe);
4379             }
4380         } else{ // TABLE
4381             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
4382                 return;
4383             }
4384             if(where == 'afterbegin'){
4385                 before = el.firstChild;
4386             }
4387             node = ieTable(2, ts, html, te);
4388         }
4389         el.insertBefore(node, before);
4390         return node;
4391     };
4392
4393     return {
4394     /** True to force the use of DOM instead of html fragments @type Boolean */
4395     useDom : false,
4396
4397     /**
4398      * Returns the markup for the passed Element(s) config
4399      * @param {Object} o The Dom object spec (and children)
4400      * @return {String}
4401      */
4402     markup : function(o){
4403         return createHtml(o);
4404     },
4405
4406     /**
4407      * Applies a style specification to an element
4408      * @param {String/HTMLElement} el The element to apply styles to
4409      * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
4410      * a function which returns such a specification.
4411      */
4412     applyStyles : function(el, styles){
4413         if(styles){
4414            el = Roo.fly(el);
4415            if(typeof styles == "string"){
4416                var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
4417                var matches;
4418                while ((matches = re.exec(styles)) != null){
4419                    el.setStyle(matches[1], matches[2]);
4420                }
4421            }else if (typeof styles == "object"){
4422                for (var style in styles){
4423                   el.setStyle(style, styles[style]);
4424                }
4425            }else if (typeof styles == "function"){
4426                 Roo.DomHelper.applyStyles(el, styles.call());
4427            }
4428         }
4429     },
4430
4431     /**
4432      * Inserts an HTML fragment into the Dom
4433      * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
4434      * @param {HTMLElement} el The context element
4435      * @param {String} html The HTML fragmenet
4436      * @return {HTMLElement} The new node
4437      */
4438     insertHtml : function(where, el, html){
4439         where = where.toLowerCase();
4440         if(el.insertAdjacentHTML){
4441             if(tableRe.test(el.tagName)){
4442                 var rs;
4443                 if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
4444                     return rs;
4445                 }
4446             }
4447             switch(where){
4448                 case "beforebegin":
4449                     el.insertAdjacentHTML('BeforeBegin', html);
4450                     return el.previousSibling;
4451                 case "afterbegin":
4452                     el.insertAdjacentHTML('AfterBegin', html);
4453                     return el.firstChild;
4454                 case "beforeend":
4455                     el.insertAdjacentHTML('BeforeEnd', html);
4456                     return el.lastChild;
4457                 case "afterend":
4458                     el.insertAdjacentHTML('AfterEnd', html);
4459                     return el.nextSibling;
4460             }
4461             throw 'Illegal insertion point -> "' + where + '"';
4462         }
4463         var range = el.ownerDocument.createRange();
4464         var frag;
4465         switch(where){
4466              case "beforebegin":
4467                 range.setStartBefore(el);
4468                 frag = range.createContextualFragment(html);
4469                 el.parentNode.insertBefore(frag, el);
4470                 return el.previousSibling;
4471              case "afterbegin":
4472                 if(el.firstChild){
4473                     range.setStartBefore(el.firstChild);
4474                     frag = range.createContextualFragment(html);
4475                     el.insertBefore(frag, el.firstChild);
4476                     return el.firstChild;
4477                 }else{
4478                     el.innerHTML = html;
4479                     return el.firstChild;
4480                 }
4481             case "beforeend":
4482                 if(el.lastChild){
4483                     range.setStartAfter(el.lastChild);
4484                     frag = range.createContextualFragment(html);
4485                     el.appendChild(frag);
4486                     return el.lastChild;
4487                 }else{
4488                     el.innerHTML = html;
4489                     return el.lastChild;
4490                 }
4491             case "afterend":
4492                 range.setStartAfter(el);
4493                 frag = range.createContextualFragment(html);
4494                 el.parentNode.insertBefore(frag, el.nextSibling);
4495                 return el.nextSibling;
4496             }
4497             throw 'Illegal insertion point -> "' + where + '"';
4498     },
4499
4500     /**
4501      * Creates new Dom element(s) and inserts them before el
4502      * @param {String/HTMLElement/Element} el The context element
4503      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4504      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4505      * @return {HTMLElement/Roo.Element} The new node
4506      */
4507     insertBefore : function(el, o, returnElement){
4508         return this.doInsert(el, o, returnElement, "beforeBegin");
4509     },
4510
4511     /**
4512      * Creates new Dom element(s) and inserts them after el
4513      * @param {String/HTMLElement/Element} el The context element
4514      * @param {Object} o The Dom object spec (and children)
4515      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4516      * @return {HTMLElement/Roo.Element} The new node
4517      */
4518     insertAfter : function(el, o, returnElement){
4519         return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
4520     },
4521
4522     /**
4523      * Creates new Dom element(s) and inserts them as the first child of el
4524      * @param {String/HTMLElement/Element} el The context element
4525      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4526      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4527      * @return {HTMLElement/Roo.Element} The new node
4528      */
4529     insertFirst : function(el, o, returnElement){
4530         return this.doInsert(el, o, returnElement, "afterBegin");
4531     },
4532
4533     // private
4534     doInsert : function(el, o, returnElement, pos, sibling){
4535         el = Roo.getDom(el);
4536         var newNode;
4537         if(this.useDom || o.ns){
4538             newNode = createDom(o, null);
4539             el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
4540         }else{
4541             var html = createHtml(o);
4542             newNode = this.insertHtml(pos, el, html);
4543         }
4544         return returnElement ? Roo.get(newNode, true) : newNode;
4545     },
4546
4547     /**
4548      * Creates new Dom element(s) and appends them to el
4549      * @param {String/HTMLElement/Element} el The context element
4550      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4551      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4552      * @return {HTMLElement/Roo.Element} The new node
4553      */
4554     append : function(el, o, returnElement){
4555         el = Roo.getDom(el);
4556         var newNode;
4557         if(this.useDom || o.ns){
4558             newNode = createDom(o, null);
4559             el.appendChild(newNode);
4560         }else{
4561             var html = createHtml(o);
4562             newNode = this.insertHtml("beforeEnd", el, html);
4563         }
4564         return returnElement ? Roo.get(newNode, true) : newNode;
4565     },
4566
4567     /**
4568      * Creates new Dom element(s) and overwrites the contents of el with them
4569      * @param {String/HTMLElement/Element} el The context element
4570      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4571      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4572      * @return {HTMLElement/Roo.Element} The new node
4573      */
4574     overwrite : function(el, o, returnElement){
4575         el = Roo.getDom(el);
4576         if (o.ns) {
4577           
4578             while (el.childNodes.length) {
4579                 el.removeChild(el.firstChild);
4580             }
4581             createDom(o, el);
4582         } else {
4583             el.innerHTML = createHtml(o);   
4584         }
4585         
4586         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4587     },
4588
4589     /**
4590      * Creates a new Roo.DomHelper.Template from the Dom object spec
4591      * @param {Object} o The Dom object spec (and children)
4592      * @return {Roo.DomHelper.Template} The new template
4593      */
4594     createTemplate : function(o){
4595         var html = createHtml(o);
4596         return new Roo.Template(html);
4597     }
4598     };
4599 }();
4600 /*
4601  * Based on:
4602  * Ext JS Library 1.1.1
4603  * Copyright(c) 2006-2007, Ext JS, LLC.
4604  *
4605  * Originally Released Under LGPL - original licence link has changed is not relivant.
4606  *
4607  * Fork - LGPL
4608  * <script type="text/javascript">
4609  */
4610  
4611 /**
4612 * @class Roo.Template
4613 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
4614 * For a list of available format functions, see {@link Roo.util.Format}.<br />
4615 * Usage:
4616 <pre><code>
4617 var t = new Roo.Template({
4618     html :  '&lt;div name="{id}"&gt;' + 
4619         '&lt;span class="{cls}"&gt;{name:trim} {someval:this.myformat}{value:ellipsis(10)}&lt;/span&gt;' +
4620         '&lt;/div&gt;',
4621     myformat: function (value, allValues) {
4622         return 'XX' + value;
4623     }
4624 });
4625 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
4626 </code></pre>
4627 * For more information see this blog post with examples:
4628 *  <a href="http://www.cnitblog.com/seeyeah/archive/2011/12/30/38728.html/">DomHelper
4629      - Create Elements using DOM, HTML fragments and Templates</a>. 
4630 * @constructor
4631 * @param {Object} cfg - Configuration object.
4632 */
4633 Roo.Template = function(cfg){
4634     // BC!
4635     if(cfg instanceof Array){
4636         cfg = cfg.join("");
4637     }else if(arguments.length > 1){
4638         cfg = Array.prototype.join.call(arguments, "");
4639     }
4640     
4641     
4642     if (typeof(cfg) == 'object') {
4643         Roo.apply(this,cfg)
4644     } else {
4645         // bc
4646         this.html = cfg;
4647     }
4648     if (this.url) {
4649         this.load();
4650     }
4651     
4652 };
4653 Roo.Template.prototype = {
4654     
4655     /**
4656      * @cfg {String} url  The Url to load the template from. beware if you are loading from a url, the data may not be ready if you use it instantly..
4657      *                    it should be fixed so that template is observable...
4658      */
4659     url : false,
4660     /**
4661      * @cfg {String} html  The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
4662      */
4663     html : '',
4664     /**
4665      * Returns an HTML fragment of this template with the specified values applied.
4666      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4667      * @return {String} The HTML fragment
4668      */
4669     applyTemplate : function(values){
4670         //Roo.log(["applyTemplate", values]);
4671         try {
4672            
4673             if(this.compiled){
4674                 return this.compiled(values);
4675             }
4676             var useF = this.disableFormats !== true;
4677             var fm = Roo.util.Format, tpl = this;
4678             var fn = function(m, name, format, args){
4679                 if(format && useF){
4680                     if(format.substr(0, 5) == "this."){
4681                         return tpl.call(format.substr(5), values[name], values);
4682                     }else{
4683                         if(args){
4684                             // quoted values are required for strings in compiled templates, 
4685                             // but for non compiled we need to strip them
4686                             // quoted reversed for jsmin
4687                             var re = /^\s*['"](.*)["']\s*$/;
4688                             args = args.split(',');
4689                             for(var i = 0, len = args.length; i < len; i++){
4690                                 args[i] = args[i].replace(re, "$1");
4691                             }
4692                             args = [values[name]].concat(args);
4693                         }else{
4694                             args = [values[name]];
4695                         }
4696                         return fm[format].apply(fm, args);
4697                     }
4698                 }else{
4699                     return values[name] !== undefined ? values[name] : "";
4700                 }
4701             };
4702             return this.html.replace(this.re, fn);
4703         } catch (e) {
4704             Roo.log(e);
4705             throw e;
4706         }
4707          
4708     },
4709     
4710     loading : false,
4711       
4712     load : function ()
4713     {
4714          
4715         if (this.loading) {
4716             return;
4717         }
4718         var _t = this;
4719         
4720         this.loading = true;
4721         this.compiled = false;
4722         
4723         var cx = new Roo.data.Connection();
4724         cx.request({
4725             url : this.url,
4726             method : 'GET',
4727             success : function (response) {
4728                 _t.loading = false;
4729                 _t.html = response.responseText;
4730                 _t.url = false;
4731                 _t.compile();
4732              },
4733             failure : function(response) {
4734                 Roo.log("Template failed to load from " + _t.url);
4735                 _t.loading = false;
4736             }
4737         });
4738     },
4739
4740     /**
4741      * Sets the HTML used as the template and optionally compiles it.
4742      * @param {String} html
4743      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
4744      * @return {Roo.Template} this
4745      */
4746     set : function(html, compile){
4747         this.html = html;
4748         this.compiled = null;
4749         if(compile){
4750             this.compile();
4751         }
4752         return this;
4753     },
4754     
4755     /**
4756      * True to disable format functions (defaults to false)
4757      * @type Boolean
4758      */
4759     disableFormats : false,
4760     
4761     /**
4762     * The regular expression used to match template variables 
4763     * @type RegExp
4764     * @property 
4765     */
4766     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
4767     
4768     /**
4769      * Compiles the template into an internal function, eliminating the RegEx overhead.
4770      * @return {Roo.Template} this
4771      */
4772     compile : function(){
4773         var fm = Roo.util.Format;
4774         var useF = this.disableFormats !== true;
4775         var sep = Roo.isGecko ? "+" : ",";
4776         var fn = function(m, name, format, args){
4777             if(format && useF){
4778                 args = args ? ',' + args : "";
4779                 if(format.substr(0, 5) != "this."){
4780                     format = "fm." + format + '(';
4781                 }else{
4782                     format = 'this.call("'+ format.substr(5) + '", ';
4783                     args = ", values";
4784                 }
4785             }else{
4786                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
4787             }
4788             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
4789         };
4790         var body;
4791         // branched to use + in gecko and [].join() in others
4792         if(Roo.isGecko){
4793             body = "this.compiled = function(values){ return '" +
4794                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
4795                     "';};";
4796         }else{
4797             body = ["this.compiled = function(values){ return ['"];
4798             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
4799             body.push("'].join('');};");
4800             body = body.join('');
4801         }
4802         /**
4803          * eval:var:values
4804          * eval:var:fm
4805          */
4806         eval(body);
4807         return this;
4808     },
4809     
4810     // private function used to call members
4811     call : function(fnName, value, allValues){
4812         return this[fnName](value, allValues);
4813     },
4814     
4815     /**
4816      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
4817      * @param {String/HTMLElement/Roo.Element} el The context element
4818      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4819      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4820      * @return {HTMLElement/Roo.Element} The new node or Element
4821      */
4822     insertFirst: function(el, values, returnElement){
4823         return this.doInsert('afterBegin', el, values, returnElement);
4824     },
4825
4826     /**
4827      * Applies the supplied values to the template and inserts the new node(s) before el.
4828      * @param {String/HTMLElement/Roo.Element} el The context element
4829      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4830      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4831      * @return {HTMLElement/Roo.Element} The new node or Element
4832      */
4833     insertBefore: function(el, values, returnElement){
4834         return this.doInsert('beforeBegin', el, values, returnElement);
4835     },
4836
4837     /**
4838      * Applies the supplied values to the template and inserts the new node(s) after el.
4839      * @param {String/HTMLElement/Roo.Element} el The context element
4840      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4841      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4842      * @return {HTMLElement/Roo.Element} The new node or Element
4843      */
4844     insertAfter : function(el, values, returnElement){
4845         return this.doInsert('afterEnd', el, values, returnElement);
4846     },
4847     
4848     /**
4849      * Applies the supplied values to the template and appends the new node(s) to el.
4850      * @param {String/HTMLElement/Roo.Element} el The context element
4851      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4852      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4853      * @return {HTMLElement/Roo.Element} The new node or Element
4854      */
4855     append : function(el, values, returnElement){
4856         return this.doInsert('beforeEnd', el, values, returnElement);
4857     },
4858
4859     doInsert : function(where, el, values, returnEl){
4860         el = Roo.getDom(el);
4861         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
4862         return returnEl ? Roo.get(newNode, true) : newNode;
4863     },
4864
4865     /**
4866      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
4867      * @param {String/HTMLElement/Roo.Element} el The context element
4868      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4869      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4870      * @return {HTMLElement/Roo.Element} The new node or Element
4871      */
4872     overwrite : function(el, values, returnElement){
4873         el = Roo.getDom(el);
4874         el.innerHTML = this.applyTemplate(values);
4875         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4876     }
4877 };
4878 /**
4879  * Alias for {@link #applyTemplate}
4880  * @method
4881  */
4882 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
4883
4884 // backwards compat
4885 Roo.DomHelper.Template = Roo.Template;
4886
4887 /**
4888  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
4889  * @param {String/HTMLElement} el A DOM element or its id
4890  * @returns {Roo.Template} The created template
4891  * @static
4892  */
4893 Roo.Template.from = function(el){
4894     el = Roo.getDom(el);
4895     return new Roo.Template(el.value || el.innerHTML);
4896 };/*
4897  * Based on:
4898  * Ext JS Library 1.1.1
4899  * Copyright(c) 2006-2007, Ext JS, LLC.
4900  *
4901  * Originally Released Under LGPL - original licence link has changed is not relivant.
4902  *
4903  * Fork - LGPL
4904  * <script type="text/javascript">
4905  */
4906  
4907
4908 /*
4909  * This is code is also distributed under MIT license for use
4910  * with jQuery and prototype JavaScript libraries.
4911  */
4912 /**
4913  * @class Roo.DomQuery
4914 Provides high performance selector/xpath processing by compiling queries into reusable functions. New pseudo classes and matchers can be plugged. It works on HTML and XML documents (if a content node is passed in).
4915 <p>
4916 DomQuery supports most of the <a href="http://www.w3.org/TR/2005/WD-css3-selectors-20051215/">CSS3 selectors spec</a>, along with some custom selectors and basic XPath.</p>
4917
4918 <p>
4919 All selectors, attribute filters and pseudos below can be combined infinitely in any order. For example "div.foo:nth-child(odd)[@foo=bar].bar:first" would be a perfectly valid selector. Node filters are processed in the order in which they appear, which allows you to optimize your queries for your document structure.
4920 </p>
4921 <h4>Element Selectors:</h4>
4922 <ul class="list">
4923     <li> <b>*</b> any element</li>
4924     <li> <b>E</b> an element with the tag E</li>
4925     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
4926     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
4927     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
4928     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
4929 </ul>
4930 <h4>Attribute Selectors:</h4>
4931 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
4932 <ul class="list">
4933     <li> <b>E[foo]</b> has an attribute "foo"</li>
4934     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
4935     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
4936     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
4937     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
4938     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
4939     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
4940 </ul>
4941 <h4>Pseudo Classes:</h4>
4942 <ul class="list">
4943     <li> <b>E:first-child</b> E is the first child of its parent</li>
4944     <li> <b>E:last-child</b> E is the last child of its parent</li>
4945     <li> <b>E:nth-child(<i>n</i>)</b> E is the <i>n</i>th child of its parent (1 based as per the spec)</li>
4946     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
4947     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
4948     <li> <b>E:only-child</b> E is the only child of its parent</li>
4949     <li> <b>E:checked</b> E is an element that is has a checked attribute that is true (e.g. a radio or checkbox) </li>
4950     <li> <b>E:first</b> the first E in the resultset</li>
4951     <li> <b>E:last</b> the last E in the resultset</li>
4952     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
4953     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
4954     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
4955     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
4956     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
4957     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
4958     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
4959     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
4960     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
4961 </ul>
4962 <h4>CSS Value Selectors:</h4>
4963 <ul class="list">
4964     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
4965     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
4966     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
4967     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
4968     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
4969     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
4970 </ul>
4971  * @singleton
4972  */
4973 Roo.DomQuery = function(){
4974     var cache = {}, simpleCache = {}, valueCache = {};
4975     var nonSpace = /\S/;
4976     var trimRe = /^\s+|\s+$/g;
4977     var tplRe = /\{(\d+)\}/g;
4978     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
4979     var tagTokenRe = /^(#)?([\w-\*]+)/;
4980     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
4981
4982     function child(p, index){
4983         var i = 0;
4984         var n = p.firstChild;
4985         while(n){
4986             if(n.nodeType == 1){
4987                if(++i == index){
4988                    return n;
4989                }
4990             }
4991             n = n.nextSibling;
4992         }
4993         return null;
4994     };
4995
4996     function next(n){
4997         while((n = n.nextSibling) && n.nodeType != 1);
4998         return n;
4999     };
5000
5001     function prev(n){
5002         while((n = n.previousSibling) && n.nodeType != 1);
5003         return n;
5004     };
5005
5006     function children(d){
5007         var n = d.firstChild, ni = -1;
5008             while(n){
5009                 var nx = n.nextSibling;
5010                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
5011                     d.removeChild(n);
5012                 }else{
5013                     n.nodeIndex = ++ni;
5014                 }
5015                 n = nx;
5016             }
5017             return this;
5018         };
5019
5020     function byClassName(c, a, v){
5021         if(!v){
5022             return c;
5023         }
5024         var r = [], ri = -1, cn;
5025         for(var i = 0, ci; ci = c[i]; i++){
5026             if((' '+ci.className+' ').indexOf(v) != -1){
5027                 r[++ri] = ci;
5028             }
5029         }
5030         return r;
5031     };
5032
5033     function attrValue(n, attr){
5034         if(!n.tagName && typeof n.length != "undefined"){
5035             n = n[0];
5036         }
5037         if(!n){
5038             return null;
5039         }
5040         if(attr == "for"){
5041             return n.htmlFor;
5042         }
5043         if(attr == "class" || attr == "className"){
5044             return n.className;
5045         }
5046         return n.getAttribute(attr) || n[attr];
5047
5048     };
5049
5050     function getNodes(ns, mode, tagName){
5051         var result = [], ri = -1, cs;
5052         if(!ns){
5053             return result;
5054         }
5055         tagName = tagName || "*";
5056         if(typeof ns.getElementsByTagName != "undefined"){
5057             ns = [ns];
5058         }
5059         if(!mode){
5060             for(var i = 0, ni; ni = ns[i]; i++){
5061                 cs = ni.getElementsByTagName(tagName);
5062                 for(var j = 0, ci; ci = cs[j]; j++){
5063                     result[++ri] = ci;
5064                 }
5065             }
5066         }else if(mode == "/" || mode == ">"){
5067             var utag = tagName.toUpperCase();
5068             for(var i = 0, ni, cn; ni = ns[i]; i++){
5069                 cn = ni.children || ni.childNodes;
5070                 for(var j = 0, cj; cj = cn[j]; j++){
5071                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
5072                         result[++ri] = cj;
5073                     }
5074                 }
5075             }
5076         }else if(mode == "+"){
5077             var utag = tagName.toUpperCase();
5078             for(var i = 0, n; n = ns[i]; i++){
5079                 while((n = n.nextSibling) && n.nodeType != 1);
5080                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
5081                     result[++ri] = n;
5082                 }
5083             }
5084         }else if(mode == "~"){
5085             for(var i = 0, n; n = ns[i]; i++){
5086                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
5087                 if(n){
5088                     result[++ri] = n;
5089                 }
5090             }
5091         }
5092         return result;
5093     };
5094
5095     function concat(a, b){
5096         if(b.slice){
5097             return a.concat(b);
5098         }
5099         for(var i = 0, l = b.length; i < l; i++){
5100             a[a.length] = b[i];
5101         }
5102         return a;
5103     }
5104
5105     function byTag(cs, tagName){
5106         if(cs.tagName || cs == document){
5107             cs = [cs];
5108         }
5109         if(!tagName){
5110             return cs;
5111         }
5112         var r = [], ri = -1;
5113         tagName = tagName.toLowerCase();
5114         for(var i = 0, ci; ci = cs[i]; i++){
5115             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
5116                 r[++ri] = ci;
5117             }
5118         }
5119         return r;
5120     };
5121
5122     function byId(cs, attr, id){
5123         if(cs.tagName || cs == document){
5124             cs = [cs];
5125         }
5126         if(!id){
5127             return cs;
5128         }
5129         var r = [], ri = -1;
5130         for(var i = 0,ci; ci = cs[i]; i++){
5131             if(ci && ci.id == id){
5132                 r[++ri] = ci;
5133                 return r;
5134             }
5135         }
5136         return r;
5137     };
5138
5139     function byAttribute(cs, attr, value, op, custom){
5140         var r = [], ri = -1, st = custom=="{";
5141         var f = Roo.DomQuery.operators[op];
5142         for(var i = 0, ci; ci = cs[i]; i++){
5143             var a;
5144             if(st){
5145                 a = Roo.DomQuery.getStyle(ci, attr);
5146             }
5147             else if(attr == "class" || attr == "className"){
5148                 a = ci.className;
5149             }else if(attr == "for"){
5150                 a = ci.htmlFor;
5151             }else if(attr == "href"){
5152                 a = ci.getAttribute("href", 2);
5153             }else{
5154                 a = ci.getAttribute(attr);
5155             }
5156             if((f && f(a, value)) || (!f && a)){
5157                 r[++ri] = ci;
5158             }
5159         }
5160         return r;
5161     };
5162
5163     function byPseudo(cs, name, value){
5164         return Roo.DomQuery.pseudos[name](cs, value);
5165     };
5166
5167     // This is for IE MSXML which does not support expandos.
5168     // IE runs the same speed using setAttribute, however FF slows way down
5169     // and Safari completely fails so they need to continue to use expandos.
5170     var isIE = window.ActiveXObject ? true : false;
5171
5172     // this eval is stop the compressor from
5173     // renaming the variable to something shorter
5174     
5175     /** eval:var:batch */
5176     var batch = 30803; 
5177
5178     var key = 30803;
5179
5180     function nodupIEXml(cs){
5181         var d = ++key;
5182         cs[0].setAttribute("_nodup", d);
5183         var r = [cs[0]];
5184         for(var i = 1, len = cs.length; i < len; i++){
5185             var c = cs[i];
5186             if(!c.getAttribute("_nodup") != d){
5187                 c.setAttribute("_nodup", d);
5188                 r[r.length] = c;
5189             }
5190         }
5191         for(var i = 0, len = cs.length; i < len; i++){
5192             cs[i].removeAttribute("_nodup");
5193         }
5194         return r;
5195     }
5196
5197     function nodup(cs){
5198         if(!cs){
5199             return [];
5200         }
5201         var len = cs.length, c, i, r = cs, cj, ri = -1;
5202         if(!len || typeof cs.nodeType != "undefined" || len == 1){
5203             return cs;
5204         }
5205         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
5206             return nodupIEXml(cs);
5207         }
5208         var d = ++key;
5209         cs[0]._nodup = d;
5210         for(i = 1; c = cs[i]; i++){
5211             if(c._nodup != d){
5212                 c._nodup = d;
5213             }else{
5214                 r = [];
5215                 for(var j = 0; j < i; j++){
5216                     r[++ri] = cs[j];
5217                 }
5218                 for(j = i+1; cj = cs[j]; j++){
5219                     if(cj._nodup != d){
5220                         cj._nodup = d;
5221                         r[++ri] = cj;
5222                     }
5223                 }
5224                 return r;
5225             }
5226         }
5227         return r;
5228     }
5229
5230     function quickDiffIEXml(c1, c2){
5231         var d = ++key;
5232         for(var i = 0, len = c1.length; i < len; i++){
5233             c1[i].setAttribute("_qdiff", d);
5234         }
5235         var r = [];
5236         for(var i = 0, len = c2.length; i < len; i++){
5237             if(c2[i].getAttribute("_qdiff") != d){
5238                 r[r.length] = c2[i];
5239             }
5240         }
5241         for(var i = 0, len = c1.length; i < len; i++){
5242            c1[i].removeAttribute("_qdiff");
5243         }
5244         return r;
5245     }
5246
5247     function quickDiff(c1, c2){
5248         var len1 = c1.length;
5249         if(!len1){
5250             return c2;
5251         }
5252         if(isIE && c1[0].selectSingleNode){
5253             return quickDiffIEXml(c1, c2);
5254         }
5255         var d = ++key;
5256         for(var i = 0; i < len1; i++){
5257             c1[i]._qdiff = d;
5258         }
5259         var r = [];
5260         for(var i = 0, len = c2.length; i < len; i++){
5261             if(c2[i]._qdiff != d){
5262                 r[r.length] = c2[i];
5263             }
5264         }
5265         return r;
5266     }
5267
5268     function quickId(ns, mode, root, id){
5269         if(ns == root){
5270            var d = root.ownerDocument || root;
5271            return d.getElementById(id);
5272         }
5273         ns = getNodes(ns, mode, "*");
5274         return byId(ns, null, id);
5275     }
5276
5277     return {
5278         getStyle : function(el, name){
5279             return Roo.fly(el).getStyle(name);
5280         },
5281         /**
5282          * Compiles a selector/xpath query into a reusable function. The returned function
5283          * takes one parameter "root" (optional), which is the context node from where the query should start.
5284          * @param {String} selector The selector/xpath query
5285          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
5286          * @return {Function}
5287          */
5288         compile : function(path, type){
5289             type = type || "select";
5290             
5291             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
5292             var q = path, mode, lq;
5293             var tk = Roo.DomQuery.matchers;
5294             var tklen = tk.length;
5295             var mm;
5296
5297             // accept leading mode switch
5298             var lmode = q.match(modeRe);
5299             if(lmode && lmode[1]){
5300                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
5301                 q = q.replace(lmode[1], "");
5302             }
5303             // strip leading slashes
5304             while(path.substr(0, 1)=="/"){
5305                 path = path.substr(1);
5306             }
5307
5308             while(q && lq != q){
5309                 lq = q;
5310                 var tm = q.match(tagTokenRe);
5311                 if(type == "select"){
5312                     if(tm){
5313                         if(tm[1] == "#"){
5314                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
5315                         }else{
5316                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
5317                         }
5318                         q = q.replace(tm[0], "");
5319                     }else if(q.substr(0, 1) != '@'){
5320                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
5321                     }
5322                 }else{
5323                     if(tm){
5324                         if(tm[1] == "#"){
5325                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
5326                         }else{
5327                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
5328                         }
5329                         q = q.replace(tm[0], "");
5330                     }
5331                 }
5332                 while(!(mm = q.match(modeRe))){
5333                     var matched = false;
5334                     for(var j = 0; j < tklen; j++){
5335                         var t = tk[j];
5336                         var m = q.match(t.re);
5337                         if(m){
5338                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
5339                                                     return m[i];
5340                                                 });
5341                             q = q.replace(m[0], "");
5342                             matched = true;
5343                             break;
5344                         }
5345                     }
5346                     // prevent infinite loop on bad selector
5347                     if(!matched){
5348                         throw 'Error parsing selector, parsing failed at "' + q + '"';
5349                     }
5350                 }
5351                 if(mm[1]){
5352                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
5353                     q = q.replace(mm[1], "");
5354                 }
5355             }
5356             fn[fn.length] = "return nodup(n);\n}";
5357             
5358              /** 
5359               * list of variables that need from compression as they are used by eval.
5360              *  eval:var:batch 
5361              *  eval:var:nodup
5362              *  eval:var:byTag
5363              *  eval:var:ById
5364              *  eval:var:getNodes
5365              *  eval:var:quickId
5366              *  eval:var:mode
5367              *  eval:var:root
5368              *  eval:var:n
5369              *  eval:var:byClassName
5370              *  eval:var:byPseudo
5371              *  eval:var:byAttribute
5372              *  eval:var:attrValue
5373              * 
5374              **/ 
5375             eval(fn.join(""));
5376             return f;
5377         },
5378
5379         /**
5380          * Selects a group of elements.
5381          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
5382          * @param {Node} root (optional) The start of the query (defaults to document).
5383          * @return {Array}
5384          */
5385         select : function(path, root, type){
5386             if(!root || root == document){
5387                 root = document;
5388             }
5389             if(typeof root == "string"){
5390                 root = document.getElementById(root);
5391             }
5392             var paths = path.split(",");
5393             var results = [];
5394             for(var i = 0, len = paths.length; i < len; i++){
5395                 var p = paths[i].replace(trimRe, "");
5396                 if(!cache[p]){
5397                     cache[p] = Roo.DomQuery.compile(p);
5398                     if(!cache[p]){
5399                         throw p + " is not a valid selector";
5400                     }
5401                 }
5402                 var result = cache[p](root);
5403                 if(result && result != document){
5404                     results = results.concat(result);
5405                 }
5406             }
5407             if(paths.length > 1){
5408                 return nodup(results);
5409             }
5410             return results;
5411         },
5412
5413         /**
5414          * Selects a single element.
5415          * @param {String} selector The selector/xpath query
5416          * @param {Node} root (optional) The start of the query (defaults to document).
5417          * @return {Element}
5418          */
5419         selectNode : function(path, root){
5420             return Roo.DomQuery.select(path, root)[0];
5421         },
5422
5423         /**
5424          * Selects the value of a node, optionally replacing null with the defaultValue.
5425          * @param {String} selector The selector/xpath query
5426          * @param {Node} root (optional) The start of the query (defaults to document).
5427          * @param {String} defaultValue
5428          */
5429         selectValue : function(path, root, defaultValue){
5430             path = path.replace(trimRe, "");
5431             if(!valueCache[path]){
5432                 valueCache[path] = Roo.DomQuery.compile(path, "select");
5433             }
5434             var n = valueCache[path](root);
5435             n = n[0] ? n[0] : n;
5436             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
5437             return ((v === null||v === undefined||v==='') ? defaultValue : v);
5438         },
5439
5440         /**
5441          * Selects the value of a node, parsing integers and floats.
5442          * @param {String} selector The selector/xpath query
5443          * @param {Node} root (optional) The start of the query (defaults to document).
5444          * @param {Number} defaultValue
5445          * @return {Number}
5446          */
5447         selectNumber : function(path, root, defaultValue){
5448             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
5449             return parseFloat(v);
5450         },
5451
5452         /**
5453          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
5454          * @param {String/HTMLElement/Array} el An element id, element or array of elements
5455          * @param {String} selector The simple selector to test
5456          * @return {Boolean}
5457          */
5458         is : function(el, ss){
5459             if(typeof el == "string"){
5460                 el = document.getElementById(el);
5461             }
5462             var isArray = (el instanceof Array);
5463             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
5464             return isArray ? (result.length == el.length) : (result.length > 0);
5465         },
5466
5467         /**
5468          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
5469          * @param {Array} el An array of elements to filter
5470          * @param {String} selector The simple selector to test
5471          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
5472          * the selector instead of the ones that match
5473          * @return {Array}
5474          */
5475         filter : function(els, ss, nonMatches){
5476             ss = ss.replace(trimRe, "");
5477             if(!simpleCache[ss]){
5478                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
5479             }
5480             var result = simpleCache[ss](els);
5481             return nonMatches ? quickDiff(result, els) : result;
5482         },
5483
5484         /**
5485          * Collection of matching regular expressions and code snippets.
5486          */
5487         matchers : [{
5488                 re: /^\.([\w-]+)/,
5489                 select: 'n = byClassName(n, null, " {1} ");'
5490             }, {
5491                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
5492                 select: 'n = byPseudo(n, "{1}", "{2}");'
5493             },{
5494                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
5495                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
5496             }, {
5497                 re: /^#([\w-]+)/,
5498                 select: 'n = byId(n, null, "{1}");'
5499             },{
5500                 re: /^@([\w-]+)/,
5501                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
5502             }
5503         ],
5504
5505         /**
5506          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
5507          * New operators can be added as long as the match the format <i>c</i>= where <i>c</i> is any character other than space, &gt; &lt;.
5508          */
5509         operators : {
5510             "=" : function(a, v){
5511                 return a == v;
5512             },
5513             "!=" : function(a, v){
5514                 return a != v;
5515             },
5516             "^=" : function(a, v){
5517                 return a && a.substr(0, v.length) == v;
5518             },
5519             "$=" : function(a, v){
5520                 return a && a.substr(a.length-v.length) == v;
5521             },
5522             "*=" : function(a, v){
5523                 return a && a.indexOf(v) !== -1;
5524             },
5525             "%=" : function(a, v){
5526                 return (a % v) == 0;
5527             },
5528             "|=" : function(a, v){
5529                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
5530             },
5531             "~=" : function(a, v){
5532                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
5533             }
5534         },
5535
5536         /**
5537          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
5538          * and the argument (if any) supplied in the selector.
5539          */
5540         pseudos : {
5541             "first-child" : function(c){
5542                 var r = [], ri = -1, n;
5543                 for(var i = 0, ci; ci = n = c[i]; i++){
5544                     while((n = n.previousSibling) && n.nodeType != 1);
5545                     if(!n){
5546                         r[++ri] = ci;
5547                     }
5548                 }
5549                 return r;
5550             },
5551
5552             "last-child" : function(c){
5553                 var r = [], ri = -1, n;
5554                 for(var i = 0, ci; ci = n = c[i]; i++){
5555                     while((n = n.nextSibling) && n.nodeType != 1);
5556                     if(!n){
5557                         r[++ri] = ci;
5558                     }
5559                 }
5560                 return r;
5561             },
5562
5563             "nth-child" : function(c, a) {
5564                 var r = [], ri = -1;
5565                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
5566                 var f = (m[1] || 1) - 0, l = m[2] - 0;
5567                 for(var i = 0, n; n = c[i]; i++){
5568                     var pn = n.parentNode;
5569                     if (batch != pn._batch) {
5570                         var j = 0;
5571                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
5572                             if(cn.nodeType == 1){
5573                                cn.nodeIndex = ++j;
5574                             }
5575                         }
5576                         pn._batch = batch;
5577                     }
5578                     if (f == 1) {
5579                         if (l == 0 || n.nodeIndex == l){
5580                             r[++ri] = n;
5581                         }
5582                     } else if ((n.nodeIndex + l) % f == 0){
5583                         r[++ri] = n;
5584                     }
5585                 }
5586
5587                 return r;
5588             },
5589
5590             "only-child" : function(c){
5591                 var r = [], ri = -1;;
5592                 for(var i = 0, ci; ci = c[i]; i++){
5593                     if(!prev(ci) && !next(ci)){
5594                         r[++ri] = ci;
5595                     }
5596                 }
5597                 return r;
5598             },
5599
5600             "empty" : function(c){
5601                 var r = [], ri = -1;
5602                 for(var i = 0, ci; ci = c[i]; i++){
5603                     var cns = ci.childNodes, j = 0, cn, empty = true;
5604                     while(cn = cns[j]){
5605                         ++j;
5606                         if(cn.nodeType == 1 || cn.nodeType == 3){
5607                             empty = false;
5608                             break;
5609                         }
5610                     }
5611                     if(empty){
5612                         r[++ri] = ci;
5613                     }
5614                 }
5615                 return r;
5616             },
5617
5618             "contains" : function(c, v){
5619                 var r = [], ri = -1;
5620                 for(var i = 0, ci; ci = c[i]; i++){
5621                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
5622                         r[++ri] = ci;
5623                     }
5624                 }
5625                 return r;
5626             },
5627
5628             "nodeValue" : function(c, v){
5629                 var r = [], ri = -1;
5630                 for(var i = 0, ci; ci = c[i]; i++){
5631                     if(ci.firstChild && ci.firstChild.nodeValue == v){
5632                         r[++ri] = ci;
5633                     }
5634                 }
5635                 return r;
5636             },
5637
5638             "checked" : function(c){
5639                 var r = [], ri = -1;
5640                 for(var i = 0, ci; ci = c[i]; i++){
5641                     if(ci.checked == true){
5642                         r[++ri] = ci;
5643                     }
5644                 }
5645                 return r;
5646             },
5647
5648             "not" : function(c, ss){
5649                 return Roo.DomQuery.filter(c, ss, true);
5650             },
5651
5652             "odd" : function(c){
5653                 return this["nth-child"](c, "odd");
5654             },
5655
5656             "even" : function(c){
5657                 return this["nth-child"](c, "even");
5658             },
5659
5660             "nth" : function(c, a){
5661                 return c[a-1] || [];
5662             },
5663
5664             "first" : function(c){
5665                 return c[0] || [];
5666             },
5667
5668             "last" : function(c){
5669                 return c[c.length-1] || [];
5670             },
5671
5672             "has" : function(c, ss){
5673                 var s = Roo.DomQuery.select;
5674                 var r = [], ri = -1;
5675                 for(var i = 0, ci; ci = c[i]; i++){
5676                     if(s(ss, ci).length > 0){
5677                         r[++ri] = ci;
5678                     }
5679                 }
5680                 return r;
5681             },
5682
5683             "next" : function(c, ss){
5684                 var is = Roo.DomQuery.is;
5685                 var r = [], ri = -1;
5686                 for(var i = 0, ci; ci = c[i]; i++){
5687                     var n = next(ci);
5688                     if(n && is(n, ss)){
5689                         r[++ri] = ci;
5690                     }
5691                 }
5692                 return r;
5693             },
5694
5695             "prev" : function(c, ss){
5696                 var is = Roo.DomQuery.is;
5697                 var r = [], ri = -1;
5698                 for(var i = 0, ci; ci = c[i]; i++){
5699                     var n = prev(ci);
5700                     if(n && is(n, ss)){
5701                         r[++ri] = ci;
5702                     }
5703                 }
5704                 return r;
5705             }
5706         }
5707     };
5708 }();
5709
5710 /**
5711  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
5712  * @param {String} path The selector/xpath query
5713  * @param {Node} root (optional) The start of the query (defaults to document).
5714  * @return {Array}
5715  * @member Roo
5716  * @method query
5717  */
5718 Roo.query = Roo.DomQuery.select;
5719 /*
5720  * Based on:
5721  * Ext JS Library 1.1.1
5722  * Copyright(c) 2006-2007, Ext JS, LLC.
5723  *
5724  * Originally Released Under LGPL - original licence link has changed is not relivant.
5725  *
5726  * Fork - LGPL
5727  * <script type="text/javascript">
5728  */
5729
5730 /**
5731  * @class Roo.util.Observable
5732  * Base class that provides a common interface for publishing events. Subclasses are expected to
5733  * to have a property "events" with all the events defined.<br>
5734  * For example:
5735  * <pre><code>
5736  Employee = function(name){
5737     this.name = name;
5738     this.addEvents({
5739         "fired" : true,
5740         "quit" : true
5741     });
5742  }
5743  Roo.extend(Employee, Roo.util.Observable);
5744 </code></pre>
5745  * @param {Object} config properties to use (incuding events / listeners)
5746  */
5747
5748 Roo.util.Observable = function(cfg){
5749     
5750     cfg = cfg|| {};
5751     this.addEvents(cfg.events || {});
5752     if (cfg.events) {
5753         delete cfg.events; // make sure
5754     }
5755      
5756     Roo.apply(this, cfg);
5757     
5758     if(this.listeners){
5759         this.on(this.listeners);
5760         delete this.listeners;
5761     }
5762 };
5763 Roo.util.Observable.prototype = {
5764     /** 
5765  * @cfg {Object} listeners  list of events and functions to call for this object, 
5766  * For example :
5767  * <pre><code>
5768     listeners :  { 
5769        'click' : function(e) {
5770            ..... 
5771         } ,
5772         .... 
5773     } 
5774   </code></pre>
5775  */
5776     
5777     
5778     /**
5779      * Fires the specified event with the passed parameters (minus the event name).
5780      * @param {String} eventName
5781      * @param {Object...} args Variable number of parameters are passed to handlers
5782      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
5783      */
5784     fireEvent : function(){
5785         var ce = this.events[arguments[0].toLowerCase()];
5786         if(typeof ce == "object"){
5787             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
5788         }else{
5789             return true;
5790         }
5791     },
5792
5793     // private
5794     filterOptRe : /^(?:scope|delay|buffer|single)$/,
5795
5796     /**
5797      * Appends an event handler to this component
5798      * @param {String}   eventName The type of event to listen for
5799      * @param {Function} handler The method the event invokes
5800      * @param {Object}   scope (optional) The scope in which to execute the handler
5801      * function. The handler function's "this" context.
5802      * @param {Object}   options (optional) An object containing handler configuration
5803      * properties. This may contain any of the following properties:<ul>
5804      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
5805      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
5806      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
5807      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
5808      * by the specified number of milliseconds. If the event fires again within that time, the original
5809      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
5810      * </ul><br>
5811      * <p>
5812      * <b>Combining Options</b><br>
5813      * Using the options argument, it is possible to combine different types of listeners:<br>
5814      * <br>
5815      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
5816                 <pre><code>
5817                 el.on('click', this.onClick, this, {
5818                         single: true,
5819                 delay: 100,
5820                 forumId: 4
5821                 });
5822                 </code></pre>
5823      * <p>
5824      * <b>Attaching multiple handlers in 1 call</b><br>
5825      * The method also allows for a single argument to be passed which is a config object containing properties
5826      * which specify multiple handlers.
5827      * <pre><code>
5828                 el.on({
5829                         'click': {
5830                         fn: this.onClick,
5831                         scope: this,
5832                         delay: 100
5833                 }, 
5834                 'mouseover': {
5835                         fn: this.onMouseOver,
5836                         scope: this
5837                 },
5838                 'mouseout': {
5839                         fn: this.onMouseOut,
5840                         scope: this
5841                 }
5842                 });
5843                 </code></pre>
5844      * <p>
5845      * Or a shorthand syntax which passes the same scope object to all handlers:
5846         <pre><code>
5847                 el.on({
5848                         'click': this.onClick,
5849                 'mouseover': this.onMouseOver,
5850                 'mouseout': this.onMouseOut,
5851                 scope: this
5852                 });
5853                 </code></pre>
5854      */
5855     addListener : function(eventName, fn, scope, o){
5856         if(typeof eventName == "object"){
5857             o = eventName;
5858             for(var e in o){
5859                 if(this.filterOptRe.test(e)){
5860                     continue;
5861                 }
5862                 if(typeof o[e] == "function"){
5863                     // shared options
5864                     this.addListener(e, o[e], o.scope,  o);
5865                 }else{
5866                     // individual options
5867                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
5868                 }
5869             }
5870             return;
5871         }
5872         o = (!o || typeof o == "boolean") ? {} : o;
5873         eventName = eventName.toLowerCase();
5874         var ce = this.events[eventName] || true;
5875         if(typeof ce == "boolean"){
5876             ce = new Roo.util.Event(this, eventName);
5877             this.events[eventName] = ce;
5878         }
5879         ce.addListener(fn, scope, o);
5880     },
5881
5882     /**
5883      * Removes a listener
5884      * @param {String}   eventName     The type of event to listen for
5885      * @param {Function} handler        The handler to remove
5886      * @param {Object}   scope  (optional) The scope (this object) for the handler
5887      */
5888     removeListener : function(eventName, fn, scope){
5889         var ce = this.events[eventName.toLowerCase()];
5890         if(typeof ce == "object"){
5891             ce.removeListener(fn, scope);
5892         }
5893     },
5894
5895     /**
5896      * Removes all listeners for this object
5897      */
5898     purgeListeners : function(){
5899         for(var evt in this.events){
5900             if(typeof this.events[evt] == "object"){
5901                  this.events[evt].clearListeners();
5902             }
5903         }
5904     },
5905
5906     relayEvents : function(o, events){
5907         var createHandler = function(ename){
5908             return function(){
5909                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
5910             };
5911         };
5912         for(var i = 0, len = events.length; i < len; i++){
5913             var ename = events[i];
5914             if(!this.events[ename]){ this.events[ename] = true; };
5915             o.on(ename, createHandler(ename), this);
5916         }
5917     },
5918
5919     /**
5920      * Used to define events on this Observable
5921      * @param {Object} object The object with the events defined
5922      */
5923     addEvents : function(o){
5924         if(!this.events){
5925             this.events = {};
5926         }
5927         Roo.applyIf(this.events, o);
5928     },
5929
5930     /**
5931      * Checks to see if this object has any listeners for a specified event
5932      * @param {String} eventName The name of the event to check for
5933      * @return {Boolean} True if the event is being listened for, else false
5934      */
5935     hasListener : function(eventName){
5936         var e = this.events[eventName];
5937         return typeof e == "object" && e.listeners.length > 0;
5938     }
5939 };
5940 /**
5941  * Appends an event handler to this element (shorthand for addListener)
5942  * @param {String}   eventName     The type of event to listen for
5943  * @param {Function} handler        The method the event invokes
5944  * @param {Object}   scope (optional) The scope in which to execute the handler
5945  * function. The handler function's "this" context.
5946  * @param {Object}   options  (optional)
5947  * @method
5948  */
5949 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
5950 /**
5951  * Removes a listener (shorthand for removeListener)
5952  * @param {String}   eventName     The type of event to listen for
5953  * @param {Function} handler        The handler to remove
5954  * @param {Object}   scope  (optional) The scope (this object) for the handler
5955  * @method
5956  */
5957 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
5958
5959 /**
5960  * Starts capture on the specified Observable. All events will be passed
5961  * to the supplied function with the event name + standard signature of the event
5962  * <b>before</b> the event is fired. If the supplied function returns false,
5963  * the event will not fire.
5964  * @param {Observable} o The Observable to capture
5965  * @param {Function} fn The function to call
5966  * @param {Object} scope (optional) The scope (this object) for the fn
5967  * @static
5968  */
5969 Roo.util.Observable.capture = function(o, fn, scope){
5970     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
5971 };
5972
5973 /**
5974  * Removes <b>all</b> added captures from the Observable.
5975  * @param {Observable} o The Observable to release
5976  * @static
5977  */
5978 Roo.util.Observable.releaseCapture = function(o){
5979     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
5980 };
5981
5982 (function(){
5983
5984     var createBuffered = function(h, o, scope){
5985         var task = new Roo.util.DelayedTask();
5986         return function(){
5987             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
5988         };
5989     };
5990
5991     var createSingle = function(h, e, fn, scope){
5992         return function(){
5993             e.removeListener(fn, scope);
5994             return h.apply(scope, arguments);
5995         };
5996     };
5997
5998     var createDelayed = function(h, o, scope){
5999         return function(){
6000             var args = Array.prototype.slice.call(arguments, 0);
6001             setTimeout(function(){
6002                 h.apply(scope, args);
6003             }, o.delay || 10);
6004         };
6005     };
6006
6007     Roo.util.Event = function(obj, name){
6008         this.name = name;
6009         this.obj = obj;
6010         this.listeners = [];
6011     };
6012
6013     Roo.util.Event.prototype = {
6014         addListener : function(fn, scope, options){
6015             var o = options || {};
6016             scope = scope || this.obj;
6017             if(!this.isListening(fn, scope)){
6018                 var l = {fn: fn, scope: scope, options: o};
6019                 var h = fn;
6020                 if(o.delay){
6021                     h = createDelayed(h, o, scope);
6022                 }
6023                 if(o.single){
6024                     h = createSingle(h, this, fn, scope);
6025                 }
6026                 if(o.buffer){
6027                     h = createBuffered(h, o, scope);
6028                 }
6029                 l.fireFn = h;
6030                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
6031                     this.listeners.push(l);
6032                 }else{
6033                     this.listeners = this.listeners.slice(0);
6034                     this.listeners.push(l);
6035                 }
6036             }
6037         },
6038
6039         findListener : function(fn, scope){
6040             scope = scope || this.obj;
6041             var ls = this.listeners;
6042             for(var i = 0, len = ls.length; i < len; i++){
6043                 var l = ls[i];
6044                 if(l.fn == fn && l.scope == scope){
6045                     return i;
6046                 }
6047             }
6048             return -1;
6049         },
6050
6051         isListening : function(fn, scope){
6052             return this.findListener(fn, scope) != -1;
6053         },
6054
6055         removeListener : function(fn, scope){
6056             var index;
6057             if((index = this.findListener(fn, scope)) != -1){
6058                 if(!this.firing){
6059                     this.listeners.splice(index, 1);
6060                 }else{
6061                     this.listeners = this.listeners.slice(0);
6062                     this.listeners.splice(index, 1);
6063                 }
6064                 return true;
6065             }
6066             return false;
6067         },
6068
6069         clearListeners : function(){
6070             this.listeners = [];
6071         },
6072
6073         fire : function(){
6074             var ls = this.listeners, scope, len = ls.length;
6075             if(len > 0){
6076                 this.firing = true;
6077                 var args = Array.prototype.slice.call(arguments, 0);                
6078                 for(var i = 0; i < len; i++){
6079                     var l = ls[i];
6080                     if(l.fireFn.apply(l.scope||this.obj||window, args) === false){
6081                         this.firing = false;
6082                         return false;
6083                     }
6084                 }
6085                 this.firing = false;
6086             }
6087             return true;
6088         }
6089     };
6090 })();/*
6091  * RooJS Library 
6092  * Copyright(c) 2007-2017, Roo J Solutions Ltd
6093  *
6094  * Licence LGPL 
6095  *
6096  */
6097  
6098 /**
6099  * @class Roo.Document
6100  * @extends Roo.util.Observable
6101  * This is a convience class to wrap up the main document loading code.. , rather than adding Roo.onReady(......)
6102  * 
6103  * @param {Object} config the methods and properties of the 'base' class for the application.
6104  * 
6105  *  Generic Page handler - implement this to start your app..
6106  * 
6107  * eg.
6108  *  MyProject = new Roo.Document({
6109         events : {
6110             'load' : true // your events..
6111         },
6112         listeners : {
6113             'ready' : function() {
6114                 // fired on Roo.onReady()
6115             }
6116         }
6117  * 
6118  */
6119 Roo.Document = function(cfg) {
6120      
6121     this.addEvents({ 
6122         'ready' : true
6123     });
6124     Roo.util.Observable.call(this,cfg);
6125     
6126     var _this = this;
6127     
6128     Roo.onReady(function() {
6129         _this.fireEvent('ready');
6130     },null,false);
6131     
6132     
6133 }
6134
6135 Roo.extend(Roo.Document, Roo.util.Observable, {});/*
6136  * Based on:
6137  * Ext JS Library 1.1.1
6138  * Copyright(c) 2006-2007, Ext JS, LLC.
6139  *
6140  * Originally Released Under LGPL - original licence link has changed is not relivant.
6141  *
6142  * Fork - LGPL
6143  * <script type="text/javascript">
6144  */
6145
6146 /**
6147  * @class Roo.EventManager
6148  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
6149  * several useful events directly.
6150  * See {@link Roo.EventObject} for more details on normalized event objects.
6151  * @singleton
6152  */
6153 Roo.EventManager = function(){
6154     var docReadyEvent, docReadyProcId, docReadyState = false;
6155     var resizeEvent, resizeTask, textEvent, textSize;
6156     var E = Roo.lib.Event;
6157     var D = Roo.lib.Dom;
6158
6159     
6160     
6161
6162     var fireDocReady = function(){
6163         if(!docReadyState){
6164             docReadyState = true;
6165             Roo.isReady = true;
6166             if(docReadyProcId){
6167                 clearInterval(docReadyProcId);
6168             }
6169             if(Roo.isGecko || Roo.isOpera) {
6170                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
6171             }
6172             if(Roo.isIE){
6173                 var defer = document.getElementById("ie-deferred-loader");
6174                 if(defer){
6175                     defer.onreadystatechange = null;
6176                     defer.parentNode.removeChild(defer);
6177                 }
6178             }
6179             if(docReadyEvent){
6180                 docReadyEvent.fire();
6181                 docReadyEvent.clearListeners();
6182             }
6183         }
6184     };
6185     
6186     var initDocReady = function(){
6187         docReadyEvent = new Roo.util.Event();
6188         if(Roo.isGecko || Roo.isOpera) {
6189             document.addEventListener("DOMContentLoaded", fireDocReady, false);
6190         }else if(Roo.isIE){
6191             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
6192             var defer = document.getElementById("ie-deferred-loader");
6193             defer.onreadystatechange = function(){
6194                 if(this.readyState == "complete"){
6195                     fireDocReady();
6196                 }
6197             };
6198         }else if(Roo.isSafari){ 
6199             docReadyProcId = setInterval(function(){
6200                 var rs = document.readyState;
6201                 if(rs == "complete") {
6202                     fireDocReady();     
6203                  }
6204             }, 10);
6205         }
6206         // no matter what, make sure it fires on load
6207         E.on(window, "load", fireDocReady);
6208     };
6209
6210     var createBuffered = function(h, o){
6211         var task = new Roo.util.DelayedTask(h);
6212         return function(e){
6213             // create new event object impl so new events don't wipe out properties
6214             e = new Roo.EventObjectImpl(e);
6215             task.delay(o.buffer, h, null, [e]);
6216         };
6217     };
6218
6219     var createSingle = function(h, el, ename, fn){
6220         return function(e){
6221             Roo.EventManager.removeListener(el, ename, fn);
6222             h(e);
6223         };
6224     };
6225
6226     var createDelayed = function(h, o){
6227         return function(e){
6228             // create new event object impl so new events don't wipe out properties
6229             e = new Roo.EventObjectImpl(e);
6230             setTimeout(function(){
6231                 h(e);
6232             }, o.delay || 10);
6233         };
6234     };
6235     var transitionEndVal = false;
6236     
6237     var transitionEnd = function()
6238     {
6239         if (transitionEndVal) {
6240             return transitionEndVal;
6241         }
6242         var el = document.createElement('div');
6243
6244         var transEndEventNames = {
6245             WebkitTransition : 'webkitTransitionEnd',
6246             MozTransition    : 'transitionend',
6247             OTransition      : 'oTransitionEnd otransitionend',
6248             transition       : 'transitionend'
6249         };
6250     
6251         for (var name in transEndEventNames) {
6252             if (el.style[name] !== undefined) {
6253                 transitionEndVal = transEndEventNames[name];
6254                 return  transitionEndVal ;
6255             }
6256         }
6257     }
6258     
6259
6260     var listen = function(element, ename, opt, fn, scope){
6261         var o = (!opt || typeof opt == "boolean") ? {} : opt;
6262         fn = fn || o.fn; scope = scope || o.scope;
6263         var el = Roo.getDom(element);
6264         
6265         
6266         if(!el){
6267             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
6268         }
6269         
6270         if (ename == 'transitionend') {
6271             ename = transitionEnd();
6272         }
6273         var h = function(e){
6274             e = Roo.EventObject.setEvent(e);
6275             var t;
6276             if(o.delegate){
6277                 t = e.getTarget(o.delegate, el);
6278                 if(!t){
6279                     return;
6280                 }
6281             }else{
6282                 t = e.target;
6283             }
6284             if(o.stopEvent === true){
6285                 e.stopEvent();
6286             }
6287             if(o.preventDefault === true){
6288                e.preventDefault();
6289             }
6290             if(o.stopPropagation === true){
6291                 e.stopPropagation();
6292             }
6293
6294             if(o.normalized === false){
6295                 e = e.browserEvent;
6296             }
6297
6298             fn.call(scope || el, e, t, o);
6299         };
6300         if(o.delay){
6301             h = createDelayed(h, o);
6302         }
6303         if(o.single){
6304             h = createSingle(h, el, ename, fn);
6305         }
6306         if(o.buffer){
6307             h = createBuffered(h, o);
6308         }
6309         
6310         fn._handlers = fn._handlers || [];
6311         
6312         
6313         fn._handlers.push([Roo.id(el), ename, h]);
6314         
6315         
6316          
6317         E.on(el, ename, h);
6318         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
6319             el.addEventListener("DOMMouseScroll", h, false);
6320             E.on(window, 'unload', function(){
6321                 el.removeEventListener("DOMMouseScroll", h, false);
6322             });
6323         }
6324         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6325             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
6326         }
6327         return h;
6328     };
6329
6330     var stopListening = function(el, ename, fn){
6331         var id = Roo.id(el), hds = fn._handlers, hd = fn;
6332         if(hds){
6333             for(var i = 0, len = hds.length; i < len; i++){
6334                 var h = hds[i];
6335                 if(h[0] == id && h[1] == ename){
6336                     hd = h[2];
6337                     hds.splice(i, 1);
6338                     break;
6339                 }
6340             }
6341         }
6342         E.un(el, ename, hd);
6343         el = Roo.getDom(el);
6344         if(ename == "mousewheel" && el.addEventListener){
6345             el.removeEventListener("DOMMouseScroll", hd, false);
6346         }
6347         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6348             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
6349         }
6350     };
6351
6352     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
6353     
6354     var pub = {
6355         
6356         
6357         /** 
6358          * Fix for doc tools
6359          * @scope Roo.EventManager
6360          */
6361         
6362         
6363         /** 
6364          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
6365          * object with a Roo.EventObject
6366          * @param {Function} fn        The method the event invokes
6367          * @param {Object}   scope    An object that becomes the scope of the handler
6368          * @param {boolean}  override If true, the obj passed in becomes
6369          *                             the execution scope of the listener
6370          * @return {Function} The wrapped function
6371          * @deprecated
6372          */
6373         wrap : function(fn, scope, override){
6374             return function(e){
6375                 Roo.EventObject.setEvent(e);
6376                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
6377             };
6378         },
6379         
6380         /**
6381      * Appends an event handler to an element (shorthand for addListener)
6382      * @param {String/HTMLElement}   element        The html element or id to assign the
6383      * @param {String}   eventName The type of event to listen for
6384      * @param {Function} handler The method the event invokes
6385      * @param {Object}   scope (optional) The scope in which to execute the handler
6386      * function. The handler function's "this" context.
6387      * @param {Object}   options (optional) An object containing handler configuration
6388      * properties. This may contain any of the following properties:<ul>
6389      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6390      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6391      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6392      * <li>preventDefault {Boolean} True to prevent the default action</li>
6393      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6394      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6395      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6396      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6397      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6398      * by the specified number of milliseconds. If the event fires again within that time, the original
6399      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6400      * </ul><br>
6401      * <p>
6402      * <b>Combining Options</b><br>
6403      * Using the options argument, it is possible to combine different types of listeners:<br>
6404      * <br>
6405      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6406      * Code:<pre><code>
6407 el.on('click', this.onClick, this, {
6408     single: true,
6409     delay: 100,
6410     stopEvent : true,
6411     forumId: 4
6412 });</code></pre>
6413      * <p>
6414      * <b>Attaching multiple handlers in 1 call</b><br>
6415       * The method also allows for a single argument to be passed which is a config object containing properties
6416      * which specify multiple handlers.
6417      * <p>
6418      * Code:<pre><code>
6419 el.on({
6420     'click' : {
6421         fn: this.onClick
6422         scope: this,
6423         delay: 100
6424     },
6425     'mouseover' : {
6426         fn: this.onMouseOver
6427         scope: this
6428     },
6429     'mouseout' : {
6430         fn: this.onMouseOut
6431         scope: this
6432     }
6433 });</code></pre>
6434      * <p>
6435      * Or a shorthand syntax:<br>
6436      * Code:<pre><code>
6437 el.on({
6438     'click' : this.onClick,
6439     'mouseover' : this.onMouseOver,
6440     'mouseout' : this.onMouseOut
6441     scope: this
6442 });</code></pre>
6443      */
6444         addListener : function(element, eventName, fn, scope, options){
6445             if(typeof eventName == "object"){
6446                 var o = eventName;
6447                 for(var e in o){
6448                     if(propRe.test(e)){
6449                         continue;
6450                     }
6451                     if(typeof o[e] == "function"){
6452                         // shared options
6453                         listen(element, e, o, o[e], o.scope);
6454                     }else{
6455                         // individual options
6456                         listen(element, e, o[e]);
6457                     }
6458                 }
6459                 return;
6460             }
6461             return listen(element, eventName, options, fn, scope);
6462         },
6463         
6464         /**
6465          * Removes an event handler
6466          *
6467          * @param {String/HTMLElement}   element        The id or html element to remove the 
6468          *                             event from
6469          * @param {String}   eventName     The type of event
6470          * @param {Function} fn
6471          * @return {Boolean} True if a listener was actually removed
6472          */
6473         removeListener : function(element, eventName, fn){
6474             return stopListening(element, eventName, fn);
6475         },
6476         
6477         /**
6478          * Fires when the document is ready (before onload and before images are loaded). Can be 
6479          * accessed shorthanded Roo.onReady().
6480          * @param {Function} fn        The method the event invokes
6481          * @param {Object}   scope    An  object that becomes the scope of the handler
6482          * @param {boolean}  options
6483          */
6484         onDocumentReady : function(fn, scope, options){
6485             if(docReadyState){ // if it already fired
6486                 docReadyEvent.addListener(fn, scope, options);
6487                 docReadyEvent.fire();
6488                 docReadyEvent.clearListeners();
6489                 return;
6490             }
6491             if(!docReadyEvent){
6492                 initDocReady();
6493             }
6494             docReadyEvent.addListener(fn, scope, options);
6495         },
6496         
6497         /**
6498          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
6499          * @param {Function} fn        The method the event invokes
6500          * @param {Object}   scope    An object that becomes the scope of the handler
6501          * @param {boolean}  options
6502          */
6503         onWindowResize : function(fn, scope, options){
6504             if(!resizeEvent){
6505                 resizeEvent = new Roo.util.Event();
6506                 resizeTask = new Roo.util.DelayedTask(function(){
6507                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6508                 });
6509                 E.on(window, "resize", function(){
6510                     if(Roo.isIE){
6511                         resizeTask.delay(50);
6512                     }else{
6513                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6514                     }
6515                 });
6516             }
6517             resizeEvent.addListener(fn, scope, options);
6518         },
6519
6520         /**
6521          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
6522          * @param {Function} fn        The method the event invokes
6523          * @param {Object}   scope    An object that becomes the scope of the handler
6524          * @param {boolean}  options
6525          */
6526         onTextResize : function(fn, scope, options){
6527             if(!textEvent){
6528                 textEvent = new Roo.util.Event();
6529                 var textEl = new Roo.Element(document.createElement('div'));
6530                 textEl.dom.className = 'x-text-resize';
6531                 textEl.dom.innerHTML = 'X';
6532                 textEl.appendTo(document.body);
6533                 textSize = textEl.dom.offsetHeight;
6534                 setInterval(function(){
6535                     if(textEl.dom.offsetHeight != textSize){
6536                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
6537                     }
6538                 }, this.textResizeInterval);
6539             }
6540             textEvent.addListener(fn, scope, options);
6541         },
6542
6543         /**
6544          * Removes the passed window resize listener.
6545          * @param {Function} fn        The method the event invokes
6546          * @param {Object}   scope    The scope of handler
6547          */
6548         removeResizeListener : function(fn, scope){
6549             if(resizeEvent){
6550                 resizeEvent.removeListener(fn, scope);
6551             }
6552         },
6553
6554         // private
6555         fireResize : function(){
6556             if(resizeEvent){
6557                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6558             }   
6559         },
6560         /**
6561          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
6562          */
6563         ieDeferSrc : false,
6564         /**
6565          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
6566          */
6567         textResizeInterval : 50
6568     };
6569     
6570     /**
6571      * Fix for doc tools
6572      * @scopeAlias pub=Roo.EventManager
6573      */
6574     
6575      /**
6576      * Appends an event handler to an element (shorthand for addListener)
6577      * @param {String/HTMLElement}   element        The html element or id to assign the
6578      * @param {String}   eventName The type of event to listen for
6579      * @param {Function} handler The method the event invokes
6580      * @param {Object}   scope (optional) The scope in which to execute the handler
6581      * function. The handler function's "this" context.
6582      * @param {Object}   options (optional) An object containing handler configuration
6583      * properties. This may contain any of the following properties:<ul>
6584      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6585      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6586      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6587      * <li>preventDefault {Boolean} True to prevent the default action</li>
6588      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6589      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6590      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6591      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6592      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6593      * by the specified number of milliseconds. If the event fires again within that time, the original
6594      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6595      * </ul><br>
6596      * <p>
6597      * <b>Combining Options</b><br>
6598      * Using the options argument, it is possible to combine different types of listeners:<br>
6599      * <br>
6600      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6601      * Code:<pre><code>
6602 el.on('click', this.onClick, this, {
6603     single: true,
6604     delay: 100,
6605     stopEvent : true,
6606     forumId: 4
6607 });</code></pre>
6608      * <p>
6609      * <b>Attaching multiple handlers in 1 call</b><br>
6610       * The method also allows for a single argument to be passed which is a config object containing properties
6611      * which specify multiple handlers.
6612      * <p>
6613      * Code:<pre><code>
6614 el.on({
6615     'click' : {
6616         fn: this.onClick
6617         scope: this,
6618         delay: 100
6619     },
6620     'mouseover' : {
6621         fn: this.onMouseOver
6622         scope: this
6623     },
6624     'mouseout' : {
6625         fn: this.onMouseOut
6626         scope: this
6627     }
6628 });</code></pre>
6629      * <p>
6630      * Or a shorthand syntax:<br>
6631      * Code:<pre><code>
6632 el.on({
6633     'click' : this.onClick,
6634     'mouseover' : this.onMouseOver,
6635     'mouseout' : this.onMouseOut
6636     scope: this
6637 });</code></pre>
6638      */
6639     pub.on = pub.addListener;
6640     pub.un = pub.removeListener;
6641
6642     pub.stoppedMouseDownEvent = new Roo.util.Event();
6643     return pub;
6644 }();
6645 /**
6646   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
6647   * @param {Function} fn        The method the event invokes
6648   * @param {Object}   scope    An  object that becomes the scope of the handler
6649   * @param {boolean}  override If true, the obj passed in becomes
6650   *                             the execution scope of the listener
6651   * @member Roo
6652   * @method onReady
6653  */
6654 Roo.onReady = Roo.EventManager.onDocumentReady;
6655
6656 Roo.onReady(function(){
6657     var bd = Roo.get(document.body);
6658     if(!bd){ return; }
6659
6660     var cls = [
6661             Roo.isIE ? "roo-ie"
6662             : Roo.isIE11 ? "roo-ie11"
6663             : Roo.isEdge ? "roo-edge"
6664             : Roo.isGecko ? "roo-gecko"
6665             : Roo.isOpera ? "roo-opera"
6666             : Roo.isSafari ? "roo-safari" : ""];
6667
6668     if(Roo.isMac){
6669         cls.push("roo-mac");
6670     }
6671     if(Roo.isLinux){
6672         cls.push("roo-linux");
6673     }
6674     if(Roo.isIOS){
6675         cls.push("roo-ios");
6676     }
6677     if(Roo.isTouch){
6678         cls.push("roo-touch");
6679     }
6680     if(Roo.isBorderBox){
6681         cls.push('roo-border-box');
6682     }
6683     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
6684         var p = bd.dom.parentNode;
6685         if(p){
6686             p.className += ' roo-strict';
6687         }
6688     }
6689     bd.addClass(cls.join(' '));
6690 });
6691
6692 /**
6693  * @class Roo.EventObject
6694  * EventObject exposes the Yahoo! UI Event functionality directly on the object
6695  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
6696  * Example:
6697  * <pre><code>
6698  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
6699     e.preventDefault();
6700     var target = e.getTarget();
6701     ...
6702  }
6703  var myDiv = Roo.get("myDiv");
6704  myDiv.on("click", handleClick);
6705  //or
6706  Roo.EventManager.on("myDiv", 'click', handleClick);
6707  Roo.EventManager.addListener("myDiv", 'click', handleClick);
6708  </code></pre>
6709  * @singleton
6710  */
6711 Roo.EventObject = function(){
6712     
6713     var E = Roo.lib.Event;
6714     
6715     // safari keypress events for special keys return bad keycodes
6716     var safariKeys = {
6717         63234 : 37, // left
6718         63235 : 39, // right
6719         63232 : 38, // up
6720         63233 : 40, // down
6721         63276 : 33, // page up
6722         63277 : 34, // page down
6723         63272 : 46, // delete
6724         63273 : 36, // home
6725         63275 : 35  // end
6726     };
6727
6728     // normalize button clicks
6729     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
6730                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
6731
6732     Roo.EventObjectImpl = function(e){
6733         if(e){
6734             this.setEvent(e.browserEvent || e);
6735         }
6736     };
6737     Roo.EventObjectImpl.prototype = {
6738         /**
6739          * Used to fix doc tools.
6740          * @scope Roo.EventObject.prototype
6741          */
6742             
6743
6744         
6745         
6746         /** The normal browser event */
6747         browserEvent : null,
6748         /** The button pressed in a mouse event */
6749         button : -1,
6750         /** True if the shift key was down during the event */
6751         shiftKey : false,
6752         /** True if the control key was down during the event */
6753         ctrlKey : false,
6754         /** True if the alt key was down during the event */
6755         altKey : false,
6756
6757         /** Key constant 
6758         * @type Number */
6759         BACKSPACE : 8,
6760         /** Key constant 
6761         * @type Number */
6762         TAB : 9,
6763         /** Key constant 
6764         * @type Number */
6765         RETURN : 13,
6766         /** Key constant 
6767         * @type Number */
6768         ENTER : 13,
6769         /** Key constant 
6770         * @type Number */
6771         SHIFT : 16,
6772         /** Key constant 
6773         * @type Number */
6774         CONTROL : 17,
6775         /** Key constant 
6776         * @type Number */
6777         ESC : 27,
6778         /** Key constant 
6779         * @type Number */
6780         SPACE : 32,
6781         /** Key constant 
6782         * @type Number */
6783         PAGEUP : 33,
6784         /** Key constant 
6785         * @type Number */
6786         PAGEDOWN : 34,
6787         /** Key constant 
6788         * @type Number */
6789         END : 35,
6790         /** Key constant 
6791         * @type Number */
6792         HOME : 36,
6793         /** Key constant 
6794         * @type Number */
6795         LEFT : 37,
6796         /** Key constant 
6797         * @type Number */
6798         UP : 38,
6799         /** Key constant 
6800         * @type Number */
6801         RIGHT : 39,
6802         /** Key constant 
6803         * @type Number */
6804         DOWN : 40,
6805         /** Key constant 
6806         * @type Number */
6807         DELETE : 46,
6808         /** Key constant 
6809         * @type Number */
6810         F5 : 116,
6811
6812            /** @private */
6813         setEvent : function(e){
6814             if(e == this || (e && e.browserEvent)){ // already wrapped
6815                 return e;
6816             }
6817             this.browserEvent = e;
6818             if(e){
6819                 // normalize buttons
6820                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
6821                 if(e.type == 'click' && this.button == -1){
6822                     this.button = 0;
6823                 }
6824                 this.type = e.type;
6825                 this.shiftKey = e.shiftKey;
6826                 // mac metaKey behaves like ctrlKey
6827                 this.ctrlKey = e.ctrlKey || e.metaKey;
6828                 this.altKey = e.altKey;
6829                 // in getKey these will be normalized for the mac
6830                 this.keyCode = e.keyCode;
6831                 // keyup warnings on firefox.
6832                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
6833                 // cache the target for the delayed and or buffered events
6834                 this.target = E.getTarget(e);
6835                 // same for XY
6836                 this.xy = E.getXY(e);
6837             }else{
6838                 this.button = -1;
6839                 this.shiftKey = false;
6840                 this.ctrlKey = false;
6841                 this.altKey = false;
6842                 this.keyCode = 0;
6843                 this.charCode =0;
6844                 this.target = null;
6845                 this.xy = [0, 0];
6846             }
6847             return this;
6848         },
6849
6850         /**
6851          * Stop the event (preventDefault and stopPropagation)
6852          */
6853         stopEvent : function(){
6854             if(this.browserEvent){
6855                 if(this.browserEvent.type == 'mousedown'){
6856                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6857                 }
6858                 E.stopEvent(this.browserEvent);
6859             }
6860         },
6861
6862         /**
6863          * Prevents the browsers default handling of the event.
6864          */
6865         preventDefault : function(){
6866             if(this.browserEvent){
6867                 E.preventDefault(this.browserEvent);
6868             }
6869         },
6870
6871         /** @private */
6872         isNavKeyPress : function(){
6873             var k = this.keyCode;
6874             k = Roo.isSafari ? (safariKeys[k] || k) : k;
6875             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
6876         },
6877
6878         isSpecialKey : function(){
6879             var k = this.keyCode;
6880             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
6881             (k == 16) || (k == 17) ||
6882             (k >= 18 && k <= 20) ||
6883             (k >= 33 && k <= 35) ||
6884             (k >= 36 && k <= 39) ||
6885             (k >= 44 && k <= 45);
6886         },
6887         /**
6888          * Cancels bubbling of the event.
6889          */
6890         stopPropagation : function(){
6891             if(this.browserEvent){
6892                 if(this.type == 'mousedown'){
6893                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6894                 }
6895                 E.stopPropagation(this.browserEvent);
6896             }
6897         },
6898
6899         /**
6900          * Gets the key code for the event.
6901          * @return {Number}
6902          */
6903         getCharCode : function(){
6904             return this.charCode || this.keyCode;
6905         },
6906
6907         /**
6908          * Returns a normalized keyCode for the event.
6909          * @return {Number} The key code
6910          */
6911         getKey : function(){
6912             var k = this.keyCode || this.charCode;
6913             return Roo.isSafari ? (safariKeys[k] || k) : k;
6914         },
6915
6916         /**
6917          * Gets the x coordinate of the event.
6918          * @return {Number}
6919          */
6920         getPageX : function(){
6921             return this.xy[0];
6922         },
6923
6924         /**
6925          * Gets the y coordinate of the event.
6926          * @return {Number}
6927          */
6928         getPageY : function(){
6929             return this.xy[1];
6930         },
6931
6932         /**
6933          * Gets the time of the event.
6934          * @return {Number}
6935          */
6936         getTime : function(){
6937             if(this.browserEvent){
6938                 return E.getTime(this.browserEvent);
6939             }
6940             return null;
6941         },
6942
6943         /**
6944          * Gets the page coordinates of the event.
6945          * @return {Array} The xy values like [x, y]
6946          */
6947         getXY : function(){
6948             return this.xy;
6949         },
6950
6951         /**
6952          * Gets the target for the event.
6953          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
6954          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6955                 search as a number or element (defaults to 10 || document.body)
6956          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6957          * @return {HTMLelement}
6958          */
6959         getTarget : function(selector, maxDepth, returnEl){
6960             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
6961         },
6962         /**
6963          * Gets the related target.
6964          * @return {HTMLElement}
6965          */
6966         getRelatedTarget : function(){
6967             if(this.browserEvent){
6968                 return E.getRelatedTarget(this.browserEvent);
6969             }
6970             return null;
6971         },
6972
6973         /**
6974          * Normalizes mouse wheel delta across browsers
6975          * @return {Number} The delta
6976          */
6977         getWheelDelta : function(){
6978             var e = this.browserEvent;
6979             var delta = 0;
6980             if(e.wheelDelta){ /* IE/Opera. */
6981                 delta = e.wheelDelta/120;
6982             }else if(e.detail){ /* Mozilla case. */
6983                 delta = -e.detail/3;
6984             }
6985             return delta;
6986         },
6987
6988         /**
6989          * Returns true if the control, meta, shift or alt key was pressed during this event.
6990          * @return {Boolean}
6991          */
6992         hasModifier : function(){
6993             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
6994         },
6995
6996         /**
6997          * Returns true if the target of this event equals el or is a child of el
6998          * @param {String/HTMLElement/Element} el
6999          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
7000          * @return {Boolean}
7001          */
7002         within : function(el, related){
7003             var t = this[related ? "getRelatedTarget" : "getTarget"]();
7004             return t && Roo.fly(el).contains(t);
7005         },
7006
7007         getPoint : function(){
7008             return new Roo.lib.Point(this.xy[0], this.xy[1]);
7009         }
7010     };
7011
7012     return new Roo.EventObjectImpl();
7013 }();
7014             
7015     /*
7016  * Based on:
7017  * Ext JS Library 1.1.1
7018  * Copyright(c) 2006-2007, Ext JS, LLC.
7019  *
7020  * Originally Released Under LGPL - original licence link has changed is not relivant.
7021  *
7022  * Fork - LGPL
7023  * <script type="text/javascript">
7024  */
7025
7026  
7027 // was in Composite Element!??!?!
7028  
7029 (function(){
7030     var D = Roo.lib.Dom;
7031     var E = Roo.lib.Event;
7032     var A = Roo.lib.Anim;
7033
7034     // local style camelizing for speed
7035     var propCache = {};
7036     var camelRe = /(-[a-z])/gi;
7037     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
7038     var view = document.defaultView;
7039
7040 /**
7041  * @class Roo.Element
7042  * Represents an Element in the DOM.<br><br>
7043  * Usage:<br>
7044 <pre><code>
7045 var el = Roo.get("my-div");
7046
7047 // or with getEl
7048 var el = getEl("my-div");
7049
7050 // or with a DOM element
7051 var el = Roo.get(myDivElement);
7052 </code></pre>
7053  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
7054  * each call instead of constructing a new one.<br><br>
7055  * <b>Animations</b><br />
7056  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
7057  * should either be a boolean (true) or an object literal with animation options. The animation options are:
7058 <pre>
7059 Option    Default   Description
7060 --------- --------  ---------------------------------------------
7061 duration  .35       The duration of the animation in seconds
7062 easing    easeOut   The YUI easing method
7063 callback  none      A function to execute when the anim completes
7064 scope     this      The scope (this) of the callback function
7065 </pre>
7066 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
7067 * manipulate the animation. Here's an example:
7068 <pre><code>
7069 var el = Roo.get("my-div");
7070
7071 // no animation
7072 el.setWidth(100);
7073
7074 // default animation
7075 el.setWidth(100, true);
7076
7077 // animation with some options set
7078 el.setWidth(100, {
7079     duration: 1,
7080     callback: this.foo,
7081     scope: this
7082 });
7083
7084 // using the "anim" property to get the Anim object
7085 var opt = {
7086     duration: 1,
7087     callback: this.foo,
7088     scope: this
7089 };
7090 el.setWidth(100, opt);
7091 ...
7092 if(opt.anim.isAnimated()){
7093     opt.anim.stop();
7094 }
7095 </code></pre>
7096 * <b> Composite (Collections of) Elements</b><br />
7097  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
7098  * @constructor Create a new Element directly.
7099  * @param {String/HTMLElement} element
7100  * @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).
7101  */
7102     Roo.Element = function(element, forceNew){
7103         var dom = typeof element == "string" ?
7104                 document.getElementById(element) : element;
7105         if(!dom){ // invalid id/element
7106             return null;
7107         }
7108         var id = dom.id;
7109         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
7110             return Roo.Element.cache[id];
7111         }
7112
7113         /**
7114          * The DOM element
7115          * @type HTMLElement
7116          */
7117         this.dom = dom;
7118
7119         /**
7120          * The DOM element ID
7121          * @type String
7122          */
7123         this.id = id || Roo.id(dom);
7124     };
7125
7126     var El = Roo.Element;
7127
7128     El.prototype = {
7129         /**
7130          * The element's default display mode  (defaults to "")
7131          * @type String
7132          */
7133         originalDisplay : "",
7134
7135         visibilityMode : 1,
7136         /**
7137          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
7138          * @type String
7139          */
7140         defaultUnit : "px",
7141         
7142         /**
7143          * Sets the element's visibility mode. When setVisible() is called it
7144          * will use this to determine whether to set the visibility or the display property.
7145          * @param visMode Element.VISIBILITY or Element.DISPLAY
7146          * @return {Roo.Element} this
7147          */
7148         setVisibilityMode : function(visMode){
7149             this.visibilityMode = visMode;
7150             return this;
7151         },
7152         /**
7153          * Convenience method for setVisibilityMode(Element.DISPLAY)
7154          * @param {String} display (optional) What to set display to when visible
7155          * @return {Roo.Element} this
7156          */
7157         enableDisplayMode : function(display){
7158             this.setVisibilityMode(El.DISPLAY);
7159             if(typeof display != "undefined") { this.originalDisplay = display; }
7160             return this;
7161         },
7162
7163         /**
7164          * 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)
7165          * @param {String} selector The simple selector to test
7166          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7167                 search as a number or element (defaults to 10 || document.body)
7168          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7169          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7170          */
7171         findParent : function(simpleSelector, maxDepth, returnEl){
7172             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
7173             maxDepth = maxDepth || 50;
7174             if(typeof maxDepth != "number"){
7175                 stopEl = Roo.getDom(maxDepth);
7176                 maxDepth = 10;
7177             }
7178             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
7179                 if(dq.is(p, simpleSelector)){
7180                     return returnEl ? Roo.get(p) : p;
7181                 }
7182                 depth++;
7183                 p = p.parentNode;
7184             }
7185             return null;
7186         },
7187
7188
7189         /**
7190          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
7191          * @param {String} selector The simple selector to test
7192          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7193                 search as a number or element (defaults to 10 || document.body)
7194          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7195          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7196          */
7197         findParentNode : function(simpleSelector, maxDepth, returnEl){
7198             var p = Roo.fly(this.dom.parentNode, '_internal');
7199             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
7200         },
7201         
7202         /**
7203          * Looks at  the scrollable parent element
7204          */
7205         findScrollableParent : function()
7206         {
7207             var overflowRegex = /(auto|scroll)/;
7208             
7209             if(this.getStyle('position') === 'fixed'){
7210                 return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
7211             }
7212             
7213             var excludeStaticParent = this.getStyle('position') === "absolute";
7214             
7215             for (var parent = this; (parent = Roo.get(parent.dom.parentNode));){
7216                 
7217                 if (excludeStaticParent && parent.getStyle('position') === "static") {
7218                     continue;
7219                 }
7220                 
7221                 if (overflowRegex.test(parent.getStyle('overflow') + parent.getStyle('overflow-x') + parent.getStyle('overflow-y'))){
7222                     return parent;
7223                 }
7224                 
7225                 if(parent.dom.nodeName.toLowerCase() == 'body'){
7226                     return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
7227                 }
7228             }
7229             
7230             return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
7231         },
7232
7233         /**
7234          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
7235          * This is a shortcut for findParentNode() that always returns an Roo.Element.
7236          * @param {String} selector The simple selector to test
7237          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7238                 search as a number or element (defaults to 10 || document.body)
7239          * @return {Roo.Element} The matching DOM node (or null if no match was found)
7240          */
7241         up : function(simpleSelector, maxDepth){
7242             return this.findParentNode(simpleSelector, maxDepth, true);
7243         },
7244
7245
7246
7247         /**
7248          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
7249          * @param {String} selector The simple selector to test
7250          * @return {Boolean} True if this element matches the selector, else false
7251          */
7252         is : function(simpleSelector){
7253             return Roo.DomQuery.is(this.dom, simpleSelector);
7254         },
7255
7256         /**
7257          * Perform animation on this element.
7258          * @param {Object} args The YUI animation control args
7259          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
7260          * @param {Function} onComplete (optional) Function to call when animation completes
7261          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
7262          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
7263          * @return {Roo.Element} this
7264          */
7265         animate : function(args, duration, onComplete, easing, animType){
7266             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
7267             return this;
7268         },
7269
7270         /*
7271          * @private Internal animation call
7272          */
7273         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
7274             animType = animType || 'run';
7275             opt = opt || {};
7276             var anim = Roo.lib.Anim[animType](
7277                 this.dom, args,
7278                 (opt.duration || defaultDur) || .35,
7279                 (opt.easing || defaultEase) || 'easeOut',
7280                 function(){
7281                     Roo.callback(cb, this);
7282                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
7283                 },
7284                 this
7285             );
7286             opt.anim = anim;
7287             return anim;
7288         },
7289
7290         // private legacy anim prep
7291         preanim : function(a, i){
7292             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
7293         },
7294
7295         /**
7296          * Removes worthless text nodes
7297          * @param {Boolean} forceReclean (optional) By default the element
7298          * keeps track if it has been cleaned already so
7299          * you can call this over and over. However, if you update the element and
7300          * need to force a reclean, you can pass true.
7301          */
7302         clean : function(forceReclean){
7303             if(this.isCleaned && forceReclean !== true){
7304                 return this;
7305             }
7306             var ns = /\S/;
7307             var d = this.dom, n = d.firstChild, ni = -1;
7308             while(n){
7309                 var nx = n.nextSibling;
7310                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
7311                     d.removeChild(n);
7312                 }else{
7313                     n.nodeIndex = ++ni;
7314                 }
7315                 n = nx;
7316             }
7317             this.isCleaned = true;
7318             return this;
7319         },
7320
7321         // private
7322         calcOffsetsTo : function(el){
7323             el = Roo.get(el);
7324             var d = el.dom;
7325             var restorePos = false;
7326             if(el.getStyle('position') == 'static'){
7327                 el.position('relative');
7328                 restorePos = true;
7329             }
7330             var x = 0, y =0;
7331             var op = this.dom;
7332             while(op && op != d && op.tagName != 'HTML'){
7333                 x+= op.offsetLeft;
7334                 y+= op.offsetTop;
7335                 op = op.offsetParent;
7336             }
7337             if(restorePos){
7338                 el.position('static');
7339             }
7340             return [x, y];
7341         },
7342
7343         /**
7344          * Scrolls this element into view within the passed container.
7345          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
7346          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
7347          * @return {Roo.Element} this
7348          */
7349         scrollIntoView : function(container, hscroll){
7350             var c = Roo.getDom(container) || document.body;
7351             var el = this.dom;
7352
7353             var o = this.calcOffsetsTo(c),
7354                 l = o[0],
7355                 t = o[1],
7356                 b = t+el.offsetHeight,
7357                 r = l+el.offsetWidth;
7358
7359             var ch = c.clientHeight;
7360             var ct = parseInt(c.scrollTop, 10);
7361             var cl = parseInt(c.scrollLeft, 10);
7362             var cb = ct + ch;
7363             var cr = cl + c.clientWidth;
7364
7365             if(t < ct){
7366                 c.scrollTop = t;
7367             }else if(b > cb){
7368                 c.scrollTop = b-ch;
7369             }
7370
7371             if(hscroll !== false){
7372                 if(l < cl){
7373                     c.scrollLeft = l;
7374                 }else if(r > cr){
7375                     c.scrollLeft = r-c.clientWidth;
7376                 }
7377             }
7378             return this;
7379         },
7380
7381         // private
7382         scrollChildIntoView : function(child, hscroll){
7383             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
7384         },
7385
7386         /**
7387          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
7388          * the new height may not be available immediately.
7389          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
7390          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
7391          * @param {Function} onComplete (optional) Function to call when animation completes
7392          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
7393          * @return {Roo.Element} this
7394          */
7395         autoHeight : function(animate, duration, onComplete, easing){
7396             var oldHeight = this.getHeight();
7397             this.clip();
7398             this.setHeight(1); // force clipping
7399             setTimeout(function(){
7400                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
7401                 if(!animate){
7402                     this.setHeight(height);
7403                     this.unclip();
7404                     if(typeof onComplete == "function"){
7405                         onComplete();
7406                     }
7407                 }else{
7408                     this.setHeight(oldHeight); // restore original height
7409                     this.setHeight(height, animate, duration, function(){
7410                         this.unclip();
7411                         if(typeof onComplete == "function") { onComplete(); }
7412                     }.createDelegate(this), easing);
7413                 }
7414             }.createDelegate(this), 0);
7415             return this;
7416         },
7417
7418         /**
7419          * Returns true if this element is an ancestor of the passed element
7420          * @param {HTMLElement/String} el The element to check
7421          * @return {Boolean} True if this element is an ancestor of el, else false
7422          */
7423         contains : function(el){
7424             if(!el){return false;}
7425             return D.isAncestor(this.dom, el.dom ? el.dom : el);
7426         },
7427
7428         /**
7429          * Checks whether the element is currently visible using both visibility and display properties.
7430          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
7431          * @return {Boolean} True if the element is currently visible, else false
7432          */
7433         isVisible : function(deep) {
7434             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
7435             if(deep !== true || !vis){
7436                 return vis;
7437             }
7438             var p = this.dom.parentNode;
7439             while(p && p.tagName.toLowerCase() != "body"){
7440                 if(!Roo.fly(p, '_isVisible').isVisible()){
7441                     return false;
7442                 }
7443                 p = p.parentNode;
7444             }
7445             return true;
7446         },
7447
7448         /**
7449          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
7450          * @param {String} selector The CSS selector
7451          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
7452          * @return {CompositeElement/CompositeElementLite} The composite element
7453          */
7454         select : function(selector, unique){
7455             return El.select(selector, unique, this.dom);
7456         },
7457
7458         /**
7459          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
7460          * @param {String} selector The CSS selector
7461          * @return {Array} An array of the matched nodes
7462          */
7463         query : function(selector, unique){
7464             return Roo.DomQuery.select(selector, this.dom);
7465         },
7466
7467         /**
7468          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
7469          * @param {String} selector The CSS selector
7470          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7471          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7472          */
7473         child : function(selector, returnDom){
7474             var n = Roo.DomQuery.selectNode(selector, this.dom);
7475             return returnDom ? n : Roo.get(n);
7476         },
7477
7478         /**
7479          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
7480          * @param {String} selector The CSS selector
7481          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7482          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7483          */
7484         down : function(selector, returnDom){
7485             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
7486             return returnDom ? n : Roo.get(n);
7487         },
7488
7489         /**
7490          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
7491          * @param {String} group The group the DD object is member of
7492          * @param {Object} config The DD config object
7493          * @param {Object} overrides An object containing methods to override/implement on the DD object
7494          * @return {Roo.dd.DD} The DD object
7495          */
7496         initDD : function(group, config, overrides){
7497             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
7498             return Roo.apply(dd, overrides);
7499         },
7500
7501         /**
7502          * Initializes a {@link Roo.dd.DDProxy} object for this element.
7503          * @param {String} group The group the DDProxy object is member of
7504          * @param {Object} config The DDProxy config object
7505          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
7506          * @return {Roo.dd.DDProxy} The DDProxy object
7507          */
7508         initDDProxy : function(group, config, overrides){
7509             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
7510             return Roo.apply(dd, overrides);
7511         },
7512
7513         /**
7514          * Initializes a {@link Roo.dd.DDTarget} object for this element.
7515          * @param {String} group The group the DDTarget object is member of
7516          * @param {Object} config The DDTarget config object
7517          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
7518          * @return {Roo.dd.DDTarget} The DDTarget object
7519          */
7520         initDDTarget : function(group, config, overrides){
7521             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
7522             return Roo.apply(dd, overrides);
7523         },
7524
7525         /**
7526          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
7527          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
7528          * @param {Boolean} visible Whether the element is visible
7529          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7530          * @return {Roo.Element} this
7531          */
7532          setVisible : function(visible, animate){
7533             if(!animate || !A){
7534                 if(this.visibilityMode == El.DISPLAY){
7535                     this.setDisplayed(visible);
7536                 }else{
7537                     this.fixDisplay();
7538                     this.dom.style.visibility = visible ? "visible" : "hidden";
7539                 }
7540             }else{
7541                 // closure for composites
7542                 var dom = this.dom;
7543                 var visMode = this.visibilityMode;
7544                 if(visible){
7545                     this.setOpacity(.01);
7546                     this.setVisible(true);
7547                 }
7548                 this.anim({opacity: { to: (visible?1:0) }},
7549                       this.preanim(arguments, 1),
7550                       null, .35, 'easeIn', function(){
7551                          if(!visible){
7552                              if(visMode == El.DISPLAY){
7553                                  dom.style.display = "none";
7554                              }else{
7555                                  dom.style.visibility = "hidden";
7556                              }
7557                              Roo.get(dom).setOpacity(1);
7558                          }
7559                      });
7560             }
7561             return this;
7562         },
7563
7564         /**
7565          * Returns true if display is not "none"
7566          * @return {Boolean}
7567          */
7568         isDisplayed : function() {
7569             return this.getStyle("display") != "none";
7570         },
7571
7572         /**
7573          * Toggles the element's visibility or display, depending on visibility mode.
7574          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7575          * @return {Roo.Element} this
7576          */
7577         toggle : function(animate){
7578             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
7579             return this;
7580         },
7581
7582         /**
7583          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
7584          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
7585          * @return {Roo.Element} this
7586          */
7587         setDisplayed : function(value) {
7588             if(typeof value == "boolean"){
7589                value = value ? this.originalDisplay : "none";
7590             }
7591             this.setStyle("display", value);
7592             return this;
7593         },
7594
7595         /**
7596          * Tries to focus the element. Any exceptions are caught and ignored.
7597          * @return {Roo.Element} this
7598          */
7599         focus : function() {
7600             try{
7601                 this.dom.focus();
7602             }catch(e){}
7603             return this;
7604         },
7605
7606         /**
7607          * Tries to blur the element. Any exceptions are caught and ignored.
7608          * @return {Roo.Element} this
7609          */
7610         blur : function() {
7611             try{
7612                 this.dom.blur();
7613             }catch(e){}
7614             return this;
7615         },
7616
7617         /**
7618          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
7619          * @param {String/Array} className The CSS class to add, or an array of classes
7620          * @return {Roo.Element} this
7621          */
7622         addClass : function(className){
7623             if(className instanceof Array){
7624                 for(var i = 0, len = className.length; i < len; i++) {
7625                     this.addClass(className[i]);
7626                 }
7627             }else{
7628                 if(className && !this.hasClass(className)){
7629                     this.dom.className = this.dom.className + " " + className;
7630                 }
7631             }
7632             return this;
7633         },
7634
7635         /**
7636          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
7637          * @param {String/Array} className The CSS class to add, or an array of classes
7638          * @return {Roo.Element} this
7639          */
7640         radioClass : function(className){
7641             var siblings = this.dom.parentNode.childNodes;
7642             for(var i = 0; i < siblings.length; i++) {
7643                 var s = siblings[i];
7644                 if(s.nodeType == 1){
7645                     Roo.get(s).removeClass(className);
7646                 }
7647             }
7648             this.addClass(className);
7649             return this;
7650         },
7651
7652         /**
7653          * Removes one or more CSS classes from the element.
7654          * @param {String/Array} className The CSS class to remove, or an array of classes
7655          * @return {Roo.Element} this
7656          */
7657         removeClass : function(className){
7658             if(!className || !this.dom.className){
7659                 return this;
7660             }
7661             if(className instanceof Array){
7662                 for(var i = 0, len = className.length; i < len; i++) {
7663                     this.removeClass(className[i]);
7664                 }
7665             }else{
7666                 if(this.hasClass(className)){
7667                     var re = this.classReCache[className];
7668                     if (!re) {
7669                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
7670                        this.classReCache[className] = re;
7671                     }
7672                     this.dom.className =
7673                         this.dom.className.replace(re, " ");
7674                 }
7675             }
7676             return this;
7677         },
7678
7679         // private
7680         classReCache: {},
7681
7682         /**
7683          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
7684          * @param {String} className The CSS class to toggle
7685          * @return {Roo.Element} this
7686          */
7687         toggleClass : function(className){
7688             if(this.hasClass(className)){
7689                 this.removeClass(className);
7690             }else{
7691                 this.addClass(className);
7692             }
7693             return this;
7694         },
7695
7696         /**
7697          * Checks if the specified CSS class exists on this element's DOM node.
7698          * @param {String} className The CSS class to check for
7699          * @return {Boolean} True if the class exists, else false
7700          */
7701         hasClass : function(className){
7702             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
7703         },
7704
7705         /**
7706          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
7707          * @param {String} oldClassName The CSS class to replace
7708          * @param {String} newClassName The replacement CSS class
7709          * @return {Roo.Element} this
7710          */
7711         replaceClass : function(oldClassName, newClassName){
7712             this.removeClass(oldClassName);
7713             this.addClass(newClassName);
7714             return this;
7715         },
7716
7717         /**
7718          * Returns an object with properties matching the styles requested.
7719          * For example, el.getStyles('color', 'font-size', 'width') might return
7720          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
7721          * @param {String} style1 A style name
7722          * @param {String} style2 A style name
7723          * @param {String} etc.
7724          * @return {Object} The style object
7725          */
7726         getStyles : function(){
7727             var a = arguments, len = a.length, r = {};
7728             for(var i = 0; i < len; i++){
7729                 r[a[i]] = this.getStyle(a[i]);
7730             }
7731             return r;
7732         },
7733
7734         /**
7735          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
7736          * @param {String} property The style property whose value is returned.
7737          * @return {String} The current value of the style property for this element.
7738          */
7739         getStyle : function(){
7740             return view && view.getComputedStyle ?
7741                 function(prop){
7742                     var el = this.dom, v, cs, camel;
7743                     if(prop == 'float'){
7744                         prop = "cssFloat";
7745                     }
7746                     if(el.style && (v = el.style[prop])){
7747                         return v;
7748                     }
7749                     if(cs = view.getComputedStyle(el, "")){
7750                         if(!(camel = propCache[prop])){
7751                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
7752                         }
7753                         return cs[camel];
7754                     }
7755                     return null;
7756                 } :
7757                 function(prop){
7758                     var el = this.dom, v, cs, camel;
7759                     if(prop == 'opacity'){
7760                         if(typeof el.style.filter == 'string'){
7761                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
7762                             if(m){
7763                                 var fv = parseFloat(m[1]);
7764                                 if(!isNaN(fv)){
7765                                     return fv ? fv / 100 : 0;
7766                                 }
7767                             }
7768                         }
7769                         return 1;
7770                     }else if(prop == 'float'){
7771                         prop = "styleFloat";
7772                     }
7773                     if(!(camel = propCache[prop])){
7774                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
7775                     }
7776                     if(v = el.style[camel]){
7777                         return v;
7778                     }
7779                     if(cs = el.currentStyle){
7780                         return cs[camel];
7781                     }
7782                     return null;
7783                 };
7784         }(),
7785
7786         /**
7787          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
7788          * @param {String/Object} property The style property to be set, or an object of multiple styles.
7789          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
7790          * @return {Roo.Element} this
7791          */
7792         setStyle : function(prop, value){
7793             if(typeof prop == "string"){
7794                 
7795                 if (prop == 'float') {
7796                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
7797                     return this;
7798                 }
7799                 
7800                 var camel;
7801                 if(!(camel = propCache[prop])){
7802                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
7803                 }
7804                 
7805                 if(camel == 'opacity') {
7806                     this.setOpacity(value);
7807                 }else{
7808                     this.dom.style[camel] = value;
7809                 }
7810             }else{
7811                 for(var style in prop){
7812                     if(typeof prop[style] != "function"){
7813                        this.setStyle(style, prop[style]);
7814                     }
7815                 }
7816             }
7817             return this;
7818         },
7819
7820         /**
7821          * More flexible version of {@link #setStyle} for setting style properties.
7822          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
7823          * a function which returns such a specification.
7824          * @return {Roo.Element} this
7825          */
7826         applyStyles : function(style){
7827             Roo.DomHelper.applyStyles(this.dom, style);
7828             return this;
7829         },
7830
7831         /**
7832           * 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).
7833           * @return {Number} The X position of the element
7834           */
7835         getX : function(){
7836             return D.getX(this.dom);
7837         },
7838
7839         /**
7840           * 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).
7841           * @return {Number} The Y position of the element
7842           */
7843         getY : function(){
7844             return D.getY(this.dom);
7845         },
7846
7847         /**
7848           * 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).
7849           * @return {Array} The XY position of the element
7850           */
7851         getXY : function(){
7852             return D.getXY(this.dom);
7853         },
7854
7855         /**
7856          * 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).
7857          * @param {Number} The X position of the element
7858          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7859          * @return {Roo.Element} this
7860          */
7861         setX : function(x, animate){
7862             if(!animate || !A){
7863                 D.setX(this.dom, x);
7864             }else{
7865                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
7866             }
7867             return this;
7868         },
7869
7870         /**
7871          * 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).
7872          * @param {Number} The Y position of the element
7873          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7874          * @return {Roo.Element} this
7875          */
7876         setY : function(y, animate){
7877             if(!animate || !A){
7878                 D.setY(this.dom, y);
7879             }else{
7880                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
7881             }
7882             return this;
7883         },
7884
7885         /**
7886          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
7887          * @param {String} left The left CSS property value
7888          * @return {Roo.Element} this
7889          */
7890         setLeft : function(left){
7891             this.setStyle("left", this.addUnits(left));
7892             return this;
7893         },
7894
7895         /**
7896          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
7897          * @param {String} top The top CSS property value
7898          * @return {Roo.Element} this
7899          */
7900         setTop : function(top){
7901             this.setStyle("top", this.addUnits(top));
7902             return this;
7903         },
7904
7905         /**
7906          * Sets the element's CSS right style.
7907          * @param {String} right The right CSS property value
7908          * @return {Roo.Element} this
7909          */
7910         setRight : function(right){
7911             this.setStyle("right", this.addUnits(right));
7912             return this;
7913         },
7914
7915         /**
7916          * Sets the element's CSS bottom style.
7917          * @param {String} bottom The bottom CSS property value
7918          * @return {Roo.Element} this
7919          */
7920         setBottom : function(bottom){
7921             this.setStyle("bottom", this.addUnits(bottom));
7922             return this;
7923         },
7924
7925         /**
7926          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7927          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7928          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
7929          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7930          * @return {Roo.Element} this
7931          */
7932         setXY : function(pos, animate){
7933             if(!animate || !A){
7934                 D.setXY(this.dom, pos);
7935             }else{
7936                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
7937             }
7938             return this;
7939         },
7940
7941         /**
7942          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7943          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7944          * @param {Number} x X value for new position (coordinates are page-based)
7945          * @param {Number} y Y value for new position (coordinates are page-based)
7946          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7947          * @return {Roo.Element} this
7948          */
7949         setLocation : function(x, y, animate){
7950             this.setXY([x, y], this.preanim(arguments, 2));
7951             return this;
7952         },
7953
7954         /**
7955          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7956          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7957          * @param {Number} x X value for new position (coordinates are page-based)
7958          * @param {Number} y Y value for new position (coordinates are page-based)
7959          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7960          * @return {Roo.Element} this
7961          */
7962         moveTo : function(x, y, animate){
7963             this.setXY([x, y], this.preanim(arguments, 2));
7964             return this;
7965         },
7966
7967         /**
7968          * Returns the region of the given element.
7969          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
7970          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
7971          */
7972         getRegion : function(){
7973             return D.getRegion(this.dom);
7974         },
7975
7976         /**
7977          * Returns the offset height of the element
7978          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
7979          * @return {Number} The element's height
7980          */
7981         getHeight : function(contentHeight){
7982             var h = this.dom.offsetHeight || 0;
7983             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
7984         },
7985
7986         /**
7987          * Returns the offset width of the element
7988          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
7989          * @return {Number} The element's width
7990          */
7991         getWidth : function(contentWidth){
7992             var w = this.dom.offsetWidth || 0;
7993             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
7994         },
7995
7996         /**
7997          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
7998          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
7999          * if a height has not been set using CSS.
8000          * @return {Number}
8001          */
8002         getComputedHeight : function(){
8003             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
8004             if(!h){
8005                 h = parseInt(this.getStyle('height'), 10) || 0;
8006                 if(!this.isBorderBox()){
8007                     h += this.getFrameWidth('tb');
8008                 }
8009             }
8010             return h;
8011         },
8012
8013         /**
8014          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
8015          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
8016          * if a width has not been set using CSS.
8017          * @return {Number}
8018          */
8019         getComputedWidth : function(){
8020             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
8021             if(!w){
8022                 w = parseInt(this.getStyle('width'), 10) || 0;
8023                 if(!this.isBorderBox()){
8024                     w += this.getFrameWidth('lr');
8025                 }
8026             }
8027             return w;
8028         },
8029
8030         /**
8031          * Returns the size of the element.
8032          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
8033          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
8034          */
8035         getSize : function(contentSize){
8036             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
8037         },
8038
8039         /**
8040          * Returns the width and height of the viewport.
8041          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
8042          */
8043         getViewSize : function(){
8044             var d = this.dom, doc = document, aw = 0, ah = 0;
8045             if(d == doc || d == doc.body){
8046                 return {width : D.getViewWidth(), height: D.getViewHeight()};
8047             }else{
8048                 return {
8049                     width : d.clientWidth,
8050                     height: d.clientHeight
8051                 };
8052             }
8053         },
8054
8055         /**
8056          * Returns the value of the "value" attribute
8057          * @param {Boolean} asNumber true to parse the value as a number
8058          * @return {String/Number}
8059          */
8060         getValue : function(asNumber){
8061             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
8062         },
8063
8064         // private
8065         adjustWidth : function(width){
8066             if(typeof width == "number"){
8067                 if(this.autoBoxAdjust && !this.isBorderBox()){
8068                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8069                 }
8070                 if(width < 0){
8071                     width = 0;
8072                 }
8073             }
8074             return width;
8075         },
8076
8077         // private
8078         adjustHeight : function(height){
8079             if(typeof height == "number"){
8080                if(this.autoBoxAdjust && !this.isBorderBox()){
8081                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8082                }
8083                if(height < 0){
8084                    height = 0;
8085                }
8086             }
8087             return height;
8088         },
8089
8090         /**
8091          * Set the width of the element
8092          * @param {Number} width The new width
8093          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8094          * @return {Roo.Element} this
8095          */
8096         setWidth : function(width, animate){
8097             width = this.adjustWidth(width);
8098             if(!animate || !A){
8099                 this.dom.style.width = this.addUnits(width);
8100             }else{
8101                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
8102             }
8103             return this;
8104         },
8105
8106         /**
8107          * Set the height of the element
8108          * @param {Number} height The new height
8109          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8110          * @return {Roo.Element} this
8111          */
8112          setHeight : function(height, animate){
8113             height = this.adjustHeight(height);
8114             if(!animate || !A){
8115                 this.dom.style.height = this.addUnits(height);
8116             }else{
8117                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
8118             }
8119             return this;
8120         },
8121
8122         /**
8123          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
8124          * @param {Number} width The new width
8125          * @param {Number} height The new height
8126          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8127          * @return {Roo.Element} this
8128          */
8129          setSize : function(width, height, animate){
8130             if(typeof width == "object"){ // in case of object from getSize()
8131                 height = width.height; width = width.width;
8132             }
8133             width = this.adjustWidth(width); height = this.adjustHeight(height);
8134             if(!animate || !A){
8135                 this.dom.style.width = this.addUnits(width);
8136                 this.dom.style.height = this.addUnits(height);
8137             }else{
8138                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
8139             }
8140             return this;
8141         },
8142
8143         /**
8144          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
8145          * @param {Number} x X value for new position (coordinates are page-based)
8146          * @param {Number} y Y value for new position (coordinates are page-based)
8147          * @param {Number} width The new width
8148          * @param {Number} height The new height
8149          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8150          * @return {Roo.Element} this
8151          */
8152         setBounds : function(x, y, width, height, animate){
8153             if(!animate || !A){
8154                 this.setSize(width, height);
8155                 this.setLocation(x, y);
8156             }else{
8157                 width = this.adjustWidth(width); height = this.adjustHeight(height);
8158                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
8159                               this.preanim(arguments, 4), 'motion');
8160             }
8161             return this;
8162         },
8163
8164         /**
8165          * 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.
8166          * @param {Roo.lib.Region} region The region to fill
8167          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8168          * @return {Roo.Element} this
8169          */
8170         setRegion : function(region, animate){
8171             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
8172             return this;
8173         },
8174
8175         /**
8176          * Appends an event handler
8177          *
8178          * @param {String}   eventName     The type of event to append
8179          * @param {Function} fn        The method the event invokes
8180          * @param {Object} scope       (optional) The scope (this object) of the fn
8181          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
8182          */
8183         addListener : function(eventName, fn, scope, options){
8184             if (this.dom) {
8185                 Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
8186             }
8187         },
8188
8189         /**
8190          * Removes an event handler from this element
8191          * @param {String} eventName the type of event to remove
8192          * @param {Function} fn the method the event invokes
8193          * @return {Roo.Element} this
8194          */
8195         removeListener : function(eventName, fn){
8196             Roo.EventManager.removeListener(this.dom,  eventName, fn);
8197             return this;
8198         },
8199
8200         /**
8201          * Removes all previous added listeners from this element
8202          * @return {Roo.Element} this
8203          */
8204         removeAllListeners : function(){
8205             E.purgeElement(this.dom);
8206             return this;
8207         },
8208
8209         relayEvent : function(eventName, observable){
8210             this.on(eventName, function(e){
8211                 observable.fireEvent(eventName, e);
8212             });
8213         },
8214
8215         /**
8216          * Set the opacity of the element
8217          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
8218          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8219          * @return {Roo.Element} this
8220          */
8221          setOpacity : function(opacity, animate){
8222             if(!animate || !A){
8223                 var s = this.dom.style;
8224                 if(Roo.isIE){
8225                     s.zoom = 1;
8226                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
8227                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
8228                 }else{
8229                     s.opacity = opacity;
8230                 }
8231             }else{
8232                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
8233             }
8234             return this;
8235         },
8236
8237         /**
8238          * Gets the left X coordinate
8239          * @param {Boolean} local True to get the local css position instead of page coordinate
8240          * @return {Number}
8241          */
8242         getLeft : function(local){
8243             if(!local){
8244                 return this.getX();
8245             }else{
8246                 return parseInt(this.getStyle("left"), 10) || 0;
8247             }
8248         },
8249
8250         /**
8251          * Gets the right X coordinate of the element (element X position + element width)
8252          * @param {Boolean} local True to get the local css position instead of page coordinate
8253          * @return {Number}
8254          */
8255         getRight : function(local){
8256             if(!local){
8257                 return this.getX() + this.getWidth();
8258             }else{
8259                 return (this.getLeft(true) + this.getWidth()) || 0;
8260             }
8261         },
8262
8263         /**
8264          * Gets the top Y coordinate
8265          * @param {Boolean} local True to get the local css position instead of page coordinate
8266          * @return {Number}
8267          */
8268         getTop : function(local) {
8269             if(!local){
8270                 return this.getY();
8271             }else{
8272                 return parseInt(this.getStyle("top"), 10) || 0;
8273             }
8274         },
8275
8276         /**
8277          * Gets the bottom Y coordinate of the element (element Y position + element height)
8278          * @param {Boolean} local True to get the local css position instead of page coordinate
8279          * @return {Number}
8280          */
8281         getBottom : function(local){
8282             if(!local){
8283                 return this.getY() + this.getHeight();
8284             }else{
8285                 return (this.getTop(true) + this.getHeight()) || 0;
8286             }
8287         },
8288
8289         /**
8290         * Initializes positioning on this element. If a desired position is not passed, it will make the
8291         * the element positioned relative IF it is not already positioned.
8292         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
8293         * @param {Number} zIndex (optional) The zIndex to apply
8294         * @param {Number} x (optional) Set the page X position
8295         * @param {Number} y (optional) Set the page Y position
8296         */
8297         position : function(pos, zIndex, x, y){
8298             if(!pos){
8299                if(this.getStyle('position') == 'static'){
8300                    this.setStyle('position', 'relative');
8301                }
8302             }else{
8303                 this.setStyle("position", pos);
8304             }
8305             if(zIndex){
8306                 this.setStyle("z-index", zIndex);
8307             }
8308             if(x !== undefined && y !== undefined){
8309                 this.setXY([x, y]);
8310             }else if(x !== undefined){
8311                 this.setX(x);
8312             }else if(y !== undefined){
8313                 this.setY(y);
8314             }
8315         },
8316
8317         /**
8318         * Clear positioning back to the default when the document was loaded
8319         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
8320         * @return {Roo.Element} this
8321          */
8322         clearPositioning : function(value){
8323             value = value ||'';
8324             this.setStyle({
8325                 "left": value,
8326                 "right": value,
8327                 "top": value,
8328                 "bottom": value,
8329                 "z-index": "",
8330                 "position" : "static"
8331             });
8332             return this;
8333         },
8334
8335         /**
8336         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
8337         * snapshot before performing an update and then restoring the element.
8338         * @return {Object}
8339         */
8340         getPositioning : function(){
8341             var l = this.getStyle("left");
8342             var t = this.getStyle("top");
8343             return {
8344                 "position" : this.getStyle("position"),
8345                 "left" : l,
8346                 "right" : l ? "" : this.getStyle("right"),
8347                 "top" : t,
8348                 "bottom" : t ? "" : this.getStyle("bottom"),
8349                 "z-index" : this.getStyle("z-index")
8350             };
8351         },
8352
8353         /**
8354          * Gets the width of the border(s) for the specified side(s)
8355          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8356          * passing lr would get the border (l)eft width + the border (r)ight width.
8357          * @return {Number} The width of the sides passed added together
8358          */
8359         getBorderWidth : function(side){
8360             return this.addStyles(side, El.borders);
8361         },
8362
8363         /**
8364          * Gets the width of the padding(s) for the specified side(s)
8365          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8366          * passing lr would get the padding (l)eft + the padding (r)ight.
8367          * @return {Number} The padding of the sides passed added together
8368          */
8369         getPadding : function(side){
8370             return this.addStyles(side, El.paddings);
8371         },
8372
8373         /**
8374         * Set positioning with an object returned by getPositioning().
8375         * @param {Object} posCfg
8376         * @return {Roo.Element} this
8377          */
8378         setPositioning : function(pc){
8379             this.applyStyles(pc);
8380             if(pc.right == "auto"){
8381                 this.dom.style.right = "";
8382             }
8383             if(pc.bottom == "auto"){
8384                 this.dom.style.bottom = "";
8385             }
8386             return this;
8387         },
8388
8389         // private
8390         fixDisplay : function(){
8391             if(this.getStyle("display") == "none"){
8392                 this.setStyle("visibility", "hidden");
8393                 this.setStyle("display", this.originalDisplay); // first try reverting to default
8394                 if(this.getStyle("display") == "none"){ // if that fails, default to block
8395                     this.setStyle("display", "block");
8396                 }
8397             }
8398         },
8399
8400         /**
8401          * Quick set left and top adding default units
8402          * @param {String} left The left CSS property value
8403          * @param {String} top The top CSS property value
8404          * @return {Roo.Element} this
8405          */
8406          setLeftTop : function(left, top){
8407             this.dom.style.left = this.addUnits(left);
8408             this.dom.style.top = this.addUnits(top);
8409             return this;
8410         },
8411
8412         /**
8413          * Move this element relative to its current position.
8414          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
8415          * @param {Number} distance How far to move the element in pixels
8416          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8417          * @return {Roo.Element} this
8418          */
8419          move : function(direction, distance, animate){
8420             var xy = this.getXY();
8421             direction = direction.toLowerCase();
8422             switch(direction){
8423                 case "l":
8424                 case "left":
8425                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
8426                     break;
8427                case "r":
8428                case "right":
8429                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
8430                     break;
8431                case "t":
8432                case "top":
8433                case "up":
8434                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
8435                     break;
8436                case "b":
8437                case "bottom":
8438                case "down":
8439                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
8440                     break;
8441             }
8442             return this;
8443         },
8444
8445         /**
8446          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
8447          * @return {Roo.Element} this
8448          */
8449         clip : function(){
8450             if(!this.isClipped){
8451                this.isClipped = true;
8452                this.originalClip = {
8453                    "o": this.getStyle("overflow"),
8454                    "x": this.getStyle("overflow-x"),
8455                    "y": this.getStyle("overflow-y")
8456                };
8457                this.setStyle("overflow", "hidden");
8458                this.setStyle("overflow-x", "hidden");
8459                this.setStyle("overflow-y", "hidden");
8460             }
8461             return this;
8462         },
8463
8464         /**
8465          *  Return clipping (overflow) to original clipping before clip() was called
8466          * @return {Roo.Element} this
8467          */
8468         unclip : function(){
8469             if(this.isClipped){
8470                 this.isClipped = false;
8471                 var o = this.originalClip;
8472                 if(o.o){this.setStyle("overflow", o.o);}
8473                 if(o.x){this.setStyle("overflow-x", o.x);}
8474                 if(o.y){this.setStyle("overflow-y", o.y);}
8475             }
8476             return this;
8477         },
8478
8479
8480         /**
8481          * Gets the x,y coordinates specified by the anchor position on the element.
8482          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
8483          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
8484          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
8485          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
8486          * @return {Array} [x, y] An array containing the element's x and y coordinates
8487          */
8488         getAnchorXY : function(anchor, local, s){
8489             //Passing a different size is useful for pre-calculating anchors,
8490             //especially for anchored animations that change the el size.
8491
8492             var w, h, vp = false;
8493             if(!s){
8494                 var d = this.dom;
8495                 if(d == document.body || d == document){
8496                     vp = true;
8497                     w = D.getViewWidth(); h = D.getViewHeight();
8498                 }else{
8499                     w = this.getWidth(); h = this.getHeight();
8500                 }
8501             }else{
8502                 w = s.width;  h = s.height;
8503             }
8504             var x = 0, y = 0, r = Math.round;
8505             switch((anchor || "tl").toLowerCase()){
8506                 case "c":
8507                     x = r(w*.5);
8508                     y = r(h*.5);
8509                 break;
8510                 case "t":
8511                     x = r(w*.5);
8512                     y = 0;
8513                 break;
8514                 case "l":
8515                     x = 0;
8516                     y = r(h*.5);
8517                 break;
8518                 case "r":
8519                     x = w;
8520                     y = r(h*.5);
8521                 break;
8522                 case "b":
8523                     x = r(w*.5);
8524                     y = h;
8525                 break;
8526                 case "tl":
8527                     x = 0;
8528                     y = 0;
8529                 break;
8530                 case "bl":
8531                     x = 0;
8532                     y = h;
8533                 break;
8534                 case "br":
8535                     x = w;
8536                     y = h;
8537                 break;
8538                 case "tr":
8539                     x = w;
8540                     y = 0;
8541                 break;
8542             }
8543             if(local === true){
8544                 return [x, y];
8545             }
8546             if(vp){
8547                 var sc = this.getScroll();
8548                 return [x + sc.left, y + sc.top];
8549             }
8550             //Add the element's offset xy
8551             var o = this.getXY();
8552             return [x+o[0], y+o[1]];
8553         },
8554
8555         /**
8556          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
8557          * supported position values.
8558          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8559          * @param {String} position The position to align to.
8560          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8561          * @return {Array} [x, y]
8562          */
8563         getAlignToXY : function(el, p, o){
8564             el = Roo.get(el);
8565             var d = this.dom;
8566             if(!el.dom){
8567                 throw "Element.alignTo with an element that doesn't exist";
8568             }
8569             var c = false; //constrain to viewport
8570             var p1 = "", p2 = "";
8571             o = o || [0,0];
8572
8573             if(!p){
8574                 p = "tl-bl";
8575             }else if(p == "?"){
8576                 p = "tl-bl?";
8577             }else if(p.indexOf("-") == -1){
8578                 p = "tl-" + p;
8579             }
8580             p = p.toLowerCase();
8581             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
8582             if(!m){
8583                throw "Element.alignTo with an invalid alignment " + p;
8584             }
8585             p1 = m[1]; p2 = m[2]; c = !!m[3];
8586
8587             //Subtract the aligned el's internal xy from the target's offset xy
8588             //plus custom offset to get the aligned el's new offset xy
8589             var a1 = this.getAnchorXY(p1, true);
8590             var a2 = el.getAnchorXY(p2, false);
8591             var x = a2[0] - a1[0] + o[0];
8592             var y = a2[1] - a1[1] + o[1];
8593             if(c){
8594                 //constrain the aligned el to viewport if necessary
8595                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
8596                 // 5px of margin for ie
8597                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
8598
8599                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
8600                 //perpendicular to the vp border, allow the aligned el to slide on that border,
8601                 //otherwise swap the aligned el to the opposite border of the target.
8602                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
8603                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
8604                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
8605                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
8606
8607                var doc = document;
8608                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
8609                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
8610
8611                if((x+w) > dw + scrollX){
8612                     x = swapX ? r.left-w : dw+scrollX-w;
8613                 }
8614                if(x < scrollX){
8615                    x = swapX ? r.right : scrollX;
8616                }
8617                if((y+h) > dh + scrollY){
8618                     y = swapY ? r.top-h : dh+scrollY-h;
8619                 }
8620                if (y < scrollY){
8621                    y = swapY ? r.bottom : scrollY;
8622                }
8623             }
8624             return [x,y];
8625         },
8626
8627         // private
8628         getConstrainToXY : function(){
8629             var os = {top:0, left:0, bottom:0, right: 0};
8630
8631             return function(el, local, offsets, proposedXY){
8632                 el = Roo.get(el);
8633                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
8634
8635                 var vw, vh, vx = 0, vy = 0;
8636                 if(el.dom == document.body || el.dom == document){
8637                     vw = Roo.lib.Dom.getViewWidth();
8638                     vh = Roo.lib.Dom.getViewHeight();
8639                 }else{
8640                     vw = el.dom.clientWidth;
8641                     vh = el.dom.clientHeight;
8642                     if(!local){
8643                         var vxy = el.getXY();
8644                         vx = vxy[0];
8645                         vy = vxy[1];
8646                     }
8647                 }
8648
8649                 var s = el.getScroll();
8650
8651                 vx += offsets.left + s.left;
8652                 vy += offsets.top + s.top;
8653
8654                 vw -= offsets.right;
8655                 vh -= offsets.bottom;
8656
8657                 var vr = vx+vw;
8658                 var vb = vy+vh;
8659
8660                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
8661                 var x = xy[0], y = xy[1];
8662                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
8663
8664                 // only move it if it needs it
8665                 var moved = false;
8666
8667                 // first validate right/bottom
8668                 if((x + w) > vr){
8669                     x = vr - w;
8670                     moved = true;
8671                 }
8672                 if((y + h) > vb){
8673                     y = vb - h;
8674                     moved = true;
8675                 }
8676                 // then make sure top/left isn't negative
8677                 if(x < vx){
8678                     x = vx;
8679                     moved = true;
8680                 }
8681                 if(y < vy){
8682                     y = vy;
8683                     moved = true;
8684                 }
8685                 return moved ? [x, y] : false;
8686             };
8687         }(),
8688
8689         // private
8690         adjustForConstraints : function(xy, parent, offsets){
8691             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
8692         },
8693
8694         /**
8695          * Aligns this element with another element relative to the specified anchor points. If the other element is the
8696          * document it aligns it to the viewport.
8697          * The position parameter is optional, and can be specified in any one of the following formats:
8698          * <ul>
8699          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
8700          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
8701          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
8702          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
8703          *   <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
8704          *       element's anchor point, and the second value is used as the target's anchor point.</li>
8705          * </ul>
8706          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
8707          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
8708          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
8709          * that specified in order to enforce the viewport constraints.
8710          * Following are all of the supported anchor positions:
8711     <pre>
8712     Value  Description
8713     -----  -----------------------------
8714     tl     The top left corner (default)
8715     t      The center of the top edge
8716     tr     The top right corner
8717     l      The center of the left edge
8718     c      In the center of the element
8719     r      The center of the right edge
8720     bl     The bottom left corner
8721     b      The center of the bottom edge
8722     br     The bottom right corner
8723     </pre>
8724     Example Usage:
8725     <pre><code>
8726     // align el to other-el using the default positioning ("tl-bl", non-constrained)
8727     el.alignTo("other-el");
8728
8729     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
8730     el.alignTo("other-el", "tr?");
8731
8732     // align the bottom right corner of el with the center left edge of other-el
8733     el.alignTo("other-el", "br-l?");
8734
8735     // align the center of el with the bottom left corner of other-el and
8736     // adjust the x position by -6 pixels (and the y position by 0)
8737     el.alignTo("other-el", "c-bl", [-6, 0]);
8738     </code></pre>
8739          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8740          * @param {String} position The position to align to.
8741          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8742          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8743          * @return {Roo.Element} this
8744          */
8745         alignTo : function(element, position, offsets, animate){
8746             var xy = this.getAlignToXY(element, position, offsets);
8747             this.setXY(xy, this.preanim(arguments, 3));
8748             return this;
8749         },
8750
8751         /**
8752          * Anchors an element to another element and realigns it when the window is resized.
8753          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8754          * @param {String} position The position to align to.
8755          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8756          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
8757          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
8758          * is a number, it is used as the buffer delay (defaults to 50ms).
8759          * @param {Function} callback The function to call after the animation finishes
8760          * @return {Roo.Element} this
8761          */
8762         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
8763             var action = function(){
8764                 this.alignTo(el, alignment, offsets, animate);
8765                 Roo.callback(callback, this);
8766             };
8767             Roo.EventManager.onWindowResize(action, this);
8768             var tm = typeof monitorScroll;
8769             if(tm != 'undefined'){
8770                 Roo.EventManager.on(window, 'scroll', action, this,
8771                     {buffer: tm == 'number' ? monitorScroll : 50});
8772             }
8773             action.call(this); // align immediately
8774             return this;
8775         },
8776         /**
8777          * Clears any opacity settings from this element. Required in some cases for IE.
8778          * @return {Roo.Element} this
8779          */
8780         clearOpacity : function(){
8781             if (window.ActiveXObject) {
8782                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
8783                     this.dom.style.filter = "";
8784                 }
8785             } else {
8786                 this.dom.style.opacity = "";
8787                 this.dom.style["-moz-opacity"] = "";
8788                 this.dom.style["-khtml-opacity"] = "";
8789             }
8790             return this;
8791         },
8792
8793         /**
8794          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8795          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8796          * @return {Roo.Element} this
8797          */
8798         hide : function(animate){
8799             this.setVisible(false, this.preanim(arguments, 0));
8800             return this;
8801         },
8802
8803         /**
8804         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8805         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8806          * @return {Roo.Element} this
8807          */
8808         show : function(animate){
8809             this.setVisible(true, this.preanim(arguments, 0));
8810             return this;
8811         },
8812
8813         /**
8814          * @private Test if size has a unit, otherwise appends the default
8815          */
8816         addUnits : function(size){
8817             return Roo.Element.addUnits(size, this.defaultUnit);
8818         },
8819
8820         /**
8821          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
8822          * @return {Roo.Element} this
8823          */
8824         beginMeasure : function(){
8825             var el = this.dom;
8826             if(el.offsetWidth || el.offsetHeight){
8827                 return this; // offsets work already
8828             }
8829             var changed = [];
8830             var p = this.dom, b = document.body; // start with this element
8831             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
8832                 var pe = Roo.get(p);
8833                 if(pe.getStyle('display') == 'none'){
8834                     changed.push({el: p, visibility: pe.getStyle("visibility")});
8835                     p.style.visibility = "hidden";
8836                     p.style.display = "block";
8837                 }
8838                 p = p.parentNode;
8839             }
8840             this._measureChanged = changed;
8841             return this;
8842
8843         },
8844
8845         /**
8846          * Restores displays to before beginMeasure was called
8847          * @return {Roo.Element} this
8848          */
8849         endMeasure : function(){
8850             var changed = this._measureChanged;
8851             if(changed){
8852                 for(var i = 0, len = changed.length; i < len; i++) {
8853                     var r = changed[i];
8854                     r.el.style.visibility = r.visibility;
8855                     r.el.style.display = "none";
8856                 }
8857                 this._measureChanged = null;
8858             }
8859             return this;
8860         },
8861
8862         /**
8863         * Update the innerHTML of this element, optionally searching for and processing scripts
8864         * @param {String} html The new HTML
8865         * @param {Boolean} loadScripts (optional) true to look for and process scripts
8866         * @param {Function} callback For async script loading you can be noticed when the update completes
8867         * @return {Roo.Element} this
8868          */
8869         update : function(html, loadScripts, callback){
8870             if(typeof html == "undefined"){
8871                 html = "";
8872             }
8873             if(loadScripts !== true){
8874                 this.dom.innerHTML = html;
8875                 if(typeof callback == "function"){
8876                     callback();
8877                 }
8878                 return this;
8879             }
8880             var id = Roo.id();
8881             var dom = this.dom;
8882
8883             html += '<span id="' + id + '"></span>';
8884
8885             E.onAvailable(id, function(){
8886                 var hd = document.getElementsByTagName("head")[0];
8887                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
8888                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
8889                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
8890
8891                 var match;
8892                 while(match = re.exec(html)){
8893                     var attrs = match[1];
8894                     var srcMatch = attrs ? attrs.match(srcRe) : false;
8895                     if(srcMatch && srcMatch[2]){
8896                        var s = document.createElement("script");
8897                        s.src = srcMatch[2];
8898                        var typeMatch = attrs.match(typeRe);
8899                        if(typeMatch && typeMatch[2]){
8900                            s.type = typeMatch[2];
8901                        }
8902                        hd.appendChild(s);
8903                     }else if(match[2] && match[2].length > 0){
8904                         if(window.execScript) {
8905                            window.execScript(match[2]);
8906                         } else {
8907                             /**
8908                              * eval:var:id
8909                              * eval:var:dom
8910                              * eval:var:html
8911                              * 
8912                              */
8913                            window.eval(match[2]);
8914                         }
8915                     }
8916                 }
8917                 var el = document.getElementById(id);
8918                 if(el){el.parentNode.removeChild(el);}
8919                 if(typeof callback == "function"){
8920                     callback();
8921                 }
8922             });
8923             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
8924             return this;
8925         },
8926
8927         /**
8928          * Direct access to the UpdateManager update() method (takes the same parameters).
8929          * @param {String/Function} url The url for this request or a function to call to get the url
8930          * @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}
8931          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
8932          * @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.
8933          * @return {Roo.Element} this
8934          */
8935         load : function(){
8936             var um = this.getUpdateManager();
8937             um.update.apply(um, arguments);
8938             return this;
8939         },
8940
8941         /**
8942         * Gets this element's UpdateManager
8943         * @return {Roo.UpdateManager} The UpdateManager
8944         */
8945         getUpdateManager : function(){
8946             if(!this.updateManager){
8947                 this.updateManager = new Roo.UpdateManager(this);
8948             }
8949             return this.updateManager;
8950         },
8951
8952         /**
8953          * Disables text selection for this element (normalized across browsers)
8954          * @return {Roo.Element} this
8955          */
8956         unselectable : function(){
8957             this.dom.unselectable = "on";
8958             this.swallowEvent("selectstart", true);
8959             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
8960             this.addClass("x-unselectable");
8961             return this;
8962         },
8963
8964         /**
8965         * Calculates the x, y to center this element on the screen
8966         * @return {Array} The x, y values [x, y]
8967         */
8968         getCenterXY : function(){
8969             return this.getAlignToXY(document, 'c-c');
8970         },
8971
8972         /**
8973         * Centers the Element in either the viewport, or another Element.
8974         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
8975         */
8976         center : function(centerIn){
8977             this.alignTo(centerIn || document, 'c-c');
8978             return this;
8979         },
8980
8981         /**
8982          * Tests various css rules/browsers to determine if this element uses a border box
8983          * @return {Boolean}
8984          */
8985         isBorderBox : function(){
8986             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
8987         },
8988
8989         /**
8990          * Return a box {x, y, width, height} that can be used to set another elements
8991          * size/location to match this element.
8992          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
8993          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
8994          * @return {Object} box An object in the format {x, y, width, height}
8995          */
8996         getBox : function(contentBox, local){
8997             var xy;
8998             if(!local){
8999                 xy = this.getXY();
9000             }else{
9001                 var left = parseInt(this.getStyle("left"), 10) || 0;
9002                 var top = parseInt(this.getStyle("top"), 10) || 0;
9003                 xy = [left, top];
9004             }
9005             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
9006             if(!contentBox){
9007                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
9008             }else{
9009                 var l = this.getBorderWidth("l")+this.getPadding("l");
9010                 var r = this.getBorderWidth("r")+this.getPadding("r");
9011                 var t = this.getBorderWidth("t")+this.getPadding("t");
9012                 var b = this.getBorderWidth("b")+this.getPadding("b");
9013                 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)};
9014             }
9015             bx.right = bx.x + bx.width;
9016             bx.bottom = bx.y + bx.height;
9017             return bx;
9018         },
9019
9020         /**
9021          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
9022          for more information about the sides.
9023          * @param {String} sides
9024          * @return {Number}
9025          */
9026         getFrameWidth : function(sides, onlyContentBox){
9027             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
9028         },
9029
9030         /**
9031          * 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.
9032          * @param {Object} box The box to fill {x, y, width, height}
9033          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
9034          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9035          * @return {Roo.Element} this
9036          */
9037         setBox : function(box, adjust, animate){
9038             var w = box.width, h = box.height;
9039             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
9040                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
9041                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
9042             }
9043             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
9044             return this;
9045         },
9046
9047         /**
9048          * Forces the browser to repaint this element
9049          * @return {Roo.Element} this
9050          */
9051          repaint : function(){
9052             var dom = this.dom;
9053             this.addClass("x-repaint");
9054             setTimeout(function(){
9055                 Roo.get(dom).removeClass("x-repaint");
9056             }, 1);
9057             return this;
9058         },
9059
9060         /**
9061          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
9062          * then it returns the calculated width of the sides (see getPadding)
9063          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
9064          * @return {Object/Number}
9065          */
9066         getMargins : function(side){
9067             if(!side){
9068                 return {
9069                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
9070                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
9071                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
9072                     right: parseInt(this.getStyle("margin-right"), 10) || 0
9073                 };
9074             }else{
9075                 return this.addStyles(side, El.margins);
9076              }
9077         },
9078
9079         // private
9080         addStyles : function(sides, styles){
9081             var val = 0, v, w;
9082             for(var i = 0, len = sides.length; i < len; i++){
9083                 v = this.getStyle(styles[sides.charAt(i)]);
9084                 if(v){
9085                      w = parseInt(v, 10);
9086                      if(w){ val += w; }
9087                 }
9088             }
9089             return val;
9090         },
9091
9092         /**
9093          * Creates a proxy element of this element
9094          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
9095          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
9096          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
9097          * @return {Roo.Element} The new proxy element
9098          */
9099         createProxy : function(config, renderTo, matchBox){
9100             if(renderTo){
9101                 renderTo = Roo.getDom(renderTo);
9102             }else{
9103                 renderTo = document.body;
9104             }
9105             config = typeof config == "object" ?
9106                 config : {tag : "div", cls: config};
9107             var proxy = Roo.DomHelper.append(renderTo, config, true);
9108             if(matchBox){
9109                proxy.setBox(this.getBox());
9110             }
9111             return proxy;
9112         },
9113
9114         /**
9115          * Puts a mask over this element to disable user interaction. Requires core.css.
9116          * This method can only be applied to elements which accept child nodes.
9117          * @param {String} msg (optional) A message to display in the mask
9118          * @param {String} msgCls (optional) A css class to apply to the msg element
9119          * @return {Element} The mask  element
9120          */
9121         mask : function(msg, msgCls)
9122         {
9123             if(this.getStyle("position") == "static" && this.dom.tagName !== 'BODY'){
9124                 this.setStyle("position", "relative");
9125             }
9126             if(!this._mask){
9127                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
9128             }
9129             
9130             this.addClass("x-masked");
9131             this._mask.setDisplayed(true);
9132             
9133             // we wander
9134             var z = 0;
9135             var dom = this.dom;
9136             while (dom && dom.style) {
9137                 if (!isNaN(parseInt(dom.style.zIndex))) {
9138                     z = Math.max(z, parseInt(dom.style.zIndex));
9139                 }
9140                 dom = dom.parentNode;
9141             }
9142             // if we are masking the body - then it hides everything..
9143             if (this.dom == document.body) {
9144                 z = 1000000;
9145                 this._mask.setWidth(Roo.lib.Dom.getDocumentWidth());
9146                 this._mask.setHeight(Roo.lib.Dom.getDocumentHeight());
9147             }
9148            
9149             if(typeof msg == 'string'){
9150                 if(!this._maskMsg){
9151                     this._maskMsg = Roo.DomHelper.append(this.dom, {
9152                         cls: "roo-el-mask-msg", 
9153                         cn: [
9154                             {
9155                                 tag: 'i',
9156                                 cls: 'fa fa-spinner fa-spin'
9157                             },
9158                             {
9159                                 tag: 'div'
9160                             }   
9161                         ]
9162                     }, true);
9163                 }
9164                 var mm = this._maskMsg;
9165                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
9166                 if (mm.dom.lastChild) { // weird IE issue?
9167                     mm.dom.lastChild.innerHTML = msg;
9168                 }
9169                 mm.setDisplayed(true);
9170                 mm.center(this);
9171                 mm.setStyle('z-index', z + 102);
9172             }
9173             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
9174                 this._mask.setHeight(this.getHeight());
9175             }
9176             this._mask.setStyle('z-index', z + 100);
9177             
9178             return this._mask;
9179         },
9180
9181         /**
9182          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
9183          * it is cached for reuse.
9184          */
9185         unmask : function(removeEl){
9186             if(this._mask){
9187                 if(removeEl === true){
9188                     this._mask.remove();
9189                     delete this._mask;
9190                     if(this._maskMsg){
9191                         this._maskMsg.remove();
9192                         delete this._maskMsg;
9193                     }
9194                 }else{
9195                     this._mask.setDisplayed(false);
9196                     if(this._maskMsg){
9197                         this._maskMsg.setDisplayed(false);
9198                     }
9199                 }
9200             }
9201             this.removeClass("x-masked");
9202         },
9203
9204         /**
9205          * Returns true if this element is masked
9206          * @return {Boolean}
9207          */
9208         isMasked : function(){
9209             return this._mask && this._mask.isVisible();
9210         },
9211
9212         /**
9213          * Creates an iframe shim for this element to keep selects and other windowed objects from
9214          * showing through.
9215          * @return {Roo.Element} The new shim element
9216          */
9217         createShim : function(){
9218             var el = document.createElement('iframe');
9219             el.frameBorder = 'no';
9220             el.className = 'roo-shim';
9221             if(Roo.isIE && Roo.isSecure){
9222                 el.src = Roo.SSL_SECURE_URL;
9223             }
9224             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
9225             shim.autoBoxAdjust = false;
9226             return shim;
9227         },
9228
9229         /**
9230          * Removes this element from the DOM and deletes it from the cache
9231          */
9232         remove : function(){
9233             if(this.dom.parentNode){
9234                 this.dom.parentNode.removeChild(this.dom);
9235             }
9236             delete El.cache[this.dom.id];
9237         },
9238
9239         /**
9240          * Sets up event handlers to add and remove a css class when the mouse is over this element
9241          * @param {String} className
9242          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
9243          * mouseout events for children elements
9244          * @return {Roo.Element} this
9245          */
9246         addClassOnOver : function(className, preventFlicker){
9247             this.on("mouseover", function(){
9248                 Roo.fly(this, '_internal').addClass(className);
9249             }, this.dom);
9250             var removeFn = function(e){
9251                 if(preventFlicker !== true || !e.within(this, true)){
9252                     Roo.fly(this, '_internal').removeClass(className);
9253                 }
9254             };
9255             this.on("mouseout", removeFn, this.dom);
9256             return this;
9257         },
9258
9259         /**
9260          * Sets up event handlers to add and remove a css class when this element has the focus
9261          * @param {String} className
9262          * @return {Roo.Element} this
9263          */
9264         addClassOnFocus : function(className){
9265             this.on("focus", function(){
9266                 Roo.fly(this, '_internal').addClass(className);
9267             }, this.dom);
9268             this.on("blur", function(){
9269                 Roo.fly(this, '_internal').removeClass(className);
9270             }, this.dom);
9271             return this;
9272         },
9273         /**
9274          * 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)
9275          * @param {String} className
9276          * @return {Roo.Element} this
9277          */
9278         addClassOnClick : function(className){
9279             var dom = this.dom;
9280             this.on("mousedown", function(){
9281                 Roo.fly(dom, '_internal').addClass(className);
9282                 var d = Roo.get(document);
9283                 var fn = function(){
9284                     Roo.fly(dom, '_internal').removeClass(className);
9285                     d.removeListener("mouseup", fn);
9286                 };
9287                 d.on("mouseup", fn);
9288             });
9289             return this;
9290         },
9291
9292         /**
9293          * Stops the specified event from bubbling and optionally prevents the default action
9294          * @param {String} eventName
9295          * @param {Boolean} preventDefault (optional) true to prevent the default action too
9296          * @return {Roo.Element} this
9297          */
9298         swallowEvent : function(eventName, preventDefault){
9299             var fn = function(e){
9300                 e.stopPropagation();
9301                 if(preventDefault){
9302                     e.preventDefault();
9303                 }
9304             };
9305             if(eventName instanceof Array){
9306                 for(var i = 0, len = eventName.length; i < len; i++){
9307                      this.on(eventName[i], fn);
9308                 }
9309                 return this;
9310             }
9311             this.on(eventName, fn);
9312             return this;
9313         },
9314
9315         /**
9316          * @private
9317          */
9318       fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
9319
9320         /**
9321          * Sizes this element to its parent element's dimensions performing
9322          * neccessary box adjustments.
9323          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
9324          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
9325          * @return {Roo.Element} this
9326          */
9327         fitToParent : function(monitorResize, targetParent) {
9328           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
9329           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
9330           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
9331             return;
9332           }
9333           var p = Roo.get(targetParent || this.dom.parentNode);
9334           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
9335           if (monitorResize === true) {
9336             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
9337             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
9338           }
9339           return this;
9340         },
9341
9342         /**
9343          * Gets the next sibling, skipping text nodes
9344          * @return {HTMLElement} The next sibling or null
9345          */
9346         getNextSibling : function(){
9347             var n = this.dom.nextSibling;
9348             while(n && n.nodeType != 1){
9349                 n = n.nextSibling;
9350             }
9351             return n;
9352         },
9353
9354         /**
9355          * Gets the previous sibling, skipping text nodes
9356          * @return {HTMLElement} The previous sibling or null
9357          */
9358         getPrevSibling : function(){
9359             var n = this.dom.previousSibling;
9360             while(n && n.nodeType != 1){
9361                 n = n.previousSibling;
9362             }
9363             return n;
9364         },
9365
9366
9367         /**
9368          * Appends the passed element(s) to this element
9369          * @param {String/HTMLElement/Array/Element/CompositeElement} el
9370          * @return {Roo.Element} this
9371          */
9372         appendChild: function(el){
9373             el = Roo.get(el);
9374             el.appendTo(this);
9375             return this;
9376         },
9377
9378         /**
9379          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
9380          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
9381          * automatically generated with the specified attributes.
9382          * @param {HTMLElement} insertBefore (optional) a child element of this element
9383          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
9384          * @return {Roo.Element} The new child element
9385          */
9386         createChild: function(config, insertBefore, returnDom){
9387             config = config || {tag:'div'};
9388             if(insertBefore){
9389                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
9390             }
9391             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
9392         },
9393
9394         /**
9395          * Appends this element to the passed element
9396          * @param {String/HTMLElement/Element} el The new parent element
9397          * @return {Roo.Element} this
9398          */
9399         appendTo: function(el){
9400             el = Roo.getDom(el);
9401             el.appendChild(this.dom);
9402             return this;
9403         },
9404
9405         /**
9406          * Inserts this element before the passed element in the DOM
9407          * @param {String/HTMLElement/Element} el The element to insert before
9408          * @return {Roo.Element} this
9409          */
9410         insertBefore: function(el){
9411             el = Roo.getDom(el);
9412             el.parentNode.insertBefore(this.dom, el);
9413             return this;
9414         },
9415
9416         /**
9417          * Inserts this element after the passed element in the DOM
9418          * @param {String/HTMLElement/Element} el The element to insert after
9419          * @return {Roo.Element} this
9420          */
9421         insertAfter: function(el){
9422             el = Roo.getDom(el);
9423             el.parentNode.insertBefore(this.dom, el.nextSibling);
9424             return this;
9425         },
9426
9427         /**
9428          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
9429          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9430          * @return {Roo.Element} The new child
9431          */
9432         insertFirst: function(el, returnDom){
9433             el = el || {};
9434             if(typeof el == 'object' && !el.nodeType){ // dh config
9435                 return this.createChild(el, this.dom.firstChild, returnDom);
9436             }else{
9437                 el = Roo.getDom(el);
9438                 this.dom.insertBefore(el, this.dom.firstChild);
9439                 return !returnDom ? Roo.get(el) : el;
9440             }
9441         },
9442
9443         /**
9444          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
9445          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9446          * @param {String} where (optional) 'before' or 'after' defaults to before
9447          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9448          * @return {Roo.Element} the inserted Element
9449          */
9450         insertSibling: function(el, where, returnDom){
9451             where = where ? where.toLowerCase() : 'before';
9452             el = el || {};
9453             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
9454
9455             if(typeof el == 'object' && !el.nodeType){ // dh config
9456                 if(where == 'after' && !this.dom.nextSibling){
9457                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
9458                 }else{
9459                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
9460                 }
9461
9462             }else{
9463                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
9464                             where == 'before' ? this.dom : this.dom.nextSibling);
9465                 if(!returnDom){
9466                     rt = Roo.get(rt);
9467                 }
9468             }
9469             return rt;
9470         },
9471
9472         /**
9473          * Creates and wraps this element with another element
9474          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
9475          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9476          * @return {HTMLElement/Element} The newly created wrapper element
9477          */
9478         wrap: function(config, returnDom){
9479             if(!config){
9480                 config = {tag: "div"};
9481             }
9482             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
9483             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
9484             return newEl;
9485         },
9486
9487         /**
9488          * Replaces the passed element with this element
9489          * @param {String/HTMLElement/Element} el The element to replace
9490          * @return {Roo.Element} this
9491          */
9492         replace: function(el){
9493             el = Roo.get(el);
9494             this.insertBefore(el);
9495             el.remove();
9496             return this;
9497         },
9498
9499         /**
9500          * Inserts an html fragment into this element
9501          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
9502          * @param {String} html The HTML fragment
9503          * @param {Boolean} returnEl True to return an Roo.Element
9504          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
9505          */
9506         insertHtml : function(where, html, returnEl){
9507             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
9508             return returnEl ? Roo.get(el) : el;
9509         },
9510
9511         /**
9512          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
9513          * @param {Object} o The object with the attributes
9514          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
9515          * @return {Roo.Element} this
9516          */
9517         set : function(o, useSet){
9518             var el = this.dom;
9519             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
9520             for(var attr in o){
9521                 if(attr == "style" || typeof o[attr] == "function")  { continue; }
9522                 if(attr=="cls"){
9523                     el.className = o["cls"];
9524                 }else{
9525                     if(useSet) {
9526                         el.setAttribute(attr, o[attr]);
9527                     } else {
9528                         el[attr] = o[attr];
9529                     }
9530                 }
9531             }
9532             if(o.style){
9533                 Roo.DomHelper.applyStyles(el, o.style);
9534             }
9535             return this;
9536         },
9537
9538         /**
9539          * Convenience method for constructing a KeyMap
9540          * @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:
9541          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9542          * @param {Function} fn The function to call
9543          * @param {Object} scope (optional) The scope of the function
9544          * @return {Roo.KeyMap} The KeyMap created
9545          */
9546         addKeyListener : function(key, fn, scope){
9547             var config;
9548             if(typeof key != "object" || key instanceof Array){
9549                 config = {
9550                     key: key,
9551                     fn: fn,
9552                     scope: scope
9553                 };
9554             }else{
9555                 config = {
9556                     key : key.key,
9557                     shift : key.shift,
9558                     ctrl : key.ctrl,
9559                     alt : key.alt,
9560                     fn: fn,
9561                     scope: scope
9562                 };
9563             }
9564             return new Roo.KeyMap(this, config);
9565         },
9566
9567         /**
9568          * Creates a KeyMap for this element
9569          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
9570          * @return {Roo.KeyMap} The KeyMap created
9571          */
9572         addKeyMap : function(config){
9573             return new Roo.KeyMap(this, config);
9574         },
9575
9576         /**
9577          * Returns true if this element is scrollable.
9578          * @return {Boolean}
9579          */
9580          isScrollable : function(){
9581             var dom = this.dom;
9582             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
9583         },
9584
9585         /**
9586          * 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().
9587          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
9588          * @param {Number} value The new scroll value
9589          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9590          * @return {Element} this
9591          */
9592
9593         scrollTo : function(side, value, animate){
9594             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
9595             if(!animate || !A){
9596                 this.dom[prop] = value;
9597             }else{
9598                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
9599                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
9600             }
9601             return this;
9602         },
9603
9604         /**
9605          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
9606          * within this element's scrollable range.
9607          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9608          * @param {Number} distance How far to scroll the element in pixels
9609          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9610          * @return {Boolean} Returns true if a scroll was triggered or false if the element
9611          * was scrolled as far as it could go.
9612          */
9613          scroll : function(direction, distance, animate){
9614              if(!this.isScrollable()){
9615                  return;
9616              }
9617              var el = this.dom;
9618              var l = el.scrollLeft, t = el.scrollTop;
9619              var w = el.scrollWidth, h = el.scrollHeight;
9620              var cw = el.clientWidth, ch = el.clientHeight;
9621              direction = direction.toLowerCase();
9622              var scrolled = false;
9623              var a = this.preanim(arguments, 2);
9624              switch(direction){
9625                  case "l":
9626                  case "left":
9627                      if(w - l > cw){
9628                          var v = Math.min(l + distance, w-cw);
9629                          this.scrollTo("left", v, a);
9630                          scrolled = true;
9631                      }
9632                      break;
9633                 case "r":
9634                 case "right":
9635                      if(l > 0){
9636                          var v = Math.max(l - distance, 0);
9637                          this.scrollTo("left", v, a);
9638                          scrolled = true;
9639                      }
9640                      break;
9641                 case "t":
9642                 case "top":
9643                 case "up":
9644                      if(t > 0){
9645                          var v = Math.max(t - distance, 0);
9646                          this.scrollTo("top", v, a);
9647                          scrolled = true;
9648                      }
9649                      break;
9650                 case "b":
9651                 case "bottom":
9652                 case "down":
9653                      if(h - t > ch){
9654                          var v = Math.min(t + distance, h-ch);
9655                          this.scrollTo("top", v, a);
9656                          scrolled = true;
9657                      }
9658                      break;
9659              }
9660              return scrolled;
9661         },
9662
9663         /**
9664          * Translates the passed page coordinates into left/top css values for this element
9665          * @param {Number/Array} x The page x or an array containing [x, y]
9666          * @param {Number} y The page y
9667          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
9668          */
9669         translatePoints : function(x, y){
9670             if(typeof x == 'object' || x instanceof Array){
9671                 y = x[1]; x = x[0];
9672             }
9673             var p = this.getStyle('position');
9674             var o = this.getXY();
9675
9676             var l = parseInt(this.getStyle('left'), 10);
9677             var t = parseInt(this.getStyle('top'), 10);
9678
9679             if(isNaN(l)){
9680                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
9681             }
9682             if(isNaN(t)){
9683                 t = (p == "relative") ? 0 : this.dom.offsetTop;
9684             }
9685
9686             return {left: (x - o[0] + l), top: (y - o[1] + t)};
9687         },
9688
9689         /**
9690          * Returns the current scroll position of the element.
9691          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
9692          */
9693         getScroll : function(){
9694             var d = this.dom, doc = document;
9695             if(d == doc || d == doc.body){
9696                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
9697                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
9698                 return {left: l, top: t};
9699             }else{
9700                 return {left: d.scrollLeft, top: d.scrollTop};
9701             }
9702         },
9703
9704         /**
9705          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
9706          * are convert to standard 6 digit hex color.
9707          * @param {String} attr The css attribute
9708          * @param {String} defaultValue The default value to use when a valid color isn't found
9709          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
9710          * YUI color anims.
9711          */
9712         getColor : function(attr, defaultValue, prefix){
9713             var v = this.getStyle(attr);
9714             if(!v || v == "transparent" || v == "inherit") {
9715                 return defaultValue;
9716             }
9717             var color = typeof prefix == "undefined" ? "#" : prefix;
9718             if(v.substr(0, 4) == "rgb("){
9719                 var rvs = v.slice(4, v.length -1).split(",");
9720                 for(var i = 0; i < 3; i++){
9721                     var h = parseInt(rvs[i]).toString(16);
9722                     if(h < 16){
9723                         h = "0" + h;
9724                     }
9725                     color += h;
9726                 }
9727             } else {
9728                 if(v.substr(0, 1) == "#"){
9729                     if(v.length == 4) {
9730                         for(var i = 1; i < 4; i++){
9731                             var c = v.charAt(i);
9732                             color +=  c + c;
9733                         }
9734                     }else if(v.length == 7){
9735                         color += v.substr(1);
9736                     }
9737                 }
9738             }
9739             return(color.length > 5 ? color.toLowerCase() : defaultValue);
9740         },
9741
9742         /**
9743          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
9744          * gradient background, rounded corners and a 4-way shadow.
9745          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
9746          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
9747          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
9748          * @return {Roo.Element} this
9749          */
9750         boxWrap : function(cls){
9751             cls = cls || 'x-box';
9752             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
9753             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
9754             return el;
9755         },
9756
9757         /**
9758          * Returns the value of a namespaced attribute from the element's underlying DOM node.
9759          * @param {String} namespace The namespace in which to look for the attribute
9760          * @param {String} name The attribute name
9761          * @return {String} The attribute value
9762          */
9763         getAttributeNS : Roo.isIE ? function(ns, name){
9764             var d = this.dom;
9765             var type = typeof d[ns+":"+name];
9766             if(type != 'undefined' && type != 'unknown'){
9767                 return d[ns+":"+name];
9768             }
9769             return d[name];
9770         } : function(ns, name){
9771             var d = this.dom;
9772             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
9773         },
9774         
9775         
9776         /**
9777          * Sets or Returns the value the dom attribute value
9778          * @param {String|Object} name The attribute name (or object to set multiple attributes)
9779          * @param {String} value (optional) The value to set the attribute to
9780          * @return {String} The attribute value
9781          */
9782         attr : function(name){
9783             if (arguments.length > 1) {
9784                 this.dom.setAttribute(name, arguments[1]);
9785                 return arguments[1];
9786             }
9787             if (typeof(name) == 'object') {
9788                 for(var i in name) {
9789                     this.attr(i, name[i]);
9790                 }
9791                 return name;
9792             }
9793             
9794             
9795             if (!this.dom.hasAttribute(name)) {
9796                 return undefined;
9797             }
9798             return this.dom.getAttribute(name);
9799         }
9800         
9801         
9802         
9803     };
9804
9805     var ep = El.prototype;
9806
9807     /**
9808      * Appends an event handler (Shorthand for addListener)
9809      * @param {String}   eventName     The type of event to append
9810      * @param {Function} fn        The method the event invokes
9811      * @param {Object} scope       (optional) The scope (this object) of the fn
9812      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9813      * @method
9814      */
9815     ep.on = ep.addListener;
9816         // backwards compat
9817     ep.mon = ep.addListener;
9818
9819     /**
9820      * Removes an event handler from this element (shorthand for removeListener)
9821      * @param {String} eventName the type of event to remove
9822      * @param {Function} fn the method the event invokes
9823      * @return {Roo.Element} this
9824      * @method
9825      */
9826     ep.un = ep.removeListener;
9827
9828     /**
9829      * true to automatically adjust width and height settings for box-model issues (default to true)
9830      */
9831     ep.autoBoxAdjust = true;
9832
9833     // private
9834     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
9835
9836     // private
9837     El.addUnits = function(v, defaultUnit){
9838         if(v === "" || v == "auto"){
9839             return v;
9840         }
9841         if(v === undefined){
9842             return '';
9843         }
9844         if(typeof v == "number" || !El.unitPattern.test(v)){
9845             return v + (defaultUnit || 'px');
9846         }
9847         return v;
9848     };
9849
9850     // special markup used throughout Roo when box wrapping elements
9851     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>';
9852     /**
9853      * Visibility mode constant - Use visibility to hide element
9854      * @static
9855      * @type Number
9856      */
9857     El.VISIBILITY = 1;
9858     /**
9859      * Visibility mode constant - Use display to hide element
9860      * @static
9861      * @type Number
9862      */
9863     El.DISPLAY = 2;
9864
9865     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
9866     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
9867     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
9868
9869
9870
9871     /**
9872      * @private
9873      */
9874     El.cache = {};
9875
9876     var docEl;
9877
9878     /**
9879      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9880      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9881      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9882      * @return {Element} The Element object
9883      * @static
9884      */
9885     El.get = function(el){
9886         var ex, elm, id;
9887         if(!el){ return null; }
9888         if(typeof el == "string"){ // element id
9889             if(!(elm = document.getElementById(el))){
9890                 return null;
9891             }
9892             if(ex = El.cache[el]){
9893                 ex.dom = elm;
9894             }else{
9895                 ex = El.cache[el] = new El(elm);
9896             }
9897             return ex;
9898         }else if(el.tagName){ // dom element
9899             if(!(id = el.id)){
9900                 id = Roo.id(el);
9901             }
9902             if(ex = El.cache[id]){
9903                 ex.dom = el;
9904             }else{
9905                 ex = El.cache[id] = new El(el);
9906             }
9907             return ex;
9908         }else if(el instanceof El){
9909             if(el != docEl){
9910                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
9911                                                               // catch case where it hasn't been appended
9912                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
9913             }
9914             return el;
9915         }else if(el.isComposite){
9916             return el;
9917         }else if(el instanceof Array){
9918             return El.select(el);
9919         }else if(el == document){
9920             // create a bogus element object representing the document object
9921             if(!docEl){
9922                 var f = function(){};
9923                 f.prototype = El.prototype;
9924                 docEl = new f();
9925                 docEl.dom = document;
9926             }
9927             return docEl;
9928         }
9929         return null;
9930     };
9931
9932     // private
9933     El.uncache = function(el){
9934         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
9935             if(a[i]){
9936                 delete El.cache[a[i].id || a[i]];
9937             }
9938         }
9939     };
9940
9941     // private
9942     // Garbage collection - uncache elements/purge listeners on orphaned elements
9943     // so we don't hold a reference and cause the browser to retain them
9944     El.garbageCollect = function(){
9945         if(!Roo.enableGarbageCollector){
9946             clearInterval(El.collectorThread);
9947             return;
9948         }
9949         for(var eid in El.cache){
9950             var el = El.cache[eid], d = el.dom;
9951             // -------------------------------------------------------
9952             // Determining what is garbage:
9953             // -------------------------------------------------------
9954             // !d
9955             // dom node is null, definitely garbage
9956             // -------------------------------------------------------
9957             // !d.parentNode
9958             // no parentNode == direct orphan, definitely garbage
9959             // -------------------------------------------------------
9960             // !d.offsetParent && !document.getElementById(eid)
9961             // display none elements have no offsetParent so we will
9962             // also try to look it up by it's id. However, check
9963             // offsetParent first so we don't do unneeded lookups.
9964             // This enables collection of elements that are not orphans
9965             // directly, but somewhere up the line they have an orphan
9966             // parent.
9967             // -------------------------------------------------------
9968             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
9969                 delete El.cache[eid];
9970                 if(d && Roo.enableListenerCollection){
9971                     E.purgeElement(d);
9972                 }
9973             }
9974         }
9975     }
9976     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
9977
9978
9979     // dom is optional
9980     El.Flyweight = function(dom){
9981         this.dom = dom;
9982     };
9983     El.Flyweight.prototype = El.prototype;
9984
9985     El._flyweights = {};
9986     /**
9987      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9988      * the dom node can be overwritten by other code.
9989      * @param {String/HTMLElement} el The dom node or id
9990      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9991      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9992      * @static
9993      * @return {Element} The shared Element object
9994      */
9995     El.fly = function(el, named){
9996         named = named || '_global';
9997         el = Roo.getDom(el);
9998         if(!el){
9999             return null;
10000         }
10001         if(!El._flyweights[named]){
10002             El._flyweights[named] = new El.Flyweight();
10003         }
10004         El._flyweights[named].dom = el;
10005         return El._flyweights[named];
10006     };
10007
10008     /**
10009      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
10010      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
10011      * Shorthand of {@link Roo.Element#get}
10012      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
10013      * @return {Element} The Element object
10014      * @member Roo
10015      * @method get
10016      */
10017     Roo.get = El.get;
10018     /**
10019      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
10020      * the dom node can be overwritten by other code.
10021      * Shorthand of {@link Roo.Element#fly}
10022      * @param {String/HTMLElement} el The dom node or id
10023      * @param {String} named (optional) Allows for creation of named reusable flyweights to
10024      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
10025      * @static
10026      * @return {Element} The shared Element object
10027      * @member Roo
10028      * @method fly
10029      */
10030     Roo.fly = El.fly;
10031
10032     // speedy lookup for elements never to box adjust
10033     var noBoxAdjust = Roo.isStrict ? {
10034         select:1
10035     } : {
10036         input:1, select:1, textarea:1
10037     };
10038     if(Roo.isIE || Roo.isGecko){
10039         noBoxAdjust['button'] = 1;
10040     }
10041
10042
10043     Roo.EventManager.on(window, 'unload', function(){
10044         delete El.cache;
10045         delete El._flyweights;
10046     });
10047 })();
10048
10049
10050
10051
10052 if(Roo.DomQuery){
10053     Roo.Element.selectorFunction = Roo.DomQuery.select;
10054 }
10055
10056 Roo.Element.select = function(selector, unique, root){
10057     var els;
10058     if(typeof selector == "string"){
10059         els = Roo.Element.selectorFunction(selector, root);
10060     }else if(selector.length !== undefined){
10061         els = selector;
10062     }else{
10063         throw "Invalid selector";
10064     }
10065     if(unique === true){
10066         return new Roo.CompositeElement(els);
10067     }else{
10068         return new Roo.CompositeElementLite(els);
10069     }
10070 };
10071 /**
10072  * Selects elements based on the passed CSS selector to enable working on them as 1.
10073  * @param {String/Array} selector The CSS selector or an array of elements
10074  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
10075  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
10076  * @return {CompositeElementLite/CompositeElement}
10077  * @member Roo
10078  * @method select
10079  */
10080 Roo.select = Roo.Element.select;
10081
10082
10083
10084
10085
10086
10087
10088
10089
10090
10091
10092
10093
10094
10095 /*
10096  * Based on:
10097  * Ext JS Library 1.1.1
10098  * Copyright(c) 2006-2007, Ext JS, LLC.
10099  *
10100  * Originally Released Under LGPL - original licence link has changed is not relivant.
10101  *
10102  * Fork - LGPL
10103  * <script type="text/javascript">
10104  */
10105
10106
10107
10108 //Notifies Element that fx methods are available
10109 Roo.enableFx = true;
10110
10111 /**
10112  * @class Roo.Fx
10113  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
10114  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
10115  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
10116  * Element effects to work.</p><br/>
10117  *
10118  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
10119  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
10120  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
10121  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
10122  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
10123  * expected results and should be done with care.</p><br/>
10124  *
10125  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
10126  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
10127 <pre>
10128 Value  Description
10129 -----  -----------------------------
10130 tl     The top left corner
10131 t      The center of the top edge
10132 tr     The top right corner
10133 l      The center of the left edge
10134 r      The center of the right edge
10135 bl     The bottom left corner
10136 b      The center of the bottom edge
10137 br     The bottom right corner
10138 </pre>
10139  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
10140  * below are common options that can be passed to any Fx method.</b>
10141  * @cfg {Function} callback A function called when the effect is finished
10142  * @cfg {Object} scope The scope of the effect function
10143  * @cfg {String} easing A valid Easing value for the effect
10144  * @cfg {String} afterCls A css class to apply after the effect
10145  * @cfg {Number} duration The length of time (in seconds) that the effect should last
10146  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
10147  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
10148  * effects that end with the element being visually hidden, ignored otherwise)
10149  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
10150  * a function which returns such a specification that will be applied to the Element after the effect finishes
10151  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
10152  * @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
10153  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
10154  */
10155 Roo.Fx = {
10156         /**
10157          * Slides the element into view.  An anchor point can be optionally passed to set the point of
10158          * origin for the slide effect.  This function automatically handles wrapping the element with
10159          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10160          * Usage:
10161          *<pre><code>
10162 // default: slide the element in from the top
10163 el.slideIn();
10164
10165 // custom: slide the element in from the right with a 2-second duration
10166 el.slideIn('r', { duration: 2 });
10167
10168 // common config options shown with default values
10169 el.slideIn('t', {
10170     easing: 'easeOut',
10171     duration: .5
10172 });
10173 </code></pre>
10174          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10175          * @param {Object} options (optional) Object literal with any of the Fx config options
10176          * @return {Roo.Element} The Element
10177          */
10178     slideIn : function(anchor, o){
10179         var el = this.getFxEl();
10180         o = o || {};
10181
10182         el.queueFx(o, function(){
10183
10184             anchor = anchor || "t";
10185
10186             // fix display to visibility
10187             this.fixDisplay();
10188
10189             // restore values after effect
10190             var r = this.getFxRestore();
10191             var b = this.getBox();
10192             // fixed size for slide
10193             this.setSize(b);
10194
10195             // wrap if needed
10196             var wrap = this.fxWrap(r.pos, o, "hidden");
10197
10198             var st = this.dom.style;
10199             st.visibility = "visible";
10200             st.position = "absolute";
10201
10202             // clear out temp styles after slide and unwrap
10203             var after = function(){
10204                 el.fxUnwrap(wrap, r.pos, o);
10205                 st.width = r.width;
10206                 st.height = r.height;
10207                 el.afterFx(o);
10208             };
10209             // time to calc the positions
10210             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
10211
10212             switch(anchor.toLowerCase()){
10213                 case "t":
10214                     wrap.setSize(b.width, 0);
10215                     st.left = st.bottom = "0";
10216                     a = {height: bh};
10217                 break;
10218                 case "l":
10219                     wrap.setSize(0, b.height);
10220                     st.right = st.top = "0";
10221                     a = {width: bw};
10222                 break;
10223                 case "r":
10224                     wrap.setSize(0, b.height);
10225                     wrap.setX(b.right);
10226                     st.left = st.top = "0";
10227                     a = {width: bw, points: pt};
10228                 break;
10229                 case "b":
10230                     wrap.setSize(b.width, 0);
10231                     wrap.setY(b.bottom);
10232                     st.left = st.top = "0";
10233                     a = {height: bh, points: pt};
10234                 break;
10235                 case "tl":
10236                     wrap.setSize(0, 0);
10237                     st.right = st.bottom = "0";
10238                     a = {width: bw, height: bh};
10239                 break;
10240                 case "bl":
10241                     wrap.setSize(0, 0);
10242                     wrap.setY(b.y+b.height);
10243                     st.right = st.top = "0";
10244                     a = {width: bw, height: bh, points: pt};
10245                 break;
10246                 case "br":
10247                     wrap.setSize(0, 0);
10248                     wrap.setXY([b.right, b.bottom]);
10249                     st.left = st.top = "0";
10250                     a = {width: bw, height: bh, points: pt};
10251                 break;
10252                 case "tr":
10253                     wrap.setSize(0, 0);
10254                     wrap.setX(b.x+b.width);
10255                     st.left = st.bottom = "0";
10256                     a = {width: bw, height: bh, points: pt};
10257                 break;
10258             }
10259             this.dom.style.visibility = "visible";
10260             wrap.show();
10261
10262             arguments.callee.anim = wrap.fxanim(a,
10263                 o,
10264                 'motion',
10265                 .5,
10266                 'easeOut', after);
10267         });
10268         return this;
10269     },
10270     
10271         /**
10272          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
10273          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
10274          * 'hidden') but block elements will still take up space in the document.  The element must be removed
10275          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
10276          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10277          * Usage:
10278          *<pre><code>
10279 // default: slide the element out to the top
10280 el.slideOut();
10281
10282 // custom: slide the element out to the right with a 2-second duration
10283 el.slideOut('r', { duration: 2 });
10284
10285 // common config options shown with default values
10286 el.slideOut('t', {
10287     easing: 'easeOut',
10288     duration: .5,
10289     remove: false,
10290     useDisplay: false
10291 });
10292 </code></pre>
10293          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10294          * @param {Object} options (optional) Object literal with any of the Fx config options
10295          * @return {Roo.Element} The Element
10296          */
10297     slideOut : function(anchor, o){
10298         var el = this.getFxEl();
10299         o = o || {};
10300
10301         el.queueFx(o, function(){
10302
10303             anchor = anchor || "t";
10304
10305             // restore values after effect
10306             var r = this.getFxRestore();
10307             
10308             var b = this.getBox();
10309             // fixed size for slide
10310             this.setSize(b);
10311
10312             // wrap if needed
10313             var wrap = this.fxWrap(r.pos, o, "visible");
10314
10315             var st = this.dom.style;
10316             st.visibility = "visible";
10317             st.position = "absolute";
10318
10319             wrap.setSize(b);
10320
10321             var after = function(){
10322                 if(o.useDisplay){
10323                     el.setDisplayed(false);
10324                 }else{
10325                     el.hide();
10326                 }
10327
10328                 el.fxUnwrap(wrap, r.pos, o);
10329
10330                 st.width = r.width;
10331                 st.height = r.height;
10332
10333                 el.afterFx(o);
10334             };
10335
10336             var a, zero = {to: 0};
10337             switch(anchor.toLowerCase()){
10338                 case "t":
10339                     st.left = st.bottom = "0";
10340                     a = {height: zero};
10341                 break;
10342                 case "l":
10343                     st.right = st.top = "0";
10344                     a = {width: zero};
10345                 break;
10346                 case "r":
10347                     st.left = st.top = "0";
10348                     a = {width: zero, points: {to:[b.right, b.y]}};
10349                 break;
10350                 case "b":
10351                     st.left = st.top = "0";
10352                     a = {height: zero, points: {to:[b.x, b.bottom]}};
10353                 break;
10354                 case "tl":
10355                     st.right = st.bottom = "0";
10356                     a = {width: zero, height: zero};
10357                 break;
10358                 case "bl":
10359                     st.right = st.top = "0";
10360                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
10361                 break;
10362                 case "br":
10363                     st.left = st.top = "0";
10364                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
10365                 break;
10366                 case "tr":
10367                     st.left = st.bottom = "0";
10368                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
10369                 break;
10370             }
10371
10372             arguments.callee.anim = wrap.fxanim(a,
10373                 o,
10374                 'motion',
10375                 .5,
10376                 "easeOut", after);
10377         });
10378         return this;
10379     },
10380
10381         /**
10382          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
10383          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
10384          * The element must be removed from the DOM using the 'remove' config option if desired.
10385          * Usage:
10386          *<pre><code>
10387 // default
10388 el.puff();
10389
10390 // common config options shown with default values
10391 el.puff({
10392     easing: 'easeOut',
10393     duration: .5,
10394     remove: false,
10395     useDisplay: false
10396 });
10397 </code></pre>
10398          * @param {Object} options (optional) Object literal with any of the Fx config options
10399          * @return {Roo.Element} The Element
10400          */
10401     puff : function(o){
10402         var el = this.getFxEl();
10403         o = o || {};
10404
10405         el.queueFx(o, function(){
10406             this.clearOpacity();
10407             this.show();
10408
10409             // restore values after effect
10410             var r = this.getFxRestore();
10411             var st = this.dom.style;
10412
10413             var after = function(){
10414                 if(o.useDisplay){
10415                     el.setDisplayed(false);
10416                 }else{
10417                     el.hide();
10418                 }
10419
10420                 el.clearOpacity();
10421
10422                 el.setPositioning(r.pos);
10423                 st.width = r.width;
10424                 st.height = r.height;
10425                 st.fontSize = '';
10426                 el.afterFx(o);
10427             };
10428
10429             var width = this.getWidth();
10430             var height = this.getHeight();
10431
10432             arguments.callee.anim = this.fxanim({
10433                     width : {to: this.adjustWidth(width * 2)},
10434                     height : {to: this.adjustHeight(height * 2)},
10435                     points : {by: [-(width * .5), -(height * .5)]},
10436                     opacity : {to: 0},
10437                     fontSize: {to:200, unit: "%"}
10438                 },
10439                 o,
10440                 'motion',
10441                 .5,
10442                 "easeOut", after);
10443         });
10444         return this;
10445     },
10446
10447         /**
10448          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
10449          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
10450          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
10451          * Usage:
10452          *<pre><code>
10453 // default
10454 el.switchOff();
10455
10456 // all config options shown with default values
10457 el.switchOff({
10458     easing: 'easeIn',
10459     duration: .3,
10460     remove: false,
10461     useDisplay: false
10462 });
10463 </code></pre>
10464          * @param {Object} options (optional) Object literal with any of the Fx config options
10465          * @return {Roo.Element} The Element
10466          */
10467     switchOff : function(o){
10468         var el = this.getFxEl();
10469         o = o || {};
10470
10471         el.queueFx(o, function(){
10472             this.clearOpacity();
10473             this.clip();
10474
10475             // restore values after effect
10476             var r = this.getFxRestore();
10477             var st = this.dom.style;
10478
10479             var after = function(){
10480                 if(o.useDisplay){
10481                     el.setDisplayed(false);
10482                 }else{
10483                     el.hide();
10484                 }
10485
10486                 el.clearOpacity();
10487                 el.setPositioning(r.pos);
10488                 st.width = r.width;
10489                 st.height = r.height;
10490
10491                 el.afterFx(o);
10492             };
10493
10494             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
10495                 this.clearOpacity();
10496                 (function(){
10497                     this.fxanim({
10498                         height:{to:1},
10499                         points:{by:[0, this.getHeight() * .5]}
10500                     }, o, 'motion', 0.3, 'easeIn', after);
10501                 }).defer(100, this);
10502             });
10503         });
10504         return this;
10505     },
10506
10507     /**
10508      * Highlights the Element by setting a color (applies to the background-color by default, but can be
10509      * changed using the "attr" config option) and then fading back to the original color. If no original
10510      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
10511      * Usage:
10512 <pre><code>
10513 // default: highlight background to yellow
10514 el.highlight();
10515
10516 // custom: highlight foreground text to blue for 2 seconds
10517 el.highlight("0000ff", { attr: 'color', duration: 2 });
10518
10519 // common config options shown with default values
10520 el.highlight("ffff9c", {
10521     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
10522     endColor: (current color) or "ffffff",
10523     easing: 'easeIn',
10524     duration: 1
10525 });
10526 </code></pre>
10527      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
10528      * @param {Object} options (optional) Object literal with any of the Fx config options
10529      * @return {Roo.Element} The Element
10530      */ 
10531     highlight : function(color, o){
10532         var el = this.getFxEl();
10533         o = o || {};
10534
10535         el.queueFx(o, function(){
10536             color = color || "ffff9c";
10537             attr = o.attr || "backgroundColor";
10538
10539             this.clearOpacity();
10540             this.show();
10541
10542             var origColor = this.getColor(attr);
10543             var restoreColor = this.dom.style[attr];
10544             endColor = (o.endColor || origColor) || "ffffff";
10545
10546             var after = function(){
10547                 el.dom.style[attr] = restoreColor;
10548                 el.afterFx(o);
10549             };
10550
10551             var a = {};
10552             a[attr] = {from: color, to: endColor};
10553             arguments.callee.anim = this.fxanim(a,
10554                 o,
10555                 'color',
10556                 1,
10557                 'easeIn', after);
10558         });
10559         return this;
10560     },
10561
10562    /**
10563     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
10564     * Usage:
10565 <pre><code>
10566 // default: a single light blue ripple
10567 el.frame();
10568
10569 // custom: 3 red ripples lasting 3 seconds total
10570 el.frame("ff0000", 3, { duration: 3 });
10571
10572 // common config options shown with default values
10573 el.frame("C3DAF9", 1, {
10574     duration: 1 //duration of entire animation (not each individual ripple)
10575     // Note: Easing is not configurable and will be ignored if included
10576 });
10577 </code></pre>
10578     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
10579     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
10580     * @param {Object} options (optional) Object literal with any of the Fx config options
10581     * @return {Roo.Element} The Element
10582     */
10583     frame : function(color, count, o){
10584         var el = this.getFxEl();
10585         o = o || {};
10586
10587         el.queueFx(o, function(){
10588             color = color || "#C3DAF9";
10589             if(color.length == 6){
10590                 color = "#" + color;
10591             }
10592             count = count || 1;
10593             duration = o.duration || 1;
10594             this.show();
10595
10596             var b = this.getBox();
10597             var animFn = function(){
10598                 var proxy = this.createProxy({
10599
10600                      style:{
10601                         visbility:"hidden",
10602                         position:"absolute",
10603                         "z-index":"35000", // yee haw
10604                         border:"0px solid " + color
10605                      }
10606                   });
10607                 var scale = Roo.isBorderBox ? 2 : 1;
10608                 proxy.animate({
10609                     top:{from:b.y, to:b.y - 20},
10610                     left:{from:b.x, to:b.x - 20},
10611                     borderWidth:{from:0, to:10},
10612                     opacity:{from:1, to:0},
10613                     height:{from:b.height, to:(b.height + (20*scale))},
10614                     width:{from:b.width, to:(b.width + (20*scale))}
10615                 }, duration, function(){
10616                     proxy.remove();
10617                 });
10618                 if(--count > 0){
10619                      animFn.defer((duration/2)*1000, this);
10620                 }else{
10621                     el.afterFx(o);
10622                 }
10623             };
10624             animFn.call(this);
10625         });
10626         return this;
10627     },
10628
10629    /**
10630     * Creates a pause before any subsequent queued effects begin.  If there are
10631     * no effects queued after the pause it will have no effect.
10632     * Usage:
10633 <pre><code>
10634 el.pause(1);
10635 </code></pre>
10636     * @param {Number} seconds The length of time to pause (in seconds)
10637     * @return {Roo.Element} The Element
10638     */
10639     pause : function(seconds){
10640         var el = this.getFxEl();
10641         var o = {};
10642
10643         el.queueFx(o, function(){
10644             setTimeout(function(){
10645                 el.afterFx(o);
10646             }, seconds * 1000);
10647         });
10648         return this;
10649     },
10650
10651    /**
10652     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
10653     * using the "endOpacity" config option.
10654     * Usage:
10655 <pre><code>
10656 // default: fade in from opacity 0 to 100%
10657 el.fadeIn();
10658
10659 // custom: fade in from opacity 0 to 75% over 2 seconds
10660 el.fadeIn({ endOpacity: .75, duration: 2});
10661
10662 // common config options shown with default values
10663 el.fadeIn({
10664     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
10665     easing: 'easeOut',
10666     duration: .5
10667 });
10668 </code></pre>
10669     * @param {Object} options (optional) Object literal with any of the Fx config options
10670     * @return {Roo.Element} The Element
10671     */
10672     fadeIn : function(o){
10673         var el = this.getFxEl();
10674         o = o || {};
10675         el.queueFx(o, function(){
10676             this.setOpacity(0);
10677             this.fixDisplay();
10678             this.dom.style.visibility = 'visible';
10679             var to = o.endOpacity || 1;
10680             arguments.callee.anim = this.fxanim({opacity:{to:to}},
10681                 o, null, .5, "easeOut", function(){
10682                 if(to == 1){
10683                     this.clearOpacity();
10684                 }
10685                 el.afterFx(o);
10686             });
10687         });
10688         return this;
10689     },
10690
10691    /**
10692     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
10693     * using the "endOpacity" config option.
10694     * Usage:
10695 <pre><code>
10696 // default: fade out from the element's current opacity to 0
10697 el.fadeOut();
10698
10699 // custom: fade out from the element's current opacity to 25% over 2 seconds
10700 el.fadeOut({ endOpacity: .25, duration: 2});
10701
10702 // common config options shown with default values
10703 el.fadeOut({
10704     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
10705     easing: 'easeOut',
10706     duration: .5
10707     remove: false,
10708     useDisplay: false
10709 });
10710 </code></pre>
10711     * @param {Object} options (optional) Object literal with any of the Fx config options
10712     * @return {Roo.Element} The Element
10713     */
10714     fadeOut : function(o){
10715         var el = this.getFxEl();
10716         o = o || {};
10717         el.queueFx(o, function(){
10718             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
10719                 o, null, .5, "easeOut", function(){
10720                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
10721                      this.dom.style.display = "none";
10722                 }else{
10723                      this.dom.style.visibility = "hidden";
10724                 }
10725                 this.clearOpacity();
10726                 el.afterFx(o);
10727             });
10728         });
10729         return this;
10730     },
10731
10732    /**
10733     * Animates the transition of an element's dimensions from a starting height/width
10734     * to an ending height/width.
10735     * Usage:
10736 <pre><code>
10737 // change height and width to 100x100 pixels
10738 el.scale(100, 100);
10739
10740 // common config options shown with default values.  The height and width will default to
10741 // the element's existing values if passed as null.
10742 el.scale(
10743     [element's width],
10744     [element's height], {
10745     easing: 'easeOut',
10746     duration: .35
10747 });
10748 </code></pre>
10749     * @param {Number} width  The new width (pass undefined to keep the original width)
10750     * @param {Number} height  The new height (pass undefined to keep the original height)
10751     * @param {Object} options (optional) Object literal with any of the Fx config options
10752     * @return {Roo.Element} The Element
10753     */
10754     scale : function(w, h, o){
10755         this.shift(Roo.apply({}, o, {
10756             width: w,
10757             height: h
10758         }));
10759         return this;
10760     },
10761
10762    /**
10763     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
10764     * Any of these properties not specified in the config object will not be changed.  This effect 
10765     * requires that at least one new dimension, position or opacity setting must be passed in on
10766     * the config object in order for the function to have any effect.
10767     * Usage:
10768 <pre><code>
10769 // slide the element horizontally to x position 200 while changing the height and opacity
10770 el.shift({ x: 200, height: 50, opacity: .8 });
10771
10772 // common config options shown with default values.
10773 el.shift({
10774     width: [element's width],
10775     height: [element's height],
10776     x: [element's x position],
10777     y: [element's y position],
10778     opacity: [element's opacity],
10779     easing: 'easeOut',
10780     duration: .35
10781 });
10782 </code></pre>
10783     * @param {Object} options  Object literal with any of the Fx config options
10784     * @return {Roo.Element} The Element
10785     */
10786     shift : function(o){
10787         var el = this.getFxEl();
10788         o = o || {};
10789         el.queueFx(o, function(){
10790             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
10791             if(w !== undefined){
10792                 a.width = {to: this.adjustWidth(w)};
10793             }
10794             if(h !== undefined){
10795                 a.height = {to: this.adjustHeight(h)};
10796             }
10797             if(x !== undefined || y !== undefined){
10798                 a.points = {to: [
10799                     x !== undefined ? x : this.getX(),
10800                     y !== undefined ? y : this.getY()
10801                 ]};
10802             }
10803             if(op !== undefined){
10804                 a.opacity = {to: op};
10805             }
10806             if(o.xy !== undefined){
10807                 a.points = {to: o.xy};
10808             }
10809             arguments.callee.anim = this.fxanim(a,
10810                 o, 'motion', .35, "easeOut", function(){
10811                 el.afterFx(o);
10812             });
10813         });
10814         return this;
10815     },
10816
10817         /**
10818          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
10819          * ending point of the effect.
10820          * Usage:
10821          *<pre><code>
10822 // default: slide the element downward while fading out
10823 el.ghost();
10824
10825 // custom: slide the element out to the right with a 2-second duration
10826 el.ghost('r', { duration: 2 });
10827
10828 // common config options shown with default values
10829 el.ghost('b', {
10830     easing: 'easeOut',
10831     duration: .5
10832     remove: false,
10833     useDisplay: false
10834 });
10835 </code></pre>
10836          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
10837          * @param {Object} options (optional) Object literal with any of the Fx config options
10838          * @return {Roo.Element} The Element
10839          */
10840     ghost : function(anchor, o){
10841         var el = this.getFxEl();
10842         o = o || {};
10843
10844         el.queueFx(o, function(){
10845             anchor = anchor || "b";
10846
10847             // restore values after effect
10848             var r = this.getFxRestore();
10849             var w = this.getWidth(),
10850                 h = this.getHeight();
10851
10852             var st = this.dom.style;
10853
10854             var after = function(){
10855                 if(o.useDisplay){
10856                     el.setDisplayed(false);
10857                 }else{
10858                     el.hide();
10859                 }
10860
10861                 el.clearOpacity();
10862                 el.setPositioning(r.pos);
10863                 st.width = r.width;
10864                 st.height = r.height;
10865
10866                 el.afterFx(o);
10867             };
10868
10869             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
10870             switch(anchor.toLowerCase()){
10871                 case "t":
10872                     pt.by = [0, -h];
10873                 break;
10874                 case "l":
10875                     pt.by = [-w, 0];
10876                 break;
10877                 case "r":
10878                     pt.by = [w, 0];
10879                 break;
10880                 case "b":
10881                     pt.by = [0, h];
10882                 break;
10883                 case "tl":
10884                     pt.by = [-w, -h];
10885                 break;
10886                 case "bl":
10887                     pt.by = [-w, h];
10888                 break;
10889                 case "br":
10890                     pt.by = [w, h];
10891                 break;
10892                 case "tr":
10893                     pt.by = [w, -h];
10894                 break;
10895             }
10896
10897             arguments.callee.anim = this.fxanim(a,
10898                 o,
10899                 'motion',
10900                 .5,
10901                 "easeOut", after);
10902         });
10903         return this;
10904     },
10905
10906         /**
10907          * Ensures that all effects queued after syncFx is called on the element are
10908          * run concurrently.  This is the opposite of {@link #sequenceFx}.
10909          * @return {Roo.Element} The Element
10910          */
10911     syncFx : function(){
10912         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10913             block : false,
10914             concurrent : true,
10915             stopFx : false
10916         });
10917         return this;
10918     },
10919
10920         /**
10921          * Ensures that all effects queued after sequenceFx is called on the element are
10922          * run in sequence.  This is the opposite of {@link #syncFx}.
10923          * @return {Roo.Element} The Element
10924          */
10925     sequenceFx : function(){
10926         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10927             block : false,
10928             concurrent : false,
10929             stopFx : false
10930         });
10931         return this;
10932     },
10933
10934         /* @private */
10935     nextFx : function(){
10936         var ef = this.fxQueue[0];
10937         if(ef){
10938             ef.call(this);
10939         }
10940     },
10941
10942         /**
10943          * Returns true if the element has any effects actively running or queued, else returns false.
10944          * @return {Boolean} True if element has active effects, else false
10945          */
10946     hasActiveFx : function(){
10947         return this.fxQueue && this.fxQueue[0];
10948     },
10949
10950         /**
10951          * Stops any running effects and clears the element's internal effects queue if it contains
10952          * any additional effects that haven't started yet.
10953          * @return {Roo.Element} The Element
10954          */
10955     stopFx : function(){
10956         if(this.hasActiveFx()){
10957             var cur = this.fxQueue[0];
10958             if(cur && cur.anim && cur.anim.isAnimated()){
10959                 this.fxQueue = [cur]; // clear out others
10960                 cur.anim.stop(true);
10961             }
10962         }
10963         return this;
10964     },
10965
10966         /* @private */
10967     beforeFx : function(o){
10968         if(this.hasActiveFx() && !o.concurrent){
10969            if(o.stopFx){
10970                this.stopFx();
10971                return true;
10972            }
10973            return false;
10974         }
10975         return true;
10976     },
10977
10978         /**
10979          * Returns true if the element is currently blocking so that no other effect can be queued
10980          * until this effect is finished, else returns false if blocking is not set.  This is commonly
10981          * used to ensure that an effect initiated by a user action runs to completion prior to the
10982          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
10983          * @return {Boolean} True if blocking, else false
10984          */
10985     hasFxBlock : function(){
10986         var q = this.fxQueue;
10987         return q && q[0] && q[0].block;
10988     },
10989
10990         /* @private */
10991     queueFx : function(o, fn){
10992         if(!this.fxQueue){
10993             this.fxQueue = [];
10994         }
10995         if(!this.hasFxBlock()){
10996             Roo.applyIf(o, this.fxDefaults);
10997             if(!o.concurrent){
10998                 var run = this.beforeFx(o);
10999                 fn.block = o.block;
11000                 this.fxQueue.push(fn);
11001                 if(run){
11002                     this.nextFx();
11003                 }
11004             }else{
11005                 fn.call(this);
11006             }
11007         }
11008         return this;
11009     },
11010
11011         /* @private */
11012     fxWrap : function(pos, o, vis){
11013         var wrap;
11014         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
11015             var wrapXY;
11016             if(o.fixPosition){
11017                 wrapXY = this.getXY();
11018             }
11019             var div = document.createElement("div");
11020             div.style.visibility = vis;
11021             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
11022             wrap.setPositioning(pos);
11023             if(wrap.getStyle("position") == "static"){
11024                 wrap.position("relative");
11025             }
11026             this.clearPositioning('auto');
11027             wrap.clip();
11028             wrap.dom.appendChild(this.dom);
11029             if(wrapXY){
11030                 wrap.setXY(wrapXY);
11031             }
11032         }
11033         return wrap;
11034     },
11035
11036         /* @private */
11037     fxUnwrap : function(wrap, pos, o){
11038         this.clearPositioning();
11039         this.setPositioning(pos);
11040         if(!o.wrap){
11041             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
11042             wrap.remove();
11043         }
11044     },
11045
11046         /* @private */
11047     getFxRestore : function(){
11048         var st = this.dom.style;
11049         return {pos: this.getPositioning(), width: st.width, height : st.height};
11050     },
11051
11052         /* @private */
11053     afterFx : function(o){
11054         if(o.afterStyle){
11055             this.applyStyles(o.afterStyle);
11056         }
11057         if(o.afterCls){
11058             this.addClass(o.afterCls);
11059         }
11060         if(o.remove === true){
11061             this.remove();
11062         }
11063         Roo.callback(o.callback, o.scope, [this]);
11064         if(!o.concurrent){
11065             this.fxQueue.shift();
11066             this.nextFx();
11067         }
11068     },
11069
11070         /* @private */
11071     getFxEl : function(){ // support for composite element fx
11072         return Roo.get(this.dom);
11073     },
11074
11075         /* @private */
11076     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
11077         animType = animType || 'run';
11078         opt = opt || {};
11079         var anim = Roo.lib.Anim[animType](
11080             this.dom, args,
11081             (opt.duration || defaultDur) || .35,
11082             (opt.easing || defaultEase) || 'easeOut',
11083             function(){
11084                 Roo.callback(cb, this);
11085             },
11086             this
11087         );
11088         opt.anim = anim;
11089         return anim;
11090     }
11091 };
11092
11093 // backwords compat
11094 Roo.Fx.resize = Roo.Fx.scale;
11095
11096 //When included, Roo.Fx is automatically applied to Element so that all basic
11097 //effects are available directly via the Element API
11098 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
11099  * Based on:
11100  * Ext JS Library 1.1.1
11101  * Copyright(c) 2006-2007, Ext JS, LLC.
11102  *
11103  * Originally Released Under LGPL - original licence link has changed is not relivant.
11104  *
11105  * Fork - LGPL
11106  * <script type="text/javascript">
11107  */
11108
11109
11110 /**
11111  * @class Roo.CompositeElement
11112  * Standard composite class. Creates a Roo.Element for every element in the collection.
11113  * <br><br>
11114  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11115  * actions will be performed on all the elements in this collection.</b>
11116  * <br><br>
11117  * All methods return <i>this</i> and can be chained.
11118  <pre><code>
11119  var els = Roo.select("#some-el div.some-class", true);
11120  // or select directly from an existing element
11121  var el = Roo.get('some-el');
11122  el.select('div.some-class', true);
11123
11124  els.setWidth(100); // all elements become 100 width
11125  els.hide(true); // all elements fade out and hide
11126  // or
11127  els.setWidth(100).hide(true);
11128  </code></pre>
11129  */
11130 Roo.CompositeElement = function(els){
11131     this.elements = [];
11132     this.addElements(els);
11133 };
11134 Roo.CompositeElement.prototype = {
11135     isComposite: true,
11136     addElements : function(els){
11137         if(!els) {
11138             return this;
11139         }
11140         if(typeof els == "string"){
11141             els = Roo.Element.selectorFunction(els);
11142         }
11143         var yels = this.elements;
11144         var index = yels.length-1;
11145         for(var i = 0, len = els.length; i < len; i++) {
11146                 yels[++index] = Roo.get(els[i]);
11147         }
11148         return this;
11149     },
11150
11151     /**
11152     * Clears this composite and adds the elements returned by the passed selector.
11153     * @param {String/Array} els A string CSS selector, an array of elements or an element
11154     * @return {CompositeElement} this
11155     */
11156     fill : function(els){
11157         this.elements = [];
11158         this.add(els);
11159         return this;
11160     },
11161
11162     /**
11163     * Filters this composite to only elements that match the passed selector.
11164     * @param {String} selector A string CSS selector
11165     * @param {Boolean} inverse return inverse filter (not matches)
11166     * @return {CompositeElement} this
11167     */
11168     filter : function(selector, inverse){
11169         var els = [];
11170         inverse = inverse || false;
11171         this.each(function(el){
11172             var match = inverse ? !el.is(selector) : el.is(selector);
11173             if(match){
11174                 els[els.length] = el.dom;
11175             }
11176         });
11177         this.fill(els);
11178         return this;
11179     },
11180
11181     invoke : function(fn, args){
11182         var els = this.elements;
11183         for(var i = 0, len = els.length; i < len; i++) {
11184                 Roo.Element.prototype[fn].apply(els[i], args);
11185         }
11186         return this;
11187     },
11188     /**
11189     * Adds elements to this composite.
11190     * @param {String/Array} els A string CSS selector, an array of elements or an element
11191     * @return {CompositeElement} this
11192     */
11193     add : function(els){
11194         if(typeof els == "string"){
11195             this.addElements(Roo.Element.selectorFunction(els));
11196         }else if(els.length !== undefined){
11197             this.addElements(els);
11198         }else{
11199             this.addElements([els]);
11200         }
11201         return this;
11202     },
11203     /**
11204     * Calls the passed function passing (el, this, index) for each element in this composite.
11205     * @param {Function} fn The function to call
11206     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11207     * @return {CompositeElement} this
11208     */
11209     each : function(fn, scope){
11210         var els = this.elements;
11211         for(var i = 0, len = els.length; i < len; i++){
11212             if(fn.call(scope || els[i], els[i], this, i) === false) {
11213                 break;
11214             }
11215         }
11216         return this;
11217     },
11218
11219     /**
11220      * Returns the Element object at the specified index
11221      * @param {Number} index
11222      * @return {Roo.Element}
11223      */
11224     item : function(index){
11225         return this.elements[index] || null;
11226     },
11227
11228     /**
11229      * Returns the first Element
11230      * @return {Roo.Element}
11231      */
11232     first : function(){
11233         return this.item(0);
11234     },
11235
11236     /**
11237      * Returns the last Element
11238      * @return {Roo.Element}
11239      */
11240     last : function(){
11241         return this.item(this.elements.length-1);
11242     },
11243
11244     /**
11245      * Returns the number of elements in this composite
11246      * @return Number
11247      */
11248     getCount : function(){
11249         return this.elements.length;
11250     },
11251
11252     /**
11253      * Returns true if this composite contains the passed element
11254      * @return Boolean
11255      */
11256     contains : function(el){
11257         return this.indexOf(el) !== -1;
11258     },
11259
11260     /**
11261      * Returns true if this composite contains the passed element
11262      * @return Boolean
11263      */
11264     indexOf : function(el){
11265         return this.elements.indexOf(Roo.get(el));
11266     },
11267
11268
11269     /**
11270     * Removes the specified element(s).
11271     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
11272     * or an array of any of those.
11273     * @param {Boolean} removeDom (optional) True to also remove the element from the document
11274     * @return {CompositeElement} this
11275     */
11276     removeElement : function(el, removeDom){
11277         if(el instanceof Array){
11278             for(var i = 0, len = el.length; i < len; i++){
11279                 this.removeElement(el[i]);
11280             }
11281             return this;
11282         }
11283         var index = typeof el == 'number' ? el : this.indexOf(el);
11284         if(index !== -1){
11285             if(removeDom){
11286                 var d = this.elements[index];
11287                 if(d.dom){
11288                     d.remove();
11289                 }else{
11290                     d.parentNode.removeChild(d);
11291                 }
11292             }
11293             this.elements.splice(index, 1);
11294         }
11295         return this;
11296     },
11297
11298     /**
11299     * Replaces the specified element with the passed element.
11300     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
11301     * to replace.
11302     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
11303     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
11304     * @return {CompositeElement} this
11305     */
11306     replaceElement : function(el, replacement, domReplace){
11307         var index = typeof el == 'number' ? el : this.indexOf(el);
11308         if(index !== -1){
11309             if(domReplace){
11310                 this.elements[index].replaceWith(replacement);
11311             }else{
11312                 this.elements.splice(index, 1, Roo.get(replacement))
11313             }
11314         }
11315         return this;
11316     },
11317
11318     /**
11319      * Removes all elements.
11320      */
11321     clear : function(){
11322         this.elements = [];
11323     }
11324 };
11325 (function(){
11326     Roo.CompositeElement.createCall = function(proto, fnName){
11327         if(!proto[fnName]){
11328             proto[fnName] = function(){
11329                 return this.invoke(fnName, arguments);
11330             };
11331         }
11332     };
11333     for(var fnName in Roo.Element.prototype){
11334         if(typeof Roo.Element.prototype[fnName] == "function"){
11335             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
11336         }
11337     };
11338 })();
11339 /*
11340  * Based on:
11341  * Ext JS Library 1.1.1
11342  * Copyright(c) 2006-2007, Ext JS, LLC.
11343  *
11344  * Originally Released Under LGPL - original licence link has changed is not relivant.
11345  *
11346  * Fork - LGPL
11347  * <script type="text/javascript">
11348  */
11349
11350 /**
11351  * @class Roo.CompositeElementLite
11352  * @extends Roo.CompositeElement
11353  * Flyweight composite class. Reuses the same Roo.Element for element operations.
11354  <pre><code>
11355  var els = Roo.select("#some-el div.some-class");
11356  // or select directly from an existing element
11357  var el = Roo.get('some-el');
11358  el.select('div.some-class');
11359
11360  els.setWidth(100); // all elements become 100 width
11361  els.hide(true); // all elements fade out and hide
11362  // or
11363  els.setWidth(100).hide(true);
11364  </code></pre><br><br>
11365  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11366  * actions will be performed on all the elements in this collection.</b>
11367  */
11368 Roo.CompositeElementLite = function(els){
11369     Roo.CompositeElementLite.superclass.constructor.call(this, els);
11370     this.el = new Roo.Element.Flyweight();
11371 };
11372 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
11373     addElements : function(els){
11374         if(els){
11375             if(els instanceof Array){
11376                 this.elements = this.elements.concat(els);
11377             }else{
11378                 var yels = this.elements;
11379                 var index = yels.length-1;
11380                 for(var i = 0, len = els.length; i < len; i++) {
11381                     yels[++index] = els[i];
11382                 }
11383             }
11384         }
11385         return this;
11386     },
11387     invoke : function(fn, args){
11388         var els = this.elements;
11389         var el = this.el;
11390         for(var i = 0, len = els.length; i < len; i++) {
11391             el.dom = els[i];
11392                 Roo.Element.prototype[fn].apply(el, args);
11393         }
11394         return this;
11395     },
11396     /**
11397      * Returns a flyweight Element of the dom element object at the specified index
11398      * @param {Number} index
11399      * @return {Roo.Element}
11400      */
11401     item : function(index){
11402         if(!this.elements[index]){
11403             return null;
11404         }
11405         this.el.dom = this.elements[index];
11406         return this.el;
11407     },
11408
11409     // fixes scope with flyweight
11410     addListener : function(eventName, handler, scope, opt){
11411         var els = this.elements;
11412         for(var i = 0, len = els.length; i < len; i++) {
11413             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
11414         }
11415         return this;
11416     },
11417
11418     /**
11419     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
11420     * passed is the flyweight (shared) Roo.Element instance, so if you require a
11421     * a reference to the dom node, use el.dom.</b>
11422     * @param {Function} fn The function to call
11423     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11424     * @return {CompositeElement} this
11425     */
11426     each : function(fn, scope){
11427         var els = this.elements;
11428         var el = this.el;
11429         for(var i = 0, len = els.length; i < len; i++){
11430             el.dom = els[i];
11431                 if(fn.call(scope || el, el, this, i) === false){
11432                 break;
11433             }
11434         }
11435         return this;
11436     },
11437
11438     indexOf : function(el){
11439         return this.elements.indexOf(Roo.getDom(el));
11440     },
11441
11442     replaceElement : function(el, replacement, domReplace){
11443         var index = typeof el == 'number' ? el : this.indexOf(el);
11444         if(index !== -1){
11445             replacement = Roo.getDom(replacement);
11446             if(domReplace){
11447                 var d = this.elements[index];
11448                 d.parentNode.insertBefore(replacement, d);
11449                 d.parentNode.removeChild(d);
11450             }
11451             this.elements.splice(index, 1, replacement);
11452         }
11453         return this;
11454     }
11455 });
11456 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
11457
11458 /*
11459  * Based on:
11460  * Ext JS Library 1.1.1
11461  * Copyright(c) 2006-2007, Ext JS, LLC.
11462  *
11463  * Originally Released Under LGPL - original licence link has changed is not relivant.
11464  *
11465  * Fork - LGPL
11466  * <script type="text/javascript">
11467  */
11468
11469  
11470
11471 /**
11472  * @class Roo.data.Connection
11473  * @extends Roo.util.Observable
11474  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
11475  * either to a configured URL, or to a URL specified at request time. 
11476  * 
11477  * Requests made by this class are asynchronous, and will return immediately. No data from
11478  * the server will be available to the statement immediately following the {@link #request} call.
11479  * To process returned data, use a callback in the request options object, or an event listener.
11480  * 
11481  * Note: If you are doing a file upload, you will not get a normal response object sent back to
11482  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
11483  * The response object is created using the innerHTML of the IFRAME's document as the responseText
11484  * property and, if present, the IFRAME's XML document as the responseXML property.
11485  * 
11486  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
11487  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
11488  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
11489  * standard DOM methods.
11490  * @constructor
11491  * @param {Object} config a configuration object.
11492  */
11493 Roo.data.Connection = function(config){
11494     Roo.apply(this, config);
11495     this.addEvents({
11496         /**
11497          * @event beforerequest
11498          * Fires before a network request is made to retrieve a data object.
11499          * @param {Connection} conn This Connection object.
11500          * @param {Object} options The options config object passed to the {@link #request} method.
11501          */
11502         "beforerequest" : true,
11503         /**
11504          * @event requestcomplete
11505          * Fires if the request was successfully completed.
11506          * @param {Connection} conn This Connection object.
11507          * @param {Object} response The XHR object containing the response data.
11508          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11509          * @param {Object} options The options config object passed to the {@link #request} method.
11510          */
11511         "requestcomplete" : true,
11512         /**
11513          * @event requestexception
11514          * Fires if an error HTTP status was returned from the server.
11515          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
11516          * @param {Connection} conn This Connection object.
11517          * @param {Object} response The XHR object containing the response data.
11518          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11519          * @param {Object} options The options config object passed to the {@link #request} method.
11520          */
11521         "requestexception" : true
11522     });
11523     Roo.data.Connection.superclass.constructor.call(this);
11524 };
11525
11526 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
11527     /**
11528      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11529      */
11530     /**
11531      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11532      * extra parameters to each request made by this object. (defaults to undefined)
11533      */
11534     /**
11535      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11536      *  to each request made by this object. (defaults to undefined)
11537      */
11538     /**
11539      * @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)
11540      */
11541     /**
11542      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11543      */
11544     timeout : 30000,
11545     /**
11546      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11547      * @type Boolean
11548      */
11549     autoAbort:false,
11550
11551     /**
11552      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11553      * @type Boolean
11554      */
11555     disableCaching: true,
11556
11557     /**
11558      * Sends an HTTP request to a remote server.
11559      * @param {Object} options An object which may contain the following properties:<ul>
11560      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
11561      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
11562      * request, a url encoded string or a function to call to get either.</li>
11563      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
11564      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
11565      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
11566      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
11567      * <li>options {Object} The parameter to the request call.</li>
11568      * <li>success {Boolean} True if the request succeeded.</li>
11569      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11570      * </ul></li>
11571      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
11572      * The callback is passed the following parameters:<ul>
11573      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11574      * <li>options {Object} The parameter to the request call.</li>
11575      * </ul></li>
11576      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
11577      * The callback is passed the following parameters:<ul>
11578      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11579      * <li>options {Object} The parameter to the request call.</li>
11580      * </ul></li>
11581      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
11582      * for the callback function. Defaults to the browser window.</li>
11583      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
11584      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
11585      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
11586      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
11587      * params for the post data. Any params will be appended to the URL.</li>
11588      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
11589      * </ul>
11590      * @return {Number} transactionId
11591      */
11592     request : function(o){
11593         if(this.fireEvent("beforerequest", this, o) !== false){
11594             var p = o.params;
11595
11596             if(typeof p == "function"){
11597                 p = p.call(o.scope||window, o);
11598             }
11599             if(typeof p == "object"){
11600                 p = Roo.urlEncode(o.params);
11601             }
11602             if(this.extraParams){
11603                 var extras = Roo.urlEncode(this.extraParams);
11604                 p = p ? (p + '&' + extras) : extras;
11605             }
11606
11607             var url = o.url || this.url;
11608             if(typeof url == 'function'){
11609                 url = url.call(o.scope||window, o);
11610             }
11611
11612             if(o.form){
11613                 var form = Roo.getDom(o.form);
11614                 url = url || form.action;
11615
11616                 var enctype = form.getAttribute("enctype");
11617                 
11618                 if (o.formData) {
11619                     return this.doFormDataUpload(o,p,url);
11620                 }
11621                 
11622                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
11623                     return this.doFormUpload(o, p, url);
11624                 }
11625                 var f = Roo.lib.Ajax.serializeForm(form);
11626                 p = p ? (p + '&' + f) : f;
11627             }
11628
11629             var hs = o.headers;
11630             if(this.defaultHeaders){
11631                 hs = Roo.apply(hs || {}, this.defaultHeaders);
11632                 if(!o.headers){
11633                     o.headers = hs;
11634                 }
11635             }
11636
11637             var cb = {
11638                 success: this.handleResponse,
11639                 failure: this.handleFailure,
11640                 scope: this,
11641                 argument: {options: o},
11642                 timeout : o.timeout || this.timeout
11643             };
11644
11645             var method = o.method||this.method||(p ? "POST" : "GET");
11646
11647             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
11648                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
11649             }
11650
11651             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11652                 if(o.autoAbort){
11653                     this.abort();
11654                 }
11655             }else if(this.autoAbort !== false){
11656                 this.abort();
11657             }
11658
11659             if((method == 'GET' && p) || o.xmlData){
11660                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11661                 p = '';
11662             }
11663             Roo.lib.Ajax.useDefaultHeader = typeof(o.headers) == 'undefined' || typeof(o.headers['Content-Type']) == 'undefined';
11664             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
11665             Roo.lib.Ajax.useDefaultHeader == true;
11666             return this.transId;
11667         }else{
11668             Roo.callback(o.callback, o.scope, [o, null, null]);
11669             return null;
11670         }
11671     },
11672
11673     /**
11674      * Determine whether this object has a request outstanding.
11675      * @param {Number} transactionId (Optional) defaults to the last transaction
11676      * @return {Boolean} True if there is an outstanding request.
11677      */
11678     isLoading : function(transId){
11679         if(transId){
11680             return Roo.lib.Ajax.isCallInProgress(transId);
11681         }else{
11682             return this.transId ? true : false;
11683         }
11684     },
11685
11686     /**
11687      * Aborts any outstanding request.
11688      * @param {Number} transactionId (Optional) defaults to the last transaction
11689      */
11690     abort : function(transId){
11691         if(transId || this.isLoading()){
11692             Roo.lib.Ajax.abort(transId || this.transId);
11693         }
11694     },
11695
11696     // private
11697     handleResponse : function(response){
11698         this.transId = false;
11699         var options = response.argument.options;
11700         response.argument = options ? options.argument : null;
11701         this.fireEvent("requestcomplete", this, response, options);
11702         Roo.callback(options.success, options.scope, [response, options]);
11703         Roo.callback(options.callback, options.scope, [options, true, response]);
11704     },
11705
11706     // private
11707     handleFailure : function(response, e){
11708         this.transId = false;
11709         var options = response.argument.options;
11710         response.argument = options ? options.argument : null;
11711         this.fireEvent("requestexception", this, response, options, e);
11712         Roo.callback(options.failure, options.scope, [response, options]);
11713         Roo.callback(options.callback, options.scope, [options, false, response]);
11714     },
11715
11716     // private
11717     doFormUpload : function(o, ps, url){
11718         var id = Roo.id();
11719         var frame = document.createElement('iframe');
11720         frame.id = id;
11721         frame.name = id;
11722         frame.className = 'x-hidden';
11723         if(Roo.isIE){
11724             frame.src = Roo.SSL_SECURE_URL;
11725         }
11726         document.body.appendChild(frame);
11727
11728         if(Roo.isIE){
11729            document.frames[id].name = id;
11730         }
11731
11732         var form = Roo.getDom(o.form);
11733         form.target = id;
11734         form.method = 'POST';
11735         form.enctype = form.encoding = 'multipart/form-data';
11736         if(url){
11737             form.action = url;
11738         }
11739
11740         var hiddens, hd;
11741         if(ps){ // add dynamic params
11742             hiddens = [];
11743             ps = Roo.urlDecode(ps, false);
11744             for(var k in ps){
11745                 if(ps.hasOwnProperty(k)){
11746                     hd = document.createElement('input');
11747                     hd.type = 'hidden';
11748                     hd.name = k;
11749                     hd.value = ps[k];
11750                     form.appendChild(hd);
11751                     hiddens.push(hd);
11752                 }
11753             }
11754         }
11755
11756         function cb(){
11757             var r = {  // bogus response object
11758                 responseText : '',
11759                 responseXML : null
11760             };
11761
11762             r.argument = o ? o.argument : null;
11763
11764             try { //
11765                 var doc;
11766                 if(Roo.isIE){
11767                     doc = frame.contentWindow.document;
11768                 }else {
11769                     doc = (frame.contentDocument || window.frames[id].document);
11770                 }
11771                 if(doc && doc.body){
11772                     r.responseText = doc.body.innerHTML;
11773                 }
11774                 if(doc && doc.XMLDocument){
11775                     r.responseXML = doc.XMLDocument;
11776                 }else {
11777                     r.responseXML = doc;
11778                 }
11779             }
11780             catch(e) {
11781                 // ignore
11782             }
11783
11784             Roo.EventManager.removeListener(frame, 'load', cb, this);
11785
11786             this.fireEvent("requestcomplete", this, r, o);
11787             Roo.callback(o.success, o.scope, [r, o]);
11788             Roo.callback(o.callback, o.scope, [o, true, r]);
11789
11790             setTimeout(function(){document.body.removeChild(frame);}, 100);
11791         }
11792
11793         Roo.EventManager.on(frame, 'load', cb, this);
11794         form.submit();
11795
11796         if(hiddens){ // remove dynamic params
11797             for(var i = 0, len = hiddens.length; i < len; i++){
11798                 form.removeChild(hiddens[i]);
11799             }
11800         }
11801     },
11802     // this is a 'formdata version???'
11803     
11804     
11805     doFormDataUpload : function(o, ps, url)
11806     {
11807         var form = Roo.getDom(o.form);
11808         form.enctype = form.encoding = 'multipart/form-data';
11809         var formData = o.formData === true ? new FormData(form) : o.formData;
11810       
11811         var cb = {
11812             success: this.handleResponse,
11813             failure: this.handleFailure,
11814             scope: this,
11815             argument: {options: o},
11816             timeout : o.timeout || this.timeout
11817         };
11818  
11819         if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11820             if(o.autoAbort){
11821                 this.abort();
11822             }
11823         }else if(this.autoAbort !== false){
11824             this.abort();
11825         }
11826
11827         //Roo.lib.Ajax.defaultPostHeader = null;
11828         Roo.lib.Ajax.useDefaultHeader = false;
11829         this.transId = Roo.lib.Ajax.request( "POST", url, cb, o.formData, o);
11830         Roo.lib.Ajax.useDefaultHeader = true;
11831  
11832          
11833     }
11834     
11835 });
11836 /*
11837  * Based on:
11838  * Ext JS Library 1.1.1
11839  * Copyright(c) 2006-2007, Ext JS, LLC.
11840  *
11841  * Originally Released Under LGPL - original licence link has changed is not relivant.
11842  *
11843  * Fork - LGPL
11844  * <script type="text/javascript">
11845  */
11846  
11847 /**
11848  * Global Ajax request class.
11849  * 
11850  * @class Roo.Ajax
11851  * @extends Roo.data.Connection
11852  * @static
11853  * 
11854  * @cfg {String} url  The default URL to be used for requests to the server. (defaults to undefined)
11855  * @cfg {Object} extraParams  An object containing properties which are used as extra parameters to each request made by this object. (defaults to undefined)
11856  * @cfg {Object} defaultHeaders  An object containing request headers which are added to each request made by this object. (defaults to undefined)
11857  * @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)
11858  * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11859  * @cfg {Boolean} autoAbort (Optional) Whether a new request should abort any pending requests. (defaults to false)
11860  * @cfg {Boolean} disableCaching (Optional)   True to add a unique cache-buster param to GET requests. (defaults to true)
11861  */
11862 Roo.Ajax = new Roo.data.Connection({
11863     // fix up the docs
11864     /**
11865      * @scope Roo.Ajax
11866      * @type {Boolear} 
11867      */
11868     autoAbort : false,
11869
11870     /**
11871      * Serialize the passed form into a url encoded string
11872      * @scope Roo.Ajax
11873      * @param {String/HTMLElement} form
11874      * @return {String}
11875      */
11876     serializeForm : function(form){
11877         return Roo.lib.Ajax.serializeForm(form);
11878     }
11879 });/*
11880  * Based on:
11881  * Ext JS Library 1.1.1
11882  * Copyright(c) 2006-2007, Ext JS, LLC.
11883  *
11884  * Originally Released Under LGPL - original licence link has changed is not relivant.
11885  *
11886  * Fork - LGPL
11887  * <script type="text/javascript">
11888  */
11889
11890  
11891 /**
11892  * @class Roo.UpdateManager
11893  * @extends Roo.util.Observable
11894  * Provides AJAX-style update for Element object.<br><br>
11895  * Usage:<br>
11896  * <pre><code>
11897  * // Get it from a Roo.Element object
11898  * var el = Roo.get("foo");
11899  * var mgr = el.getUpdateManager();
11900  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
11901  * ...
11902  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
11903  * <br>
11904  * // or directly (returns the same UpdateManager instance)
11905  * var mgr = new Roo.UpdateManager("myElementId");
11906  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
11907  * mgr.on("update", myFcnNeedsToKnow);
11908  * <br>
11909    // short handed call directly from the element object
11910    Roo.get("foo").load({
11911         url: "bar.php",
11912         scripts:true,
11913         params: "for=bar",
11914         text: "Loading Foo..."
11915    });
11916  * </code></pre>
11917  * @constructor
11918  * Create new UpdateManager directly.
11919  * @param {String/HTMLElement/Roo.Element} el The element to update
11920  * @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).
11921  */
11922 Roo.UpdateManager = function(el, forceNew){
11923     el = Roo.get(el);
11924     if(!forceNew && el.updateManager){
11925         return el.updateManager;
11926     }
11927     /**
11928      * The Element object
11929      * @type Roo.Element
11930      */
11931     this.el = el;
11932     /**
11933      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
11934      * @type String
11935      */
11936     this.defaultUrl = null;
11937
11938     this.addEvents({
11939         /**
11940          * @event beforeupdate
11941          * Fired before an update is made, return false from your handler and the update is cancelled.
11942          * @param {Roo.Element} el
11943          * @param {String/Object/Function} url
11944          * @param {String/Object} params
11945          */
11946         "beforeupdate": true,
11947         /**
11948          * @event update
11949          * Fired after successful update is made.
11950          * @param {Roo.Element} el
11951          * @param {Object} oResponseObject The response Object
11952          */
11953         "update": true,
11954         /**
11955          * @event failure
11956          * Fired on update failure.
11957          * @param {Roo.Element} el
11958          * @param {Object} oResponseObject The response Object
11959          */
11960         "failure": true
11961     });
11962     var d = Roo.UpdateManager.defaults;
11963     /**
11964      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
11965      * @type String
11966      */
11967     this.sslBlankUrl = d.sslBlankUrl;
11968     /**
11969      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
11970      * @type Boolean
11971      */
11972     this.disableCaching = d.disableCaching;
11973     /**
11974      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
11975      * @type String
11976      */
11977     this.indicatorText = d.indicatorText;
11978     /**
11979      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
11980      * @type String
11981      */
11982     this.showLoadIndicator = d.showLoadIndicator;
11983     /**
11984      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
11985      * @type Number
11986      */
11987     this.timeout = d.timeout;
11988
11989     /**
11990      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
11991      * @type Boolean
11992      */
11993     this.loadScripts = d.loadScripts;
11994
11995     /**
11996      * Transaction object of current executing transaction
11997      */
11998     this.transaction = null;
11999
12000     /**
12001      * @private
12002      */
12003     this.autoRefreshProcId = null;
12004     /**
12005      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
12006      * @type Function
12007      */
12008     this.refreshDelegate = this.refresh.createDelegate(this);
12009     /**
12010      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
12011      * @type Function
12012      */
12013     this.updateDelegate = this.update.createDelegate(this);
12014     /**
12015      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
12016      * @type Function
12017      */
12018     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
12019     /**
12020      * @private
12021      */
12022     this.successDelegate = this.processSuccess.createDelegate(this);
12023     /**
12024      * @private
12025      */
12026     this.failureDelegate = this.processFailure.createDelegate(this);
12027
12028     if(!this.renderer){
12029      /**
12030       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
12031       */
12032     this.renderer = new Roo.UpdateManager.BasicRenderer();
12033     }
12034     
12035     Roo.UpdateManager.superclass.constructor.call(this);
12036 };
12037
12038 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
12039     /**
12040      * Get the Element this UpdateManager is bound to
12041      * @return {Roo.Element} The element
12042      */
12043     getEl : function(){
12044         return this.el;
12045     },
12046     /**
12047      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
12048      * @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:
12049 <pre><code>
12050 um.update({<br/>
12051     url: "your-url.php",<br/>
12052     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
12053     callback: yourFunction,<br/>
12054     scope: yourObject, //(optional scope)  <br/>
12055     discardUrl: false, <br/>
12056     nocache: false,<br/>
12057     text: "Loading...",<br/>
12058     timeout: 30,<br/>
12059     scripts: false<br/>
12060 });
12061 </code></pre>
12062      * The only required property is url. The optional properties nocache, text and scripts
12063      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
12064      * @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}
12065      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
12066      * @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.
12067      */
12068     update : function(url, params, callback, discardUrl){
12069         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
12070             var method = this.method,
12071                 cfg;
12072             if(typeof url == "object"){ // must be config object
12073                 cfg = url;
12074                 url = cfg.url;
12075                 params = params || cfg.params;
12076                 callback = callback || cfg.callback;
12077                 discardUrl = discardUrl || cfg.discardUrl;
12078                 if(callback && cfg.scope){
12079                     callback = callback.createDelegate(cfg.scope);
12080                 }
12081                 if(typeof cfg.method != "undefined"){method = cfg.method;};
12082                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
12083                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
12084                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
12085                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
12086             }
12087             this.showLoading();
12088             if(!discardUrl){
12089                 this.defaultUrl = url;
12090             }
12091             if(typeof url == "function"){
12092                 url = url.call(this);
12093             }
12094
12095             method = method || (params ? "POST" : "GET");
12096             if(method == "GET"){
12097                 url = this.prepareUrl(url);
12098             }
12099
12100             var o = Roo.apply(cfg ||{}, {
12101                 url : url,
12102                 params: params,
12103                 success: this.successDelegate,
12104                 failure: this.failureDelegate,
12105                 callback: undefined,
12106                 timeout: (this.timeout*1000),
12107                 argument: {"url": url, "form": null, "callback": callback, "params": params}
12108             });
12109             Roo.log("updated manager called with timeout of " + o.timeout);
12110             this.transaction = Roo.Ajax.request(o);
12111         }
12112     },
12113
12114     /**
12115      * 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.
12116      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
12117      * @param {String/HTMLElement} form The form Id or form element
12118      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
12119      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
12120      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
12121      */
12122     formUpdate : function(form, url, reset, callback){
12123         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
12124             if(typeof url == "function"){
12125                 url = url.call(this);
12126             }
12127             form = Roo.getDom(form);
12128             this.transaction = Roo.Ajax.request({
12129                 form: form,
12130                 url:url,
12131                 success: this.successDelegate,
12132                 failure: this.failureDelegate,
12133                 timeout: (this.timeout*1000),
12134                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
12135             });
12136             this.showLoading.defer(1, this);
12137         }
12138     },
12139
12140     /**
12141      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
12142      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
12143      */
12144     refresh : function(callback){
12145         if(this.defaultUrl == null){
12146             return;
12147         }
12148         this.update(this.defaultUrl, null, callback, true);
12149     },
12150
12151     /**
12152      * Set this element to auto refresh.
12153      * @param {Number} interval How often to update (in seconds).
12154      * @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)
12155      * @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}
12156      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
12157      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
12158      */
12159     startAutoRefresh : function(interval, url, params, callback, refreshNow){
12160         if(refreshNow){
12161             this.update(url || this.defaultUrl, params, callback, true);
12162         }
12163         if(this.autoRefreshProcId){
12164             clearInterval(this.autoRefreshProcId);
12165         }
12166         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
12167     },
12168
12169     /**
12170      * Stop auto refresh on this element.
12171      */
12172      stopAutoRefresh : function(){
12173         if(this.autoRefreshProcId){
12174             clearInterval(this.autoRefreshProcId);
12175             delete this.autoRefreshProcId;
12176         }
12177     },
12178
12179     isAutoRefreshing : function(){
12180        return this.autoRefreshProcId ? true : false;
12181     },
12182     /**
12183      * Called to update the element to "Loading" state. Override to perform custom action.
12184      */
12185     showLoading : function(){
12186         if(this.showLoadIndicator){
12187             this.el.update(this.indicatorText);
12188         }
12189     },
12190
12191     /**
12192      * Adds unique parameter to query string if disableCaching = true
12193      * @private
12194      */
12195     prepareUrl : function(url){
12196         if(this.disableCaching){
12197             var append = "_dc=" + (new Date().getTime());
12198             if(url.indexOf("?") !== -1){
12199                 url += "&" + append;
12200             }else{
12201                 url += "?" + append;
12202             }
12203         }
12204         return url;
12205     },
12206
12207     /**
12208      * @private
12209      */
12210     processSuccess : function(response){
12211         this.transaction = null;
12212         if(response.argument.form && response.argument.reset){
12213             try{ // put in try/catch since some older FF releases had problems with this
12214                 response.argument.form.reset();
12215             }catch(e){}
12216         }
12217         if(this.loadScripts){
12218             this.renderer.render(this.el, response, this,
12219                 this.updateComplete.createDelegate(this, [response]));
12220         }else{
12221             this.renderer.render(this.el, response, this);
12222             this.updateComplete(response);
12223         }
12224     },
12225
12226     updateComplete : function(response){
12227         this.fireEvent("update", this.el, response);
12228         if(typeof response.argument.callback == "function"){
12229             response.argument.callback(this.el, true, response);
12230         }
12231     },
12232
12233     /**
12234      * @private
12235      */
12236     processFailure : function(response){
12237         this.transaction = null;
12238         this.fireEvent("failure", this.el, response);
12239         if(typeof response.argument.callback == "function"){
12240             response.argument.callback(this.el, false, response);
12241         }
12242     },
12243
12244     /**
12245      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
12246      * @param {Object} renderer The object implementing the render() method
12247      */
12248     setRenderer : function(renderer){
12249         this.renderer = renderer;
12250     },
12251
12252     getRenderer : function(){
12253        return this.renderer;
12254     },
12255
12256     /**
12257      * Set the defaultUrl used for updates
12258      * @param {String/Function} defaultUrl The url or a function to call to get the url
12259      */
12260     setDefaultUrl : function(defaultUrl){
12261         this.defaultUrl = defaultUrl;
12262     },
12263
12264     /**
12265      * Aborts the executing transaction
12266      */
12267     abort : function(){
12268         if(this.transaction){
12269             Roo.Ajax.abort(this.transaction);
12270         }
12271     },
12272
12273     /**
12274      * Returns true if an update is in progress
12275      * @return {Boolean}
12276      */
12277     isUpdating : function(){
12278         if(this.transaction){
12279             return Roo.Ajax.isLoading(this.transaction);
12280         }
12281         return false;
12282     }
12283 });
12284
12285 /**
12286  * @class Roo.UpdateManager.defaults
12287  * @static (not really - but it helps the doc tool)
12288  * The defaults collection enables customizing the default properties of UpdateManager
12289  */
12290    Roo.UpdateManager.defaults = {
12291        /**
12292          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
12293          * @type Number
12294          */
12295          timeout : 30,
12296
12297          /**
12298          * True to process scripts by default (Defaults to false).
12299          * @type Boolean
12300          */
12301         loadScripts : false,
12302
12303         /**
12304         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
12305         * @type String
12306         */
12307         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
12308         /**
12309          * Whether to append unique parameter on get request to disable caching (Defaults to false).
12310          * @type Boolean
12311          */
12312         disableCaching : false,
12313         /**
12314          * Whether to show indicatorText when loading (Defaults to true).
12315          * @type Boolean
12316          */
12317         showLoadIndicator : true,
12318         /**
12319          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12320          * @type String
12321          */
12322         indicatorText : '<div class="loading-indicator">Loading...</div>'
12323    };
12324
12325 /**
12326  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
12327  *Usage:
12328  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
12329  * @param {String/HTMLElement/Roo.Element} el The element to update
12330  * @param {String} url The url
12331  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
12332  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
12333  * @static
12334  * @deprecated
12335  * @member Roo.UpdateManager
12336  */
12337 Roo.UpdateManager.updateElement = function(el, url, params, options){
12338     var um = Roo.get(el, true).getUpdateManager();
12339     Roo.apply(um, options);
12340     um.update(url, params, options ? options.callback : null);
12341 };
12342 // alias for backwards compat
12343 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
12344 /**
12345  * @class Roo.UpdateManager.BasicRenderer
12346  * Default Content renderer. Updates the elements innerHTML with the responseText.
12347  */
12348 Roo.UpdateManager.BasicRenderer = function(){};
12349
12350 Roo.UpdateManager.BasicRenderer.prototype = {
12351     /**
12352      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
12353      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
12354      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
12355      * @param {Roo.Element} el The element being rendered
12356      * @param {Object} response The YUI Connect response object
12357      * @param {UpdateManager} updateManager The calling update manager
12358      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
12359      */
12360      render : function(el, response, updateManager, callback){
12361         el.update(response.responseText, updateManager.loadScripts, callback);
12362     }
12363 };
12364 /*
12365  * Based on:
12366  * Roo JS
12367  * (c)) Alan Knowles
12368  * Licence : LGPL
12369  */
12370
12371
12372 /**
12373  * @class Roo.DomTemplate
12374  * @extends Roo.Template
12375  * An effort at a dom based template engine..
12376  *
12377  * Similar to XTemplate, except it uses dom parsing to create the template..
12378  *
12379  * Supported features:
12380  *
12381  *  Tags:
12382
12383 <pre><code>
12384       {a_variable} - output encoded.
12385       {a_variable.format:("Y-m-d")} - call a method on the variable
12386       {a_variable:raw} - unencoded output
12387       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
12388       {a_variable:this.method_on_template(...)} - call a method on the template object.
12389  
12390 </code></pre>
12391  *  The tpl tag:
12392 <pre><code>
12393         &lt;div roo-for="a_variable or condition.."&gt;&lt;/div&gt;
12394         &lt;div roo-if="a_variable or condition"&gt;&lt;/div&gt;
12395         &lt;div roo-exec="some javascript"&gt;&lt;/div&gt;
12396         &lt;div roo-name="named_template"&gt;&lt;/div&gt; 
12397   
12398 </code></pre>
12399  *      
12400  */
12401 Roo.DomTemplate = function()
12402 {
12403      Roo.DomTemplate.superclass.constructor.apply(this, arguments);
12404      if (this.html) {
12405         this.compile();
12406      }
12407 };
12408
12409
12410 Roo.extend(Roo.DomTemplate, Roo.Template, {
12411     /**
12412      * id counter for sub templates.
12413      */
12414     id : 0,
12415     /**
12416      * flag to indicate if dom parser is inside a pre,
12417      * it will strip whitespace if not.
12418      */
12419     inPre : false,
12420     
12421     /**
12422      * The various sub templates
12423      */
12424     tpls : false,
12425     
12426     
12427     
12428     /**
12429      *
12430      * basic tag replacing syntax
12431      * WORD:WORD()
12432      *
12433      * // you can fake an object call by doing this
12434      *  x.t:(test,tesT) 
12435      * 
12436      */
12437     re : /(\{|\%7B)([\w-\.]+)(?:\:([\w\.]*)(?:\(([^)]*?)?\))?)?(\}|\%7D)/g,
12438     //re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
12439     
12440     iterChild : function (node, method) {
12441         
12442         var oldPre = this.inPre;
12443         if (node.tagName == 'PRE') {
12444             this.inPre = true;
12445         }
12446         for( var i = 0; i < node.childNodes.length; i++) {
12447             method.call(this, node.childNodes[i]);
12448         }
12449         this.inPre = oldPre;
12450     },
12451     
12452     
12453     
12454     /**
12455      * compile the template
12456      *
12457      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
12458      *
12459      */
12460     compile: function()
12461     {
12462         var s = this.html;
12463         
12464         // covert the html into DOM...
12465         var doc = false;
12466         var div =false;
12467         try {
12468             doc = document.implementation.createHTMLDocument("");
12469             doc.documentElement.innerHTML =   this.html  ;
12470             div = doc.documentElement;
12471         } catch (e) {
12472             // old IE... - nasty -- it causes all sorts of issues.. with
12473             // images getting pulled from server..
12474             div = document.createElement('div');
12475             div.innerHTML = this.html;
12476         }
12477         //doc.documentElement.innerHTML = htmlBody
12478          
12479         
12480         
12481         this.tpls = [];
12482         var _t = this;
12483         this.iterChild(div, function(n) {_t.compileNode(n, true); });
12484         
12485         var tpls = this.tpls;
12486         
12487         // create a top level template from the snippet..
12488         
12489         //Roo.log(div.innerHTML);
12490         
12491         var tpl = {
12492             uid : 'master',
12493             id : this.id++,
12494             attr : false,
12495             value : false,
12496             body : div.innerHTML,
12497             
12498             forCall : false,
12499             execCall : false,
12500             dom : div,
12501             isTop : true
12502             
12503         };
12504         tpls.unshift(tpl);
12505         
12506         
12507         // compile them...
12508         this.tpls = [];
12509         Roo.each(tpls, function(tp){
12510             this.compileTpl(tp);
12511             this.tpls[tp.id] = tp;
12512         }, this);
12513         
12514         this.master = tpls[0];
12515         return this;
12516         
12517         
12518     },
12519     
12520     compileNode : function(node, istop) {
12521         // test for
12522         //Roo.log(node);
12523         
12524         
12525         // skip anything not a tag..
12526         if (node.nodeType != 1) {
12527             if (node.nodeType == 3 && !this.inPre) {
12528                 // reduce white space..
12529                 node.nodeValue = node.nodeValue.replace(/\s+/g, ' '); 
12530                 
12531             }
12532             return;
12533         }
12534         
12535         var tpl = {
12536             uid : false,
12537             id : false,
12538             attr : false,
12539             value : false,
12540             body : '',
12541             
12542             forCall : false,
12543             execCall : false,
12544             dom : false,
12545             isTop : istop
12546             
12547             
12548         };
12549         
12550         
12551         switch(true) {
12552             case (node.hasAttribute('roo-for')): tpl.attr = 'for'; break;
12553             case (node.hasAttribute('roo-if')): tpl.attr = 'if'; break;
12554             case (node.hasAttribute('roo-name')): tpl.attr = 'name'; break;
12555             case (node.hasAttribute('roo-exec')): tpl.attr = 'exec'; break;
12556             // no default..
12557         }
12558         
12559         
12560         if (!tpl.attr) {
12561             // just itterate children..
12562             this.iterChild(node,this.compileNode);
12563             return;
12564         }
12565         tpl.uid = this.id++;
12566         tpl.value = node.getAttribute('roo-' +  tpl.attr);
12567         node.removeAttribute('roo-'+ tpl.attr);
12568         if (tpl.attr != 'name') {
12569             var placeholder = document.createTextNode('{domtpl' + tpl.uid + '}');
12570             node.parentNode.replaceChild(placeholder,  node);
12571         } else {
12572             
12573             var placeholder =  document.createElement('span');
12574             placeholder.className = 'roo-tpl-' + tpl.value;
12575             node.parentNode.replaceChild(placeholder,  node);
12576         }
12577         
12578         // parent now sees '{domtplXXXX}
12579         this.iterChild(node,this.compileNode);
12580         
12581         // we should now have node body...
12582         var div = document.createElement('div');
12583         div.appendChild(node);
12584         tpl.dom = node;
12585         // this has the unfortunate side effect of converting tagged attributes
12586         // eg. href="{...}" into %7C...%7D
12587         // this has been fixed by searching for those combo's although it's a bit hacky..
12588         
12589         
12590         tpl.body = div.innerHTML;
12591         
12592         
12593          
12594         tpl.id = tpl.uid;
12595         switch(tpl.attr) {
12596             case 'for' :
12597                 switch (tpl.value) {
12598                     case '.':  tpl.forCall = new Function('values', 'parent', 'with(values){ return values; }'); break;
12599                     case '..': tpl.forCall= new Function('values', 'parent', 'with(values){ return parent; }'); break;
12600                     default:   tpl.forCall= new Function('values', 'parent', 'with(values){ return '+tpl.value+'; }');
12601                 }
12602                 break;
12603             
12604             case 'exec':
12605                 tpl.execCall = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12606                 break;
12607             
12608             case 'if':     
12609                 tpl.ifCall = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12610                 break;
12611             
12612             case 'name':
12613                 tpl.id  = tpl.value; // replace non characters???
12614                 break;
12615             
12616         }
12617         
12618         
12619         this.tpls.push(tpl);
12620         
12621         
12622         
12623     },
12624     
12625     
12626     
12627     
12628     /**
12629      * Compile a segment of the template into a 'sub-template'
12630      *
12631      * 
12632      * 
12633      *
12634      */
12635     compileTpl : function(tpl)
12636     {
12637         var fm = Roo.util.Format;
12638         var useF = this.disableFormats !== true;
12639         
12640         var sep = Roo.isGecko ? "+\n" : ",\n";
12641         
12642         var undef = function(str) {
12643             Roo.debug && Roo.log("Property not found :"  + str);
12644             return '';
12645         };
12646           
12647         //Roo.log(tpl.body);
12648         
12649         
12650         
12651         var fn = function(m, lbrace, name, format, args)
12652         {
12653             //Roo.log("ARGS");
12654             //Roo.log(arguments);
12655             args = args ? args.replace(/\\'/g,"'") : args;
12656             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
12657             if (typeof(format) == 'undefined') {
12658                 format =  'htmlEncode'; 
12659             }
12660             if (format == 'raw' ) {
12661                 format = false;
12662             }
12663             
12664             if(name.substr(0, 6) == 'domtpl'){
12665                 return "'"+ sep +'this.applySubTemplate('+name.substr(6)+', values, parent)'+sep+"'";
12666             }
12667             
12668             // build an array of options to determine if value is undefined..
12669             
12670             // basically get 'xxxx.yyyy' then do
12671             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
12672             //    (function () { Roo.log("Property not found"); return ''; })() :
12673             //    ......
12674             
12675             var udef_ar = [];
12676             var lookfor = '';
12677             Roo.each(name.split('.'), function(st) {
12678                 lookfor += (lookfor.length ? '.': '') + st;
12679                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
12680             });
12681             
12682             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
12683             
12684             
12685             if(format && useF){
12686                 
12687                 args = args ? ',' + args : "";
12688                  
12689                 if(format.substr(0, 5) != "this."){
12690                     format = "fm." + format + '(';
12691                 }else{
12692                     format = 'this.call("'+ format.substr(5) + '", ';
12693                     args = ", values";
12694                 }
12695                 
12696                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
12697             }
12698              
12699             if (args && args.length) {
12700                 // called with xxyx.yuu:(test,test)
12701                 // change to ()
12702                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
12703             }
12704             // raw.. - :raw modifier..
12705             return "'"+ sep + udef_st  + name + ")"+sep+"'";
12706             
12707         };
12708         var body;
12709         // branched to use + in gecko and [].join() in others
12710         if(Roo.isGecko){
12711             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
12712                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
12713                     "';};};";
12714         }else{
12715             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
12716             body.push(tpl.body.replace(/(\r\n|\n)/g,
12717                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
12718             body.push("'].join('');};};");
12719             body = body.join('');
12720         }
12721         
12722         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
12723        
12724         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
12725         eval(body);
12726         
12727         return this;
12728     },
12729      
12730     /**
12731      * same as applyTemplate, except it's done to one of the subTemplates
12732      * when using named templates, you can do:
12733      *
12734      * var str = pl.applySubTemplate('your-name', values);
12735      *
12736      * 
12737      * @param {Number} id of the template
12738      * @param {Object} values to apply to template
12739      * @param {Object} parent (normaly the instance of this object)
12740      */
12741     applySubTemplate : function(id, values, parent)
12742     {
12743         
12744         
12745         var t = this.tpls[id];
12746         
12747         
12748         try { 
12749             if(t.ifCall && !t.ifCall.call(this, values, parent)){
12750                 Roo.debug && Roo.log('if call on ' + t.value + ' return false');
12751                 return '';
12752             }
12753         } catch(e) {
12754             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-if="' + t.value + '" - ' + e.toString());
12755             Roo.log(values);
12756           
12757             return '';
12758         }
12759         try { 
12760             
12761             if(t.execCall && t.execCall.call(this, values, parent)){
12762                 return '';
12763             }
12764         } catch(e) {
12765             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12766             Roo.log(values);
12767             return '';
12768         }
12769         
12770         try {
12771             var vs = t.forCall ? t.forCall.call(this, values, parent) : values;
12772             parent = t.target ? values : parent;
12773             if(t.forCall && vs instanceof Array){
12774                 var buf = [];
12775                 for(var i = 0, len = vs.length; i < len; i++){
12776                     try {
12777                         buf[buf.length] = t.compiled.call(this, vs[i], parent);
12778                     } catch (e) {
12779                         Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12780                         Roo.log(e.body);
12781                         //Roo.log(t.compiled);
12782                         Roo.log(vs[i]);
12783                     }   
12784                 }
12785                 return buf.join('');
12786             }
12787         } catch (e) {
12788             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12789             Roo.log(values);
12790             return '';
12791         }
12792         try {
12793             return t.compiled.call(this, vs, parent);
12794         } catch (e) {
12795             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12796             Roo.log(e.body);
12797             //Roo.log(t.compiled);
12798             Roo.log(values);
12799             return '';
12800         }
12801     },
12802
12803    
12804
12805     applyTemplate : function(values){
12806         return this.master.compiled.call(this, values, {});
12807         //var s = this.subs;
12808     },
12809
12810     apply : function(){
12811         return this.applyTemplate.apply(this, arguments);
12812     }
12813
12814  });
12815
12816 Roo.DomTemplate.from = function(el){
12817     el = Roo.getDom(el);
12818     return new Roo.Domtemplate(el.value || el.innerHTML);
12819 };/*
12820  * Based on:
12821  * Ext JS Library 1.1.1
12822  * Copyright(c) 2006-2007, Ext JS, LLC.
12823  *
12824  * Originally Released Under LGPL - original licence link has changed is not relivant.
12825  *
12826  * Fork - LGPL
12827  * <script type="text/javascript">
12828  */
12829
12830 /**
12831  * @class Roo.util.DelayedTask
12832  * Provides a convenient method of performing setTimeout where a new
12833  * timeout cancels the old timeout. An example would be performing validation on a keypress.
12834  * You can use this class to buffer
12835  * the keypress events for a certain number of milliseconds, and perform only if they stop
12836  * for that amount of time.
12837  * @constructor The parameters to this constructor serve as defaults and are not required.
12838  * @param {Function} fn (optional) The default function to timeout
12839  * @param {Object} scope (optional) The default scope of that timeout
12840  * @param {Array} args (optional) The default Array of arguments
12841  */
12842 Roo.util.DelayedTask = function(fn, scope, args){
12843     var id = null, d, t;
12844
12845     var call = function(){
12846         var now = new Date().getTime();
12847         if(now - t >= d){
12848             clearInterval(id);
12849             id = null;
12850             fn.apply(scope, args || []);
12851         }
12852     };
12853     /**
12854      * Cancels any pending timeout and queues a new one
12855      * @param {Number} delay The milliseconds to delay
12856      * @param {Function} newFn (optional) Overrides function passed to constructor
12857      * @param {Object} newScope (optional) Overrides scope passed to constructor
12858      * @param {Array} newArgs (optional) Overrides args passed to constructor
12859      */
12860     this.delay = function(delay, newFn, newScope, newArgs){
12861         if(id && delay != d){
12862             this.cancel();
12863         }
12864         d = delay;
12865         t = new Date().getTime();
12866         fn = newFn || fn;
12867         scope = newScope || scope;
12868         args = newArgs || args;
12869         if(!id){
12870             id = setInterval(call, d);
12871         }
12872     };
12873
12874     /**
12875      * Cancel the last queued timeout
12876      */
12877     this.cancel = function(){
12878         if(id){
12879             clearInterval(id);
12880             id = null;
12881         }
12882     };
12883 };/*
12884  * Based on:
12885  * Ext JS Library 1.1.1
12886  * Copyright(c) 2006-2007, Ext JS, LLC.
12887  *
12888  * Originally Released Under LGPL - original licence link has changed is not relivant.
12889  *
12890  * Fork - LGPL
12891  * <script type="text/javascript">
12892  */
12893  
12894  
12895 Roo.util.TaskRunner = function(interval){
12896     interval = interval || 10;
12897     var tasks = [], removeQueue = [];
12898     var id = 0;
12899     var running = false;
12900
12901     var stopThread = function(){
12902         running = false;
12903         clearInterval(id);
12904         id = 0;
12905     };
12906
12907     var startThread = function(){
12908         if(!running){
12909             running = true;
12910             id = setInterval(runTasks, interval);
12911         }
12912     };
12913
12914     var removeTask = function(task){
12915         removeQueue.push(task);
12916         if(task.onStop){
12917             task.onStop();
12918         }
12919     };
12920
12921     var runTasks = function(){
12922         if(removeQueue.length > 0){
12923             for(var i = 0, len = removeQueue.length; i < len; i++){
12924                 tasks.remove(removeQueue[i]);
12925             }
12926             removeQueue = [];
12927             if(tasks.length < 1){
12928                 stopThread();
12929                 return;
12930             }
12931         }
12932         var now = new Date().getTime();
12933         for(var i = 0, len = tasks.length; i < len; ++i){
12934             var t = tasks[i];
12935             var itime = now - t.taskRunTime;
12936             if(t.interval <= itime){
12937                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
12938                 t.taskRunTime = now;
12939                 if(rt === false || t.taskRunCount === t.repeat){
12940                     removeTask(t);
12941                     return;
12942                 }
12943             }
12944             if(t.duration && t.duration <= (now - t.taskStartTime)){
12945                 removeTask(t);
12946             }
12947         }
12948     };
12949
12950     /**
12951      * Queues a new task.
12952      * @param {Object} task
12953      */
12954     this.start = function(task){
12955         tasks.push(task);
12956         task.taskStartTime = new Date().getTime();
12957         task.taskRunTime = 0;
12958         task.taskRunCount = 0;
12959         startThread();
12960         return task;
12961     };
12962
12963     this.stop = function(task){
12964         removeTask(task);
12965         return task;
12966     };
12967
12968     this.stopAll = function(){
12969         stopThread();
12970         for(var i = 0, len = tasks.length; i < len; i++){
12971             if(tasks[i].onStop){
12972                 tasks[i].onStop();
12973             }
12974         }
12975         tasks = [];
12976         removeQueue = [];
12977     };
12978 };
12979
12980 Roo.TaskMgr = new Roo.util.TaskRunner();/*
12981  * Based on:
12982  * Ext JS Library 1.1.1
12983  * Copyright(c) 2006-2007, Ext JS, LLC.
12984  *
12985  * Originally Released Under LGPL - original licence link has changed is not relivant.
12986  *
12987  * Fork - LGPL
12988  * <script type="text/javascript">
12989  */
12990
12991  
12992 /**
12993  * @class Roo.util.MixedCollection
12994  * @extends Roo.util.Observable
12995  * A Collection class that maintains both numeric indexes and keys and exposes events.
12996  * @constructor
12997  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
12998  * collection (defaults to false)
12999  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
13000  * and return the key value for that item.  This is used when available to look up the key on items that
13001  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
13002  * equivalent to providing an implementation for the {@link #getKey} method.
13003  */
13004 Roo.util.MixedCollection = function(allowFunctions, keyFn){
13005     this.items = [];
13006     this.map = {};
13007     this.keys = [];
13008     this.length = 0;
13009     this.addEvents({
13010         /**
13011          * @event clear
13012          * Fires when the collection is cleared.
13013          */
13014         "clear" : true,
13015         /**
13016          * @event add
13017          * Fires when an item is added to the collection.
13018          * @param {Number} index The index at which the item was added.
13019          * @param {Object} o The item added.
13020          * @param {String} key The key associated with the added item.
13021          */
13022         "add" : true,
13023         /**
13024          * @event replace
13025          * Fires when an item is replaced in the collection.
13026          * @param {String} key he key associated with the new added.
13027          * @param {Object} old The item being replaced.
13028          * @param {Object} new The new item.
13029          */
13030         "replace" : true,
13031         /**
13032          * @event remove
13033          * Fires when an item is removed from the collection.
13034          * @param {Object} o The item being removed.
13035          * @param {String} key (optional) The key associated with the removed item.
13036          */
13037         "remove" : true,
13038         "sort" : true
13039     });
13040     this.allowFunctions = allowFunctions === true;
13041     if(keyFn){
13042         this.getKey = keyFn;
13043     }
13044     Roo.util.MixedCollection.superclass.constructor.call(this);
13045 };
13046
13047 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
13048     allowFunctions : false,
13049     
13050 /**
13051  * Adds an item to the collection.
13052  * @param {String} key The key to associate with the item
13053  * @param {Object} o The item to add.
13054  * @return {Object} The item added.
13055  */
13056     add : function(key, o){
13057         if(arguments.length == 1){
13058             o = arguments[0];
13059             key = this.getKey(o);
13060         }
13061         if(typeof key == "undefined" || key === null){
13062             this.length++;
13063             this.items.push(o);
13064             this.keys.push(null);
13065         }else{
13066             var old = this.map[key];
13067             if(old){
13068                 return this.replace(key, o);
13069             }
13070             this.length++;
13071             this.items.push(o);
13072             this.map[key] = o;
13073             this.keys.push(key);
13074         }
13075         this.fireEvent("add", this.length-1, o, key);
13076         return o;
13077     },
13078        
13079 /**
13080   * MixedCollection has a generic way to fetch keys if you implement getKey.
13081 <pre><code>
13082 // normal way
13083 var mc = new Roo.util.MixedCollection();
13084 mc.add(someEl.dom.id, someEl);
13085 mc.add(otherEl.dom.id, otherEl);
13086 //and so on
13087
13088 // using getKey
13089 var mc = new Roo.util.MixedCollection();
13090 mc.getKey = function(el){
13091    return el.dom.id;
13092 };
13093 mc.add(someEl);
13094 mc.add(otherEl);
13095
13096 // or via the constructor
13097 var mc = new Roo.util.MixedCollection(false, function(el){
13098    return el.dom.id;
13099 });
13100 mc.add(someEl);
13101 mc.add(otherEl);
13102 </code></pre>
13103  * @param o {Object} The item for which to find the key.
13104  * @return {Object} The key for the passed item.
13105  */
13106     getKey : function(o){
13107          return o.id; 
13108     },
13109    
13110 /**
13111  * Replaces an item in the collection.
13112  * @param {String} key The key associated with the item to replace, or the item to replace.
13113  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
13114  * @return {Object}  The new item.
13115  */
13116     replace : function(key, o){
13117         if(arguments.length == 1){
13118             o = arguments[0];
13119             key = this.getKey(o);
13120         }
13121         var old = this.item(key);
13122         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
13123              return this.add(key, o);
13124         }
13125         var index = this.indexOfKey(key);
13126         this.items[index] = o;
13127         this.map[key] = o;
13128         this.fireEvent("replace", key, old, o);
13129         return o;
13130     },
13131    
13132 /**
13133  * Adds all elements of an Array or an Object to the collection.
13134  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
13135  * an Array of values, each of which are added to the collection.
13136  */
13137     addAll : function(objs){
13138         if(arguments.length > 1 || objs instanceof Array){
13139             var args = arguments.length > 1 ? arguments : objs;
13140             for(var i = 0, len = args.length; i < len; i++){
13141                 this.add(args[i]);
13142             }
13143         }else{
13144             for(var key in objs){
13145                 if(this.allowFunctions || typeof objs[key] != "function"){
13146                     this.add(key, objs[key]);
13147                 }
13148             }
13149         }
13150     },
13151    
13152 /**
13153  * Executes the specified function once for every item in the collection, passing each
13154  * item as the first and only parameter. returning false from the function will stop the iteration.
13155  * @param {Function} fn The function to execute for each item.
13156  * @param {Object} scope (optional) The scope in which to execute the function.
13157  */
13158     each : function(fn, scope){
13159         var items = [].concat(this.items); // each safe for removal
13160         for(var i = 0, len = items.length; i < len; i++){
13161             if(fn.call(scope || items[i], items[i], i, len) === false){
13162                 break;
13163             }
13164         }
13165     },
13166    
13167 /**
13168  * Executes the specified function once for every key in the collection, passing each
13169  * key, and its associated item as the first two parameters.
13170  * @param {Function} fn The function to execute for each item.
13171  * @param {Object} scope (optional) The scope in which to execute the function.
13172  */
13173     eachKey : function(fn, scope){
13174         for(var i = 0, len = this.keys.length; i < len; i++){
13175             fn.call(scope || window, this.keys[i], this.items[i], i, len);
13176         }
13177     },
13178    
13179 /**
13180  * Returns the first item in the collection which elicits a true return value from the
13181  * passed selection function.
13182  * @param {Function} fn The selection function to execute for each item.
13183  * @param {Object} scope (optional) The scope in which to execute the function.
13184  * @return {Object} The first item in the collection which returned true from the selection function.
13185  */
13186     find : function(fn, scope){
13187         for(var i = 0, len = this.items.length; i < len; i++){
13188             if(fn.call(scope || window, this.items[i], this.keys[i])){
13189                 return this.items[i];
13190             }
13191         }
13192         return null;
13193     },
13194    
13195 /**
13196  * Inserts an item at the specified index in the collection.
13197  * @param {Number} index The index to insert the item at.
13198  * @param {String} key The key to associate with the new item, or the item itself.
13199  * @param {Object} o  (optional) If the second parameter was a key, the new item.
13200  * @return {Object} The item inserted.
13201  */
13202     insert : function(index, key, o){
13203         if(arguments.length == 2){
13204             o = arguments[1];
13205             key = this.getKey(o);
13206         }
13207         if(index >= this.length){
13208             return this.add(key, o);
13209         }
13210         this.length++;
13211         this.items.splice(index, 0, o);
13212         if(typeof key != "undefined" && key != null){
13213             this.map[key] = o;
13214         }
13215         this.keys.splice(index, 0, key);
13216         this.fireEvent("add", index, o, key);
13217         return o;
13218     },
13219    
13220 /**
13221  * Removed an item from the collection.
13222  * @param {Object} o The item to remove.
13223  * @return {Object} The item removed.
13224  */
13225     remove : function(o){
13226         return this.removeAt(this.indexOf(o));
13227     },
13228    
13229 /**
13230  * Remove an item from a specified index in the collection.
13231  * @param {Number} index The index within the collection of the item to remove.
13232  */
13233     removeAt : function(index){
13234         if(index < this.length && index >= 0){
13235             this.length--;
13236             var o = this.items[index];
13237             this.items.splice(index, 1);
13238             var key = this.keys[index];
13239             if(typeof key != "undefined"){
13240                 delete this.map[key];
13241             }
13242             this.keys.splice(index, 1);
13243             this.fireEvent("remove", o, key);
13244         }
13245     },
13246    
13247 /**
13248  * Removed an item associated with the passed key fom the collection.
13249  * @param {String} key The key of the item to remove.
13250  */
13251     removeKey : function(key){
13252         return this.removeAt(this.indexOfKey(key));
13253     },
13254    
13255 /**
13256  * Returns the number of items in the collection.
13257  * @return {Number} the number of items in the collection.
13258  */
13259     getCount : function(){
13260         return this.length; 
13261     },
13262    
13263 /**
13264  * Returns index within the collection of the passed Object.
13265  * @param {Object} o The item to find the index of.
13266  * @return {Number} index of the item.
13267  */
13268     indexOf : function(o){
13269         if(!this.items.indexOf){
13270             for(var i = 0, len = this.items.length; i < len; i++){
13271                 if(this.items[i] == o) {
13272                     return i;
13273                 }
13274             }
13275             return -1;
13276         }else{
13277             return this.items.indexOf(o);
13278         }
13279     },
13280    
13281 /**
13282  * Returns index within the collection of the passed key.
13283  * @param {String} key The key to find the index of.
13284  * @return {Number} index of the key.
13285  */
13286     indexOfKey : function(key){
13287         if(!this.keys.indexOf){
13288             for(var i = 0, len = this.keys.length; i < len; i++){
13289                 if(this.keys[i] == key) {
13290                     return i;
13291                 }
13292             }
13293             return -1;
13294         }else{
13295             return this.keys.indexOf(key);
13296         }
13297     },
13298    
13299 /**
13300  * Returns the item associated with the passed key OR index. Key has priority over index.
13301  * @param {String/Number} key The key or index of the item.
13302  * @return {Object} The item associated with the passed key.
13303  */
13304     item : function(key){
13305         if (key === 'length') {
13306             return null;
13307         }
13308         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
13309         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
13310     },
13311     
13312 /**
13313  * Returns the item at the specified index.
13314  * @param {Number} index The index of the item.
13315  * @return {Object}
13316  */
13317     itemAt : function(index){
13318         return this.items[index];
13319     },
13320     
13321 /**
13322  * Returns the item associated with the passed key.
13323  * @param {String/Number} key The key of the item.
13324  * @return {Object} The item associated with the passed key.
13325  */
13326     key : function(key){
13327         return this.map[key];
13328     },
13329    
13330 /**
13331  * Returns true if the collection contains the passed Object as an item.
13332  * @param {Object} o  The Object to look for in the collection.
13333  * @return {Boolean} True if the collection contains the Object as an item.
13334  */
13335     contains : function(o){
13336         return this.indexOf(o) != -1;
13337     },
13338    
13339 /**
13340  * Returns true if the collection contains the passed Object as a key.
13341  * @param {String} key The key to look for in the collection.
13342  * @return {Boolean} True if the collection contains the Object as a key.
13343  */
13344     containsKey : function(key){
13345         return typeof this.map[key] != "undefined";
13346     },
13347    
13348 /**
13349  * Removes all items from the collection.
13350  */
13351     clear : function(){
13352         this.length = 0;
13353         this.items = [];
13354         this.keys = [];
13355         this.map = {};
13356         this.fireEvent("clear");
13357     },
13358    
13359 /**
13360  * Returns the first item in the collection.
13361  * @return {Object} the first item in the collection..
13362  */
13363     first : function(){
13364         return this.items[0]; 
13365     },
13366    
13367 /**
13368  * Returns the last item in the collection.
13369  * @return {Object} the last item in the collection..
13370  */
13371     last : function(){
13372         return this.items[this.length-1];   
13373     },
13374     
13375     _sort : function(property, dir, fn){
13376         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
13377         fn = fn || function(a, b){
13378             return a-b;
13379         };
13380         var c = [], k = this.keys, items = this.items;
13381         for(var i = 0, len = items.length; i < len; i++){
13382             c[c.length] = {key: k[i], value: items[i], index: i};
13383         }
13384         c.sort(function(a, b){
13385             var v = fn(a[property], b[property]) * dsc;
13386             if(v == 0){
13387                 v = (a.index < b.index ? -1 : 1);
13388             }
13389             return v;
13390         });
13391         for(var i = 0, len = c.length; i < len; i++){
13392             items[i] = c[i].value;
13393             k[i] = c[i].key;
13394         }
13395         this.fireEvent("sort", this);
13396     },
13397     
13398     /**
13399      * Sorts this collection with the passed comparison function
13400      * @param {String} direction (optional) "ASC" or "DESC"
13401      * @param {Function} fn (optional) comparison function
13402      */
13403     sort : function(dir, fn){
13404         this._sort("value", dir, fn);
13405     },
13406     
13407     /**
13408      * Sorts this collection by keys
13409      * @param {String} direction (optional) "ASC" or "DESC"
13410      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
13411      */
13412     keySort : function(dir, fn){
13413         this._sort("key", dir, fn || function(a, b){
13414             return String(a).toUpperCase()-String(b).toUpperCase();
13415         });
13416     },
13417     
13418     /**
13419      * Returns a range of items in this collection
13420      * @param {Number} startIndex (optional) defaults to 0
13421      * @param {Number} endIndex (optional) default to the last item
13422      * @return {Array} An array of items
13423      */
13424     getRange : function(start, end){
13425         var items = this.items;
13426         if(items.length < 1){
13427             return [];
13428         }
13429         start = start || 0;
13430         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
13431         var r = [];
13432         if(start <= end){
13433             for(var i = start; i <= end; i++) {
13434                     r[r.length] = items[i];
13435             }
13436         }else{
13437             for(var i = start; i >= end; i--) {
13438                     r[r.length] = items[i];
13439             }
13440         }
13441         return r;
13442     },
13443         
13444     /**
13445      * Filter the <i>objects</i> in this collection by a specific property. 
13446      * Returns a new collection that has been filtered.
13447      * @param {String} property A property on your objects
13448      * @param {String/RegExp} value Either string that the property values 
13449      * should start with or a RegExp to test against the property
13450      * @return {MixedCollection} The new filtered collection
13451      */
13452     filter : function(property, value){
13453         if(!value.exec){ // not a regex
13454             value = String(value);
13455             if(value.length == 0){
13456                 return this.clone();
13457             }
13458             value = new RegExp("^" + Roo.escapeRe(value), "i");
13459         }
13460         return this.filterBy(function(o){
13461             return o && value.test(o[property]);
13462         });
13463         },
13464     
13465     /**
13466      * Filter by a function. * Returns a new collection that has been filtered.
13467      * The passed function will be called with each 
13468      * object in the collection. If the function returns true, the value is included 
13469      * otherwise it is filtered.
13470      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
13471      * @param {Object} scope (optional) The scope of the function (defaults to this) 
13472      * @return {MixedCollection} The new filtered collection
13473      */
13474     filterBy : function(fn, scope){
13475         var r = new Roo.util.MixedCollection();
13476         r.getKey = this.getKey;
13477         var k = this.keys, it = this.items;
13478         for(var i = 0, len = it.length; i < len; i++){
13479             if(fn.call(scope||this, it[i], k[i])){
13480                                 r.add(k[i], it[i]);
13481                         }
13482         }
13483         return r;
13484     },
13485     
13486     /**
13487      * Creates a duplicate of this collection
13488      * @return {MixedCollection}
13489      */
13490     clone : function(){
13491         var r = new Roo.util.MixedCollection();
13492         var k = this.keys, it = this.items;
13493         for(var i = 0, len = it.length; i < len; i++){
13494             r.add(k[i], it[i]);
13495         }
13496         r.getKey = this.getKey;
13497         return r;
13498     }
13499 });
13500 /**
13501  * Returns the item associated with the passed key or index.
13502  * @method
13503  * @param {String/Number} key The key or index of the item.
13504  * @return {Object} The item associated with the passed key.
13505  */
13506 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
13507  * Based on:
13508  * Ext JS Library 1.1.1
13509  * Copyright(c) 2006-2007, Ext JS, LLC.
13510  *
13511  * Originally Released Under LGPL - original licence link has changed is not relivant.
13512  *
13513  * Fork - LGPL
13514  * <script type="text/javascript">
13515  */
13516 /**
13517  * @class Roo.util.JSON
13518  * Modified version of Douglas Crockford"s json.js that doesn"t
13519  * mess with the Object prototype 
13520  * http://www.json.org/js.html
13521  * @singleton
13522  */
13523 Roo.util.JSON = new (function(){
13524     var useHasOwn = {}.hasOwnProperty ? true : false;
13525     
13526     // crashes Safari in some instances
13527     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
13528     
13529     var pad = function(n) {
13530         return n < 10 ? "0" + n : n;
13531     };
13532     
13533     var m = {
13534         "\b": '\\b',
13535         "\t": '\\t',
13536         "\n": '\\n',
13537         "\f": '\\f',
13538         "\r": '\\r',
13539         '"' : '\\"',
13540         "\\": '\\\\'
13541     };
13542
13543     var encodeString = function(s){
13544         if (/["\\\x00-\x1f]/.test(s)) {
13545             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
13546                 var c = m[b];
13547                 if(c){
13548                     return c;
13549                 }
13550                 c = b.charCodeAt();
13551                 return "\\u00" +
13552                     Math.floor(c / 16).toString(16) +
13553                     (c % 16).toString(16);
13554             }) + '"';
13555         }
13556         return '"' + s + '"';
13557     };
13558     
13559     var encodeArray = function(o){
13560         var a = ["["], b, i, l = o.length, v;
13561             for (i = 0; i < l; i += 1) {
13562                 v = o[i];
13563                 switch (typeof v) {
13564                     case "undefined":
13565                     case "function":
13566                     case "unknown":
13567                         break;
13568                     default:
13569                         if (b) {
13570                             a.push(',');
13571                         }
13572                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
13573                         b = true;
13574                 }
13575             }
13576             a.push("]");
13577             return a.join("");
13578     };
13579     
13580     var encodeDate = function(o){
13581         return '"' + o.getFullYear() + "-" +
13582                 pad(o.getMonth() + 1) + "-" +
13583                 pad(o.getDate()) + "T" +
13584                 pad(o.getHours()) + ":" +
13585                 pad(o.getMinutes()) + ":" +
13586                 pad(o.getSeconds()) + '"';
13587     };
13588     
13589     /**
13590      * Encodes an Object, Array or other value
13591      * @param {Mixed} o The variable to encode
13592      * @return {String} The JSON string
13593      */
13594     this.encode = function(o)
13595     {
13596         // should this be extended to fully wrap stringify..
13597         
13598         if(typeof o == "undefined" || o === null){
13599             return "null";
13600         }else if(o instanceof Array){
13601             return encodeArray(o);
13602         }else if(o instanceof Date){
13603             return encodeDate(o);
13604         }else if(typeof o == "string"){
13605             return encodeString(o);
13606         }else if(typeof o == "number"){
13607             return isFinite(o) ? String(o) : "null";
13608         }else if(typeof o == "boolean"){
13609             return String(o);
13610         }else {
13611             var a = ["{"], b, i, v;
13612             for (i in o) {
13613                 if(!useHasOwn || o.hasOwnProperty(i)) {
13614                     v = o[i];
13615                     switch (typeof v) {
13616                     case "undefined":
13617                     case "function":
13618                     case "unknown":
13619                         break;
13620                     default:
13621                         if(b){
13622                             a.push(',');
13623                         }
13624                         a.push(this.encode(i), ":",
13625                                 v === null ? "null" : this.encode(v));
13626                         b = true;
13627                     }
13628                 }
13629             }
13630             a.push("}");
13631             return a.join("");
13632         }
13633     };
13634     
13635     /**
13636      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
13637      * @param {String} json The JSON string
13638      * @return {Object} The resulting object
13639      */
13640     this.decode = function(json){
13641         
13642         return  /** eval:var:json */ eval("(" + json + ')');
13643     };
13644 })();
13645 /** 
13646  * Shorthand for {@link Roo.util.JSON#encode}
13647  * @member Roo encode 
13648  * @method */
13649 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
13650 /** 
13651  * Shorthand for {@link Roo.util.JSON#decode}
13652  * @member Roo decode 
13653  * @method */
13654 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
13655 /*
13656  * Based on:
13657  * Ext JS Library 1.1.1
13658  * Copyright(c) 2006-2007, Ext JS, LLC.
13659  *
13660  * Originally Released Under LGPL - original licence link has changed is not relivant.
13661  *
13662  * Fork - LGPL
13663  * <script type="text/javascript">
13664  */
13665  
13666 /**
13667  * @class Roo.util.Format
13668  * Reusable data formatting functions
13669  * @singleton
13670  */
13671 Roo.util.Format = function(){
13672     var trimRe = /^\s+|\s+$/g;
13673     return {
13674         /**
13675          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
13676          * @param {String} value The string to truncate
13677          * @param {Number} length The maximum length to allow before truncating
13678          * @return {String} The converted text
13679          */
13680         ellipsis : function(value, len){
13681             if(value && value.length > len){
13682                 return value.substr(0, len-3)+"...";
13683             }
13684             return value;
13685         },
13686
13687         /**
13688          * Checks a reference and converts it to empty string if it is undefined
13689          * @param {Mixed} value Reference to check
13690          * @return {Mixed} Empty string if converted, otherwise the original value
13691          */
13692         undef : function(value){
13693             return typeof value != "undefined" ? value : "";
13694         },
13695
13696         /**
13697          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
13698          * @param {String} value The string to encode
13699          * @return {String} The encoded text
13700          */
13701         htmlEncode : function(value){
13702             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
13703         },
13704
13705         /**
13706          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
13707          * @param {String} value The string to decode
13708          * @return {String} The decoded text
13709          */
13710         htmlDecode : function(value){
13711             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
13712         },
13713
13714         /**
13715          * Trims any whitespace from either side of a string
13716          * @param {String} value The text to trim
13717          * @return {String} The trimmed text
13718          */
13719         trim : function(value){
13720             return String(value).replace(trimRe, "");
13721         },
13722
13723         /**
13724          * Returns a substring from within an original string
13725          * @param {String} value The original text
13726          * @param {Number} start The start index of the substring
13727          * @param {Number} length The length of the substring
13728          * @return {String} The substring
13729          */
13730         substr : function(value, start, length){
13731             return String(value).substr(start, length);
13732         },
13733
13734         /**
13735          * Converts a string to all lower case letters
13736          * @param {String} value The text to convert
13737          * @return {String} The converted text
13738          */
13739         lowercase : function(value){
13740             return String(value).toLowerCase();
13741         },
13742
13743         /**
13744          * Converts a string to all upper case letters
13745          * @param {String} value The text to convert
13746          * @return {String} The converted text
13747          */
13748         uppercase : function(value){
13749             return String(value).toUpperCase();
13750         },
13751
13752         /**
13753          * Converts the first character only of a string to upper case
13754          * @param {String} value The text to convert
13755          * @return {String} The converted text
13756          */
13757         capitalize : function(value){
13758             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
13759         },
13760
13761         // private
13762         call : function(value, fn){
13763             if(arguments.length > 2){
13764                 var args = Array.prototype.slice.call(arguments, 2);
13765                 args.unshift(value);
13766                  
13767                 return /** eval:var:value */  eval(fn).apply(window, args);
13768             }else{
13769                 /** eval:var:value */
13770                 return /** eval:var:value */ eval(fn).call(window, value);
13771             }
13772         },
13773
13774        
13775         /**
13776          * safer version of Math.toFixed..??/
13777          * @param {Number/String} value The numeric value to format
13778          * @param {Number/String} value Decimal places 
13779          * @return {String} The formatted currency string
13780          */
13781         toFixed : function(v, n)
13782         {
13783             // why not use to fixed - precision is buggered???
13784             if (!n) {
13785                 return Math.round(v-0);
13786             }
13787             var fact = Math.pow(10,n+1);
13788             v = (Math.round((v-0)*fact))/fact;
13789             var z = (''+fact).substring(2);
13790             if (v == Math.floor(v)) {
13791                 return Math.floor(v) + '.' + z;
13792             }
13793             
13794             // now just padd decimals..
13795             var ps = String(v).split('.');
13796             var fd = (ps[1] + z);
13797             var r = fd.substring(0,n); 
13798             var rm = fd.substring(n); 
13799             if (rm < 5) {
13800                 return ps[0] + '.' + r;
13801             }
13802             r*=1; // turn it into a number;
13803             r++;
13804             if (String(r).length != n) {
13805                 ps[0]*=1;
13806                 ps[0]++;
13807                 r = String(r).substring(1); // chop the end off.
13808             }
13809             
13810             return ps[0] + '.' + r;
13811              
13812         },
13813         
13814         /**
13815          * Format a number as US currency
13816          * @param {Number/String} value The numeric value to format
13817          * @return {String} The formatted currency string
13818          */
13819         usMoney : function(v){
13820             return '$' + Roo.util.Format.number(v);
13821         },
13822         
13823         /**
13824          * Format a number
13825          * eventually this should probably emulate php's number_format
13826          * @param {Number/String} value The numeric value to format
13827          * @param {Number} decimals number of decimal places
13828          * @param {String} delimiter for thousands (default comma)
13829          * @return {String} The formatted currency string
13830          */
13831         number : function(v, decimals, thousandsDelimiter)
13832         {
13833             // multiply and round.
13834             decimals = typeof(decimals) == 'undefined' ? 2 : decimals;
13835             thousandsDelimiter = typeof(thousandsDelimiter) == 'undefined' ? ',' : thousandsDelimiter;
13836             
13837             var mul = Math.pow(10, decimals);
13838             var zero = String(mul).substring(1);
13839             v = (Math.round((v-0)*mul))/mul;
13840             
13841             // if it's '0' number.. then
13842             
13843             //v = (v == Math.floor(v)) ? v + "." + zero : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
13844             v = String(v);
13845             var ps = v.split('.');
13846             var whole = ps[0];
13847             
13848             var r = /(\d+)(\d{3})/;
13849             // add comma's
13850             
13851             if(thousandsDelimiter.length != 0) {
13852                 whole = whole.replace(/\B(?=(\d{3})+(?!\d))/g, thousandsDelimiter );
13853             } 
13854             
13855             var sub = ps[1] ?
13856                     // has decimals..
13857                     (decimals ?  ('.'+ ps[1] + zero.substring(ps[1].length)) : '') :
13858                     // does not have decimals
13859                     (decimals ? ('.' + zero) : '');
13860             
13861             
13862             return whole + sub ;
13863         },
13864         
13865         /**
13866          * Parse a value into a formatted date using the specified format pattern.
13867          * @param {Mixed} value The value to format
13868          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
13869          * @return {String} The formatted date string
13870          */
13871         date : function(v, format){
13872             if(!v){
13873                 return "";
13874             }
13875             if(!(v instanceof Date)){
13876                 v = new Date(Date.parse(v));
13877             }
13878             return v.dateFormat(format || Roo.util.Format.defaults.date);
13879         },
13880
13881         /**
13882          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
13883          * @param {String} format Any valid date format string
13884          * @return {Function} The date formatting function
13885          */
13886         dateRenderer : function(format){
13887             return function(v){
13888                 return Roo.util.Format.date(v, format);  
13889             };
13890         },
13891
13892         // private
13893         stripTagsRE : /<\/?[^>]+>/gi,
13894         
13895         /**
13896          * Strips all HTML tags
13897          * @param {Mixed} value The text from which to strip tags
13898          * @return {String} The stripped text
13899          */
13900         stripTags : function(v){
13901             return !v ? v : String(v).replace(this.stripTagsRE, "");
13902         }
13903     };
13904 }();
13905 Roo.util.Format.defaults = {
13906     date : 'd/M/Y'
13907 };/*
13908  * Based on:
13909  * Ext JS Library 1.1.1
13910  * Copyright(c) 2006-2007, Ext JS, LLC.
13911  *
13912  * Originally Released Under LGPL - original licence link has changed is not relivant.
13913  *
13914  * Fork - LGPL
13915  * <script type="text/javascript">
13916  */
13917
13918
13919  
13920
13921 /**
13922  * @class Roo.MasterTemplate
13923  * @extends Roo.Template
13924  * Provides a template that can have child templates. The syntax is:
13925 <pre><code>
13926 var t = new Roo.MasterTemplate(
13927         '&lt;select name="{name}"&gt;',
13928                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
13929         '&lt;/select&gt;'
13930 );
13931 t.add('options', {value: 'foo', text: 'bar'});
13932 // or you can add multiple child elements in one shot
13933 t.addAll('options', [
13934     {value: 'foo', text: 'bar'},
13935     {value: 'foo2', text: 'bar2'},
13936     {value: 'foo3', text: 'bar3'}
13937 ]);
13938 // then append, applying the master template values
13939 t.append('my-form', {name: 'my-select'});
13940 </code></pre>
13941 * A name attribute for the child template is not required if you have only one child
13942 * template or you want to refer to them by index.
13943  */
13944 Roo.MasterTemplate = function(){
13945     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
13946     this.originalHtml = this.html;
13947     var st = {};
13948     var m, re = this.subTemplateRe;
13949     re.lastIndex = 0;
13950     var subIndex = 0;
13951     while(m = re.exec(this.html)){
13952         var name = m[1], content = m[2];
13953         st[subIndex] = {
13954             name: name,
13955             index: subIndex,
13956             buffer: [],
13957             tpl : new Roo.Template(content)
13958         };
13959         if(name){
13960             st[name] = st[subIndex];
13961         }
13962         st[subIndex].tpl.compile();
13963         st[subIndex].tpl.call = this.call.createDelegate(this);
13964         subIndex++;
13965     }
13966     this.subCount = subIndex;
13967     this.subs = st;
13968 };
13969 Roo.extend(Roo.MasterTemplate, Roo.Template, {
13970     /**
13971     * The regular expression used to match sub templates
13972     * @type RegExp
13973     * @property
13974     */
13975     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
13976
13977     /**
13978      * Applies the passed values to a child template.
13979      * @param {String/Number} name (optional) The name or index of the child template
13980      * @param {Array/Object} values The values to be applied to the template
13981      * @return {MasterTemplate} this
13982      */
13983      add : function(name, values){
13984         if(arguments.length == 1){
13985             values = arguments[0];
13986             name = 0;
13987         }
13988         var s = this.subs[name];
13989         s.buffer[s.buffer.length] = s.tpl.apply(values);
13990         return this;
13991     },
13992
13993     /**
13994      * Applies all the passed values to a child template.
13995      * @param {String/Number} name (optional) The name or index of the child template
13996      * @param {Array} values The values to be applied to the template, this should be an array of objects.
13997      * @param {Boolean} reset (optional) True to reset the template first
13998      * @return {MasterTemplate} this
13999      */
14000     fill : function(name, values, reset){
14001         var a = arguments;
14002         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
14003             values = a[0];
14004             name = 0;
14005             reset = a[1];
14006         }
14007         if(reset){
14008             this.reset();
14009         }
14010         for(var i = 0, len = values.length; i < len; i++){
14011             this.add(name, values[i]);
14012         }
14013         return this;
14014     },
14015
14016     /**
14017      * Resets the template for reuse
14018      * @return {MasterTemplate} this
14019      */
14020      reset : function(){
14021         var s = this.subs;
14022         for(var i = 0; i < this.subCount; i++){
14023             s[i].buffer = [];
14024         }
14025         return this;
14026     },
14027
14028     applyTemplate : function(values){
14029         var s = this.subs;
14030         var replaceIndex = -1;
14031         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
14032             return s[++replaceIndex].buffer.join("");
14033         });
14034         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
14035     },
14036
14037     apply : function(){
14038         return this.applyTemplate.apply(this, arguments);
14039     },
14040
14041     compile : function(){return this;}
14042 });
14043
14044 /**
14045  * Alias for fill().
14046  * @method
14047  */
14048 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
14049  /**
14050  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
14051  * var tpl = Roo.MasterTemplate.from('element-id');
14052  * @param {String/HTMLElement} el
14053  * @param {Object} config
14054  * @static
14055  */
14056 Roo.MasterTemplate.from = function(el, config){
14057     el = Roo.getDom(el);
14058     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
14059 };/*
14060  * Based on:
14061  * Ext JS Library 1.1.1
14062  * Copyright(c) 2006-2007, Ext JS, LLC.
14063  *
14064  * Originally Released Under LGPL - original licence link has changed is not relivant.
14065  *
14066  * Fork - LGPL
14067  * <script type="text/javascript">
14068  */
14069
14070  
14071 /**
14072  * @class Roo.util.CSS
14073  * Utility class for manipulating CSS rules
14074  * @singleton
14075  */
14076 Roo.util.CSS = function(){
14077         var rules = null;
14078         var doc = document;
14079
14080     var camelRe = /(-[a-z])/gi;
14081     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
14082
14083    return {
14084    /**
14085     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
14086     * tag and appended to the HEAD of the document.
14087     * @param {String|Object} cssText The text containing the css rules
14088     * @param {String} id An id to add to the stylesheet for later removal
14089     * @return {StyleSheet}
14090     */
14091     createStyleSheet : function(cssText, id){
14092         var ss;
14093         var head = doc.getElementsByTagName("head")[0];
14094         var nrules = doc.createElement("style");
14095         nrules.setAttribute("type", "text/css");
14096         if(id){
14097             nrules.setAttribute("id", id);
14098         }
14099         if (typeof(cssText) != 'string') {
14100             // support object maps..
14101             // not sure if this a good idea.. 
14102             // perhaps it should be merged with the general css handling
14103             // and handle js style props.
14104             var cssTextNew = [];
14105             for(var n in cssText) {
14106                 var citems = [];
14107                 for(var k in cssText[n]) {
14108                     citems.push( k + ' : ' +cssText[n][k] + ';' );
14109                 }
14110                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
14111                 
14112             }
14113             cssText = cssTextNew.join("\n");
14114             
14115         }
14116        
14117        
14118        if(Roo.isIE){
14119            head.appendChild(nrules);
14120            ss = nrules.styleSheet;
14121            ss.cssText = cssText;
14122        }else{
14123            try{
14124                 nrules.appendChild(doc.createTextNode(cssText));
14125            }catch(e){
14126                nrules.cssText = cssText; 
14127            }
14128            head.appendChild(nrules);
14129            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
14130        }
14131        this.cacheStyleSheet(ss);
14132        return ss;
14133    },
14134
14135    /**
14136     * Removes a style or link tag by id
14137     * @param {String} id The id of the tag
14138     */
14139    removeStyleSheet : function(id){
14140        var existing = doc.getElementById(id);
14141        if(existing){
14142            existing.parentNode.removeChild(existing);
14143        }
14144    },
14145
14146    /**
14147     * Dynamically swaps an existing stylesheet reference for a new one
14148     * @param {String} id The id of an existing link tag to remove
14149     * @param {String} url The href of the new stylesheet to include
14150     */
14151    swapStyleSheet : function(id, url){
14152        this.removeStyleSheet(id);
14153        var ss = doc.createElement("link");
14154        ss.setAttribute("rel", "stylesheet");
14155        ss.setAttribute("type", "text/css");
14156        ss.setAttribute("id", id);
14157        ss.setAttribute("href", url);
14158        doc.getElementsByTagName("head")[0].appendChild(ss);
14159    },
14160    
14161    /**
14162     * Refresh the rule cache if you have dynamically added stylesheets
14163     * @return {Object} An object (hash) of rules indexed by selector
14164     */
14165    refreshCache : function(){
14166        return this.getRules(true);
14167    },
14168
14169    // private
14170    cacheStyleSheet : function(stylesheet){
14171        if(!rules){
14172            rules = {};
14173        }
14174        try{// try catch for cross domain access issue
14175            var ssRules = stylesheet.cssRules || stylesheet.rules;
14176            for(var j = ssRules.length-1; j >= 0; --j){
14177                rules[ssRules[j].selectorText] = ssRules[j];
14178            }
14179        }catch(e){}
14180    },
14181    
14182    /**
14183     * Gets all css rules for the document
14184     * @param {Boolean} refreshCache true to refresh the internal cache
14185     * @return {Object} An object (hash) of rules indexed by selector
14186     */
14187    getRules : function(refreshCache){
14188                 if(rules == null || refreshCache){
14189                         rules = {};
14190                         var ds = doc.styleSheets;
14191                         for(var i =0, len = ds.length; i < len; i++){
14192                             try{
14193                         this.cacheStyleSheet(ds[i]);
14194                     }catch(e){} 
14195                 }
14196                 }
14197                 return rules;
14198         },
14199         
14200         /**
14201     * Gets an an individual CSS rule by selector(s)
14202     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
14203     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
14204     * @return {CSSRule} The CSS rule or null if one is not found
14205     */
14206    getRule : function(selector, refreshCache){
14207                 var rs = this.getRules(refreshCache);
14208                 if(!(selector instanceof Array)){
14209                     return rs[selector];
14210                 }
14211                 for(var i = 0; i < selector.length; i++){
14212                         if(rs[selector[i]]){
14213                                 return rs[selector[i]];
14214                         }
14215                 }
14216                 return null;
14217         },
14218         
14219         
14220         /**
14221     * Updates a rule property
14222     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
14223     * @param {String} property The css property
14224     * @param {String} value The new value for the property
14225     * @return {Boolean} true If a rule was found and updated
14226     */
14227    updateRule : function(selector, property, value){
14228                 if(!(selector instanceof Array)){
14229                         var rule = this.getRule(selector);
14230                         if(rule){
14231                                 rule.style[property.replace(camelRe, camelFn)] = value;
14232                                 return true;
14233                         }
14234                 }else{
14235                         for(var i = 0; i < selector.length; i++){
14236                                 if(this.updateRule(selector[i], property, value)){
14237                                         return true;
14238                                 }
14239                         }
14240                 }
14241                 return false;
14242         }
14243    };   
14244 }();/*
14245  * Based on:
14246  * Ext JS Library 1.1.1
14247  * Copyright(c) 2006-2007, Ext JS, LLC.
14248  *
14249  * Originally Released Under LGPL - original licence link has changed is not relivant.
14250  *
14251  * Fork - LGPL
14252  * <script type="text/javascript">
14253  */
14254
14255  
14256
14257 /**
14258  * @class Roo.util.ClickRepeater
14259  * @extends Roo.util.Observable
14260  * 
14261  * A wrapper class which can be applied to any element. Fires a "click" event while the
14262  * mouse is pressed. The interval between firings may be specified in the config but
14263  * defaults to 10 milliseconds.
14264  * 
14265  * Optionally, a CSS class may be applied to the element during the time it is pressed.
14266  * 
14267  * @cfg {String/HTMLElement/Element} el The element to act as a button.
14268  * @cfg {Number} delay The initial delay before the repeating event begins firing.
14269  * Similar to an autorepeat key delay.
14270  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
14271  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
14272  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
14273  *           "interval" and "delay" are ignored. "immediate" is honored.
14274  * @cfg {Boolean} preventDefault True to prevent the default click event
14275  * @cfg {Boolean} stopDefault True to stop the default click event
14276  * 
14277  * @history
14278  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
14279  *     2007-02-02 jvs Renamed to ClickRepeater
14280  *   2007-02-03 jvs Modifications for FF Mac and Safari 
14281  *
14282  *  @constructor
14283  * @param {String/HTMLElement/Element} el The element to listen on
14284  * @param {Object} config
14285  **/
14286 Roo.util.ClickRepeater = function(el, config)
14287 {
14288     this.el = Roo.get(el);
14289     this.el.unselectable();
14290
14291     Roo.apply(this, config);
14292
14293     this.addEvents({
14294     /**
14295      * @event mousedown
14296      * Fires when the mouse button is depressed.
14297      * @param {Roo.util.ClickRepeater} this
14298      */
14299         "mousedown" : true,
14300     /**
14301      * @event click
14302      * Fires on a specified interval during the time the element is pressed.
14303      * @param {Roo.util.ClickRepeater} this
14304      */
14305         "click" : true,
14306     /**
14307      * @event mouseup
14308      * Fires when the mouse key is released.
14309      * @param {Roo.util.ClickRepeater} this
14310      */
14311         "mouseup" : true
14312     });
14313
14314     this.el.on("mousedown", this.handleMouseDown, this);
14315     if(this.preventDefault || this.stopDefault){
14316         this.el.on("click", function(e){
14317             if(this.preventDefault){
14318                 e.preventDefault();
14319             }
14320             if(this.stopDefault){
14321                 e.stopEvent();
14322             }
14323         }, this);
14324     }
14325
14326     // allow inline handler
14327     if(this.handler){
14328         this.on("click", this.handler,  this.scope || this);
14329     }
14330
14331     Roo.util.ClickRepeater.superclass.constructor.call(this);
14332 };
14333
14334 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
14335     interval : 20,
14336     delay: 250,
14337     preventDefault : true,
14338     stopDefault : false,
14339     timer : 0,
14340
14341     // private
14342     handleMouseDown : function(){
14343         clearTimeout(this.timer);
14344         this.el.blur();
14345         if(this.pressClass){
14346             this.el.addClass(this.pressClass);
14347         }
14348         this.mousedownTime = new Date();
14349
14350         Roo.get(document).on("mouseup", this.handleMouseUp, this);
14351         this.el.on("mouseout", this.handleMouseOut, this);
14352
14353         this.fireEvent("mousedown", this);
14354         this.fireEvent("click", this);
14355         
14356         this.timer = this.click.defer(this.delay || this.interval, this);
14357     },
14358
14359     // private
14360     click : function(){
14361         this.fireEvent("click", this);
14362         this.timer = this.click.defer(this.getInterval(), this);
14363     },
14364
14365     // private
14366     getInterval: function(){
14367         if(!this.accelerate){
14368             return this.interval;
14369         }
14370         var pressTime = this.mousedownTime.getElapsed();
14371         if(pressTime < 500){
14372             return 400;
14373         }else if(pressTime < 1700){
14374             return 320;
14375         }else if(pressTime < 2600){
14376             return 250;
14377         }else if(pressTime < 3500){
14378             return 180;
14379         }else if(pressTime < 4400){
14380             return 140;
14381         }else if(pressTime < 5300){
14382             return 80;
14383         }else if(pressTime < 6200){
14384             return 50;
14385         }else{
14386             return 10;
14387         }
14388     },
14389
14390     // private
14391     handleMouseOut : function(){
14392         clearTimeout(this.timer);
14393         if(this.pressClass){
14394             this.el.removeClass(this.pressClass);
14395         }
14396         this.el.on("mouseover", this.handleMouseReturn, this);
14397     },
14398
14399     // private
14400     handleMouseReturn : function(){
14401         this.el.un("mouseover", this.handleMouseReturn);
14402         if(this.pressClass){
14403             this.el.addClass(this.pressClass);
14404         }
14405         this.click();
14406     },
14407
14408     // private
14409     handleMouseUp : function(){
14410         clearTimeout(this.timer);
14411         this.el.un("mouseover", this.handleMouseReturn);
14412         this.el.un("mouseout", this.handleMouseOut);
14413         Roo.get(document).un("mouseup", this.handleMouseUp);
14414         this.el.removeClass(this.pressClass);
14415         this.fireEvent("mouseup", this);
14416     }
14417 });/*
14418  * Based on:
14419  * Ext JS Library 1.1.1
14420  * Copyright(c) 2006-2007, Ext JS, LLC.
14421  *
14422  * Originally Released Under LGPL - original licence link has changed is not relivant.
14423  *
14424  * Fork - LGPL
14425  * <script type="text/javascript">
14426  */
14427
14428  
14429 /**
14430  * @class Roo.KeyNav
14431  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
14432  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
14433  * way to implement custom navigation schemes for any UI component.</p>
14434  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
14435  * pageUp, pageDown, del, home, end.  Usage:</p>
14436  <pre><code>
14437 var nav = new Roo.KeyNav("my-element", {
14438     "left" : function(e){
14439         this.moveLeft(e.ctrlKey);
14440     },
14441     "right" : function(e){
14442         this.moveRight(e.ctrlKey);
14443     },
14444     "enter" : function(e){
14445         this.save();
14446     },
14447     scope : this
14448 });
14449 </code></pre>
14450  * @constructor
14451  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14452  * @param {Object} config The config
14453  */
14454 Roo.KeyNav = function(el, config){
14455     this.el = Roo.get(el);
14456     Roo.apply(this, config);
14457     if(!this.disabled){
14458         this.disabled = true;
14459         this.enable();
14460     }
14461 };
14462
14463 Roo.KeyNav.prototype = {
14464     /**
14465      * @cfg {Boolean} disabled
14466      * True to disable this KeyNav instance (defaults to false)
14467      */
14468     disabled : false,
14469     /**
14470      * @cfg {String} defaultEventAction
14471      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
14472      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
14473      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
14474      */
14475     defaultEventAction: "stopEvent",
14476     /**
14477      * @cfg {Boolean} forceKeyDown
14478      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
14479      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
14480      * handle keydown instead of keypress.
14481      */
14482     forceKeyDown : false,
14483
14484     // private
14485     prepareEvent : function(e){
14486         var k = e.getKey();
14487         var h = this.keyToHandler[k];
14488         //if(h && this[h]){
14489         //    e.stopPropagation();
14490         //}
14491         if(Roo.isSafari && h && k >= 37 && k <= 40){
14492             e.stopEvent();
14493         }
14494     },
14495
14496     // private
14497     relay : function(e){
14498         var k = e.getKey();
14499         var h = this.keyToHandler[k];
14500         if(h && this[h]){
14501             if(this.doRelay(e, this[h], h) !== true){
14502                 e[this.defaultEventAction]();
14503             }
14504         }
14505     },
14506
14507     // private
14508     doRelay : function(e, h, hname){
14509         return h.call(this.scope || this, e);
14510     },
14511
14512     // possible handlers
14513     enter : false,
14514     left : false,
14515     right : false,
14516     up : false,
14517     down : false,
14518     tab : false,
14519     esc : false,
14520     pageUp : false,
14521     pageDown : false,
14522     del : false,
14523     home : false,
14524     end : false,
14525
14526     // quick lookup hash
14527     keyToHandler : {
14528         37 : "left",
14529         39 : "right",
14530         38 : "up",
14531         40 : "down",
14532         33 : "pageUp",
14533         34 : "pageDown",
14534         46 : "del",
14535         36 : "home",
14536         35 : "end",
14537         13 : "enter",
14538         27 : "esc",
14539         9  : "tab"
14540     },
14541
14542         /**
14543          * Enable this KeyNav
14544          */
14545         enable: function(){
14546                 if(this.disabled){
14547             // ie won't do special keys on keypress, no one else will repeat keys with keydown
14548             // the EventObject will normalize Safari automatically
14549             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14550                 this.el.on("keydown", this.relay,  this);
14551             }else{
14552                 this.el.on("keydown", this.prepareEvent,  this);
14553                 this.el.on("keypress", this.relay,  this);
14554             }
14555                     this.disabled = false;
14556                 }
14557         },
14558
14559         /**
14560          * Disable this KeyNav
14561          */
14562         disable: function(){
14563                 if(!this.disabled){
14564                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14565                 this.el.un("keydown", this.relay);
14566             }else{
14567                 this.el.un("keydown", this.prepareEvent);
14568                 this.el.un("keypress", this.relay);
14569             }
14570                     this.disabled = true;
14571                 }
14572         }
14573 };/*
14574  * Based on:
14575  * Ext JS Library 1.1.1
14576  * Copyright(c) 2006-2007, Ext JS, LLC.
14577  *
14578  * Originally Released Under LGPL - original licence link has changed is not relivant.
14579  *
14580  * Fork - LGPL
14581  * <script type="text/javascript">
14582  */
14583
14584  
14585 /**
14586  * @class Roo.KeyMap
14587  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
14588  * The constructor accepts the same config object as defined by {@link #addBinding}.
14589  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
14590  * combination it will call the function with this signature (if the match is a multi-key
14591  * combination the callback will still be called only once): (String key, Roo.EventObject e)
14592  * A KeyMap can also handle a string representation of keys.<br />
14593  * Usage:
14594  <pre><code>
14595 // map one key by key code
14596 var map = new Roo.KeyMap("my-element", {
14597     key: 13, // or Roo.EventObject.ENTER
14598     fn: myHandler,
14599     scope: myObject
14600 });
14601
14602 // map multiple keys to one action by string
14603 var map = new Roo.KeyMap("my-element", {
14604     key: "a\r\n\t",
14605     fn: myHandler,
14606     scope: myObject
14607 });
14608
14609 // map multiple keys to multiple actions by strings and array of codes
14610 var map = new Roo.KeyMap("my-element", [
14611     {
14612         key: [10,13],
14613         fn: function(){ alert("Return was pressed"); }
14614     }, {
14615         key: "abc",
14616         fn: function(){ alert('a, b or c was pressed'); }
14617     }, {
14618         key: "\t",
14619         ctrl:true,
14620         shift:true,
14621         fn: function(){ alert('Control + shift + tab was pressed.'); }
14622     }
14623 ]);
14624 </code></pre>
14625  * <b>Note: A KeyMap starts enabled</b>
14626  * @constructor
14627  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14628  * @param {Object} config The config (see {@link #addBinding})
14629  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
14630  */
14631 Roo.KeyMap = function(el, config, eventName){
14632     this.el  = Roo.get(el);
14633     this.eventName = eventName || "keydown";
14634     this.bindings = [];
14635     if(config){
14636         this.addBinding(config);
14637     }
14638     this.enable();
14639 };
14640
14641 Roo.KeyMap.prototype = {
14642     /**
14643      * True to stop the event from bubbling and prevent the default browser action if the
14644      * key was handled by the KeyMap (defaults to false)
14645      * @type Boolean
14646      */
14647     stopEvent : false,
14648
14649     /**
14650      * Add a new binding to this KeyMap. The following config object properties are supported:
14651      * <pre>
14652 Property    Type             Description
14653 ----------  ---------------  ----------------------------------------------------------------------
14654 key         String/Array     A single keycode or an array of keycodes to handle
14655 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
14656 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
14657 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
14658 fn          Function         The function to call when KeyMap finds the expected key combination
14659 scope       Object           The scope of the callback function
14660 </pre>
14661      *
14662      * Usage:
14663      * <pre><code>
14664 // Create a KeyMap
14665 var map = new Roo.KeyMap(document, {
14666     key: Roo.EventObject.ENTER,
14667     fn: handleKey,
14668     scope: this
14669 });
14670
14671 //Add a new binding to the existing KeyMap later
14672 map.addBinding({
14673     key: 'abc',
14674     shift: true,
14675     fn: handleKey,
14676     scope: this
14677 });
14678 </code></pre>
14679      * @param {Object/Array} config A single KeyMap config or an array of configs
14680      */
14681         addBinding : function(config){
14682         if(config instanceof Array){
14683             for(var i = 0, len = config.length; i < len; i++){
14684                 this.addBinding(config[i]);
14685             }
14686             return;
14687         }
14688         var keyCode = config.key,
14689             shift = config.shift, 
14690             ctrl = config.ctrl, 
14691             alt = config.alt,
14692             fn = config.fn,
14693             scope = config.scope;
14694         if(typeof keyCode == "string"){
14695             var ks = [];
14696             var keyString = keyCode.toUpperCase();
14697             for(var j = 0, len = keyString.length; j < len; j++){
14698                 ks.push(keyString.charCodeAt(j));
14699             }
14700             keyCode = ks;
14701         }
14702         var keyArray = keyCode instanceof Array;
14703         var handler = function(e){
14704             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14705                 var k = e.getKey();
14706                 if(keyArray){
14707                     for(var i = 0, len = keyCode.length; i < len; i++){
14708                         if(keyCode[i] == k){
14709                           if(this.stopEvent){
14710                               e.stopEvent();
14711                           }
14712                           fn.call(scope || window, k, e);
14713                           return;
14714                         }
14715                     }
14716                 }else{
14717                     if(k == keyCode){
14718                         if(this.stopEvent){
14719                            e.stopEvent();
14720                         }
14721                         fn.call(scope || window, k, e);
14722                     }
14723                 }
14724             }
14725         };
14726         this.bindings.push(handler);  
14727         },
14728
14729     /**
14730      * Shorthand for adding a single key listener
14731      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
14732      * following options:
14733      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14734      * @param {Function} fn The function to call
14735      * @param {Object} scope (optional) The scope of the function
14736      */
14737     on : function(key, fn, scope){
14738         var keyCode, shift, ctrl, alt;
14739         if(typeof key == "object" && !(key instanceof Array)){
14740             keyCode = key.key;
14741             shift = key.shift;
14742             ctrl = key.ctrl;
14743             alt = key.alt;
14744         }else{
14745             keyCode = key;
14746         }
14747         this.addBinding({
14748             key: keyCode,
14749             shift: shift,
14750             ctrl: ctrl,
14751             alt: alt,
14752             fn: fn,
14753             scope: scope
14754         })
14755     },
14756
14757     // private
14758     handleKeyDown : function(e){
14759             if(this.enabled){ //just in case
14760             var b = this.bindings;
14761             for(var i = 0, len = b.length; i < len; i++){
14762                 b[i].call(this, e);
14763             }
14764             }
14765         },
14766         
14767         /**
14768          * Returns true if this KeyMap is enabled
14769          * @return {Boolean} 
14770          */
14771         isEnabled : function(){
14772             return this.enabled;  
14773         },
14774         
14775         /**
14776          * Enables this KeyMap
14777          */
14778         enable: function(){
14779                 if(!this.enabled){
14780                     this.el.on(this.eventName, this.handleKeyDown, this);
14781                     this.enabled = true;
14782                 }
14783         },
14784
14785         /**
14786          * Disable this KeyMap
14787          */
14788         disable: function(){
14789                 if(this.enabled){
14790                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
14791                     this.enabled = false;
14792                 }
14793         }
14794 };/*
14795  * Based on:
14796  * Ext JS Library 1.1.1
14797  * Copyright(c) 2006-2007, Ext JS, LLC.
14798  *
14799  * Originally Released Under LGPL - original licence link has changed is not relivant.
14800  *
14801  * Fork - LGPL
14802  * <script type="text/javascript">
14803  */
14804
14805  
14806 /**
14807  * @class Roo.util.TextMetrics
14808  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
14809  * wide, in pixels, a given block of text will be.
14810  * @singleton
14811  */
14812 Roo.util.TextMetrics = function(){
14813     var shared;
14814     return {
14815         /**
14816          * Measures the size of the specified text
14817          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
14818          * that can affect the size of the rendered text
14819          * @param {String} text The text to measure
14820          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14821          * in order to accurately measure the text height
14822          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14823          */
14824         measure : function(el, text, fixedWidth){
14825             if(!shared){
14826                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
14827             }
14828             shared.bind(el);
14829             shared.setFixedWidth(fixedWidth || 'auto');
14830             return shared.getSize(text);
14831         },
14832
14833         /**
14834          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
14835          * the overhead of multiple calls to initialize the style properties on each measurement.
14836          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
14837          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14838          * in order to accurately measure the text height
14839          * @return {Roo.util.TextMetrics.Instance} instance The new instance
14840          */
14841         createInstance : function(el, fixedWidth){
14842             return Roo.util.TextMetrics.Instance(el, fixedWidth);
14843         }
14844     };
14845 }();
14846
14847  
14848
14849 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
14850     var ml = new Roo.Element(document.createElement('div'));
14851     document.body.appendChild(ml.dom);
14852     ml.position('absolute');
14853     ml.setLeftTop(-1000, -1000);
14854     ml.hide();
14855
14856     if(fixedWidth){
14857         ml.setWidth(fixedWidth);
14858     }
14859      
14860     var instance = {
14861         /**
14862          * Returns the size of the specified text based on the internal element's style and width properties
14863          * @memberOf Roo.util.TextMetrics.Instance#
14864          * @param {String} text The text to measure
14865          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14866          */
14867         getSize : function(text){
14868             ml.update(text);
14869             var s = ml.getSize();
14870             ml.update('');
14871             return s;
14872         },
14873
14874         /**
14875          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
14876          * that can affect the size of the rendered text
14877          * @memberOf Roo.util.TextMetrics.Instance#
14878          * @param {String/HTMLElement} el The element, dom node or id
14879          */
14880         bind : function(el){
14881             ml.setStyle(
14882                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
14883             );
14884         },
14885
14886         /**
14887          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
14888          * to set a fixed width in order to accurately measure the text height.
14889          * @memberOf Roo.util.TextMetrics.Instance#
14890          * @param {Number} width The width to set on the element
14891          */
14892         setFixedWidth : function(width){
14893             ml.setWidth(width);
14894         },
14895
14896         /**
14897          * Returns the measured width of the specified text
14898          * @memberOf Roo.util.TextMetrics.Instance#
14899          * @param {String} text The text to measure
14900          * @return {Number} width The width in pixels
14901          */
14902         getWidth : function(text){
14903             ml.dom.style.width = 'auto';
14904             return this.getSize(text).width;
14905         },
14906
14907         /**
14908          * Returns the measured height of the specified text.  For multiline text, be sure to call
14909          * {@link #setFixedWidth} if necessary.
14910          * @memberOf Roo.util.TextMetrics.Instance#
14911          * @param {String} text The text to measure
14912          * @return {Number} height The height in pixels
14913          */
14914         getHeight : function(text){
14915             return this.getSize(text).height;
14916         }
14917     };
14918
14919     instance.bind(bindTo);
14920
14921     return instance;
14922 };
14923
14924 // backwards compat
14925 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
14926  * Based on:
14927  * Ext JS Library 1.1.1
14928  * Copyright(c) 2006-2007, Ext JS, LLC.
14929  *
14930  * Originally Released Under LGPL - original licence link has changed is not relivant.
14931  *
14932  * Fork - LGPL
14933  * <script type="text/javascript">
14934  */
14935
14936 /**
14937  * @class Roo.state.Provider
14938  * Abstract base class for state provider implementations. This class provides methods
14939  * for encoding and decoding <b>typed</b> variables including dates and defines the 
14940  * Provider interface.
14941  */
14942 Roo.state.Provider = function(){
14943     /**
14944      * @event statechange
14945      * Fires when a state change occurs.
14946      * @param {Provider} this This state provider
14947      * @param {String} key The state key which was changed
14948      * @param {String} value The encoded value for the state
14949      */
14950     this.addEvents({
14951         "statechange": true
14952     });
14953     this.state = {};
14954     Roo.state.Provider.superclass.constructor.call(this);
14955 };
14956 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
14957     /**
14958      * Returns the current value for a key
14959      * @param {String} name The key name
14960      * @param {Mixed} defaultValue A default value to return if the key's value is not found
14961      * @return {Mixed} The state data
14962      */
14963     get : function(name, defaultValue){
14964         return typeof this.state[name] == "undefined" ?
14965             defaultValue : this.state[name];
14966     },
14967     
14968     /**
14969      * Clears a value from the state
14970      * @param {String} name The key name
14971      */
14972     clear : function(name){
14973         delete this.state[name];
14974         this.fireEvent("statechange", this, name, null);
14975     },
14976     
14977     /**
14978      * Sets the value for a key
14979      * @param {String} name The key name
14980      * @param {Mixed} value The value to set
14981      */
14982     set : function(name, value){
14983         this.state[name] = value;
14984         this.fireEvent("statechange", this, name, value);
14985     },
14986     
14987     /**
14988      * Decodes a string previously encoded with {@link #encodeValue}.
14989      * @param {String} value The value to decode
14990      * @return {Mixed} The decoded value
14991      */
14992     decodeValue : function(cookie){
14993         var re = /^(a|n|d|b|s|o)\:(.*)$/;
14994         var matches = re.exec(unescape(cookie));
14995         if(!matches || !matches[1]) {
14996             return; // non state cookie
14997         }
14998         var type = matches[1];
14999         var v = matches[2];
15000         switch(type){
15001             case "n":
15002                 return parseFloat(v);
15003             case "d":
15004                 return new Date(Date.parse(v));
15005             case "b":
15006                 return (v == "1");
15007             case "a":
15008                 var all = [];
15009                 var values = v.split("^");
15010                 for(var i = 0, len = values.length; i < len; i++){
15011                     all.push(this.decodeValue(values[i]));
15012                 }
15013                 return all;
15014            case "o":
15015                 var all = {};
15016                 var values = v.split("^");
15017                 for(var i = 0, len = values.length; i < len; i++){
15018                     var kv = values[i].split("=");
15019                     all[kv[0]] = this.decodeValue(kv[1]);
15020                 }
15021                 return all;
15022            default:
15023                 return v;
15024         }
15025     },
15026     
15027     /**
15028      * Encodes a value including type information.  Decode with {@link #decodeValue}.
15029      * @param {Mixed} value The value to encode
15030      * @return {String} The encoded value
15031      */
15032     encodeValue : function(v){
15033         var enc;
15034         if(typeof v == "number"){
15035             enc = "n:" + v;
15036         }else if(typeof v == "boolean"){
15037             enc = "b:" + (v ? "1" : "0");
15038         }else if(v instanceof Date){
15039             enc = "d:" + v.toGMTString();
15040         }else if(v instanceof Array){
15041             var flat = "";
15042             for(var i = 0, len = v.length; i < len; i++){
15043                 flat += this.encodeValue(v[i]);
15044                 if(i != len-1) {
15045                     flat += "^";
15046                 }
15047             }
15048             enc = "a:" + flat;
15049         }else if(typeof v == "object"){
15050             var flat = "";
15051             for(var key in v){
15052                 if(typeof v[key] != "function"){
15053                     flat += key + "=" + this.encodeValue(v[key]) + "^";
15054                 }
15055             }
15056             enc = "o:" + flat.substring(0, flat.length-1);
15057         }else{
15058             enc = "s:" + v;
15059         }
15060         return escape(enc);        
15061     }
15062 });
15063
15064 /*
15065  * Based on:
15066  * Ext JS Library 1.1.1
15067  * Copyright(c) 2006-2007, Ext JS, LLC.
15068  *
15069  * Originally Released Under LGPL - original licence link has changed is not relivant.
15070  *
15071  * Fork - LGPL
15072  * <script type="text/javascript">
15073  */
15074 /**
15075  * @class Roo.state.Manager
15076  * This is the global state manager. By default all components that are "state aware" check this class
15077  * for state information if you don't pass them a custom state provider. In order for this class
15078  * to be useful, it must be initialized with a provider when your application initializes.
15079  <pre><code>
15080 // in your initialization function
15081 init : function(){
15082    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
15083    ...
15084    // supposed you have a {@link Roo.BorderLayout}
15085    var layout = new Roo.BorderLayout(...);
15086    layout.restoreState();
15087    // or a {Roo.BasicDialog}
15088    var dialog = new Roo.BasicDialog(...);
15089    dialog.restoreState();
15090  </code></pre>
15091  * @singleton
15092  */
15093 Roo.state.Manager = function(){
15094     var provider = new Roo.state.Provider();
15095     
15096     return {
15097         /**
15098          * Configures the default state provider for your application
15099          * @param {Provider} stateProvider The state provider to set
15100          */
15101         setProvider : function(stateProvider){
15102             provider = stateProvider;
15103         },
15104         
15105         /**
15106          * Returns the current value for a key
15107          * @param {String} name The key name
15108          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
15109          * @return {Mixed} The state data
15110          */
15111         get : function(key, defaultValue){
15112             return provider.get(key, defaultValue);
15113         },
15114         
15115         /**
15116          * Sets the value for a key
15117          * @param {String} name The key name
15118          * @param {Mixed} value The state data
15119          */
15120          set : function(key, value){
15121             provider.set(key, value);
15122         },
15123         
15124         /**
15125          * Clears a value from the state
15126          * @param {String} name The key name
15127          */
15128         clear : function(key){
15129             provider.clear(key);
15130         },
15131         
15132         /**
15133          * Gets the currently configured state provider
15134          * @return {Provider} The state provider
15135          */
15136         getProvider : function(){
15137             return provider;
15138         }
15139     };
15140 }();
15141 /*
15142  * Based on:
15143  * Ext JS Library 1.1.1
15144  * Copyright(c) 2006-2007, Ext JS, LLC.
15145  *
15146  * Originally Released Under LGPL - original licence link has changed is not relivant.
15147  *
15148  * Fork - LGPL
15149  * <script type="text/javascript">
15150  */
15151 /**
15152  * @class Roo.state.CookieProvider
15153  * @extends Roo.state.Provider
15154  * The default Provider implementation which saves state via cookies.
15155  * <br />Usage:
15156  <pre><code>
15157    var cp = new Roo.state.CookieProvider({
15158        path: "/cgi-bin/",
15159        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
15160        domain: "roojs.com"
15161    })
15162    Roo.state.Manager.setProvider(cp);
15163  </code></pre>
15164  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
15165  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
15166  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
15167  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
15168  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
15169  * domain the page is running on including the 'www' like 'www.roojs.com')
15170  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
15171  * @constructor
15172  * Create a new CookieProvider
15173  * @param {Object} config The configuration object
15174  */
15175 Roo.state.CookieProvider = function(config){
15176     Roo.state.CookieProvider.superclass.constructor.call(this);
15177     this.path = "/";
15178     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
15179     this.domain = null;
15180     this.secure = false;
15181     Roo.apply(this, config);
15182     this.state = this.readCookies();
15183 };
15184
15185 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
15186     // private
15187     set : function(name, value){
15188         if(typeof value == "undefined" || value === null){
15189             this.clear(name);
15190             return;
15191         }
15192         this.setCookie(name, value);
15193         Roo.state.CookieProvider.superclass.set.call(this, name, value);
15194     },
15195
15196     // private
15197     clear : function(name){
15198         this.clearCookie(name);
15199         Roo.state.CookieProvider.superclass.clear.call(this, name);
15200     },
15201
15202     // private
15203     readCookies : function(){
15204         var cookies = {};
15205         var c = document.cookie + ";";
15206         var re = /\s?(.*?)=(.*?);/g;
15207         var matches;
15208         while((matches = re.exec(c)) != null){
15209             var name = matches[1];
15210             var value = matches[2];
15211             if(name && name.substring(0,3) == "ys-"){
15212                 cookies[name.substr(3)] = this.decodeValue(value);
15213             }
15214         }
15215         return cookies;
15216     },
15217
15218     // private
15219     setCookie : function(name, value){
15220         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
15221            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
15222            ((this.path == null) ? "" : ("; path=" + this.path)) +
15223            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
15224            ((this.secure == true) ? "; secure" : "");
15225     },
15226
15227     // private
15228     clearCookie : function(name){
15229         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
15230            ((this.path == null) ? "" : ("; path=" + this.path)) +
15231            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
15232            ((this.secure == true) ? "; secure" : "");
15233     }
15234 });/*
15235  * Based on:
15236  * Ext JS Library 1.1.1
15237  * Copyright(c) 2006-2007, Ext JS, LLC.
15238  *
15239  * Originally Released Under LGPL - original licence link has changed is not relivant.
15240  *
15241  * Fork - LGPL
15242  * <script type="text/javascript">
15243  */
15244  
15245
15246 /**
15247  * @class Roo.ComponentMgr
15248  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
15249  * @singleton
15250  */
15251 Roo.ComponentMgr = function(){
15252     var all = new Roo.util.MixedCollection();
15253
15254     return {
15255         /**
15256          * Registers a component.
15257          * @param {Roo.Component} c The component
15258          */
15259         register : function(c){
15260             all.add(c);
15261         },
15262
15263         /**
15264          * Unregisters a component.
15265          * @param {Roo.Component} c The component
15266          */
15267         unregister : function(c){
15268             all.remove(c);
15269         },
15270
15271         /**
15272          * Returns a component by id
15273          * @param {String} id The component id
15274          */
15275         get : function(id){
15276             return all.get(id);
15277         },
15278
15279         /**
15280          * Registers a function that will be called when a specified component is added to ComponentMgr
15281          * @param {String} id The component id
15282          * @param {Funtction} fn The callback function
15283          * @param {Object} scope The scope of the callback
15284          */
15285         onAvailable : function(id, fn, scope){
15286             all.on("add", function(index, o){
15287                 if(o.id == id){
15288                     fn.call(scope || o, o);
15289                     all.un("add", fn, scope);
15290                 }
15291             });
15292         }
15293     };
15294 }();/*
15295  * Based on:
15296  * Ext JS Library 1.1.1
15297  * Copyright(c) 2006-2007, Ext JS, LLC.
15298  *
15299  * Originally Released Under LGPL - original licence link has changed is not relivant.
15300  *
15301  * Fork - LGPL
15302  * <script type="text/javascript">
15303  */
15304  
15305 /**
15306  * @class Roo.Component
15307  * @extends Roo.util.Observable
15308  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
15309  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
15310  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
15311  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
15312  * All visual components (widgets) that require rendering into a layout should subclass Component.
15313  * @constructor
15314  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
15315  * 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
15316  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
15317  */
15318 Roo.Component = function(config){
15319     config = config || {};
15320     if(config.tagName || config.dom || typeof config == "string"){ // element object
15321         config = {el: config, id: config.id || config};
15322     }
15323     this.initialConfig = config;
15324
15325     Roo.apply(this, config);
15326     this.addEvents({
15327         /**
15328          * @event disable
15329          * Fires after the component is disabled.
15330              * @param {Roo.Component} this
15331              */
15332         disable : true,
15333         /**
15334          * @event enable
15335          * Fires after the component is enabled.
15336              * @param {Roo.Component} this
15337              */
15338         enable : true,
15339         /**
15340          * @event beforeshow
15341          * Fires before the component is shown.  Return false to stop the show.
15342              * @param {Roo.Component} this
15343              */
15344         beforeshow : true,
15345         /**
15346          * @event show
15347          * Fires after the component is shown.
15348              * @param {Roo.Component} this
15349              */
15350         show : true,
15351         /**
15352          * @event beforehide
15353          * Fires before the component is hidden. Return false to stop the hide.
15354              * @param {Roo.Component} this
15355              */
15356         beforehide : true,
15357         /**
15358          * @event hide
15359          * Fires after the component is hidden.
15360              * @param {Roo.Component} this
15361              */
15362         hide : true,
15363         /**
15364          * @event beforerender
15365          * Fires before the component is rendered. Return false to stop the render.
15366              * @param {Roo.Component} this
15367              */
15368         beforerender : true,
15369         /**
15370          * @event render
15371          * Fires after the component is rendered.
15372              * @param {Roo.Component} this
15373              */
15374         render : true,
15375         /**
15376          * @event beforedestroy
15377          * Fires before the component is destroyed. Return false to stop the destroy.
15378              * @param {Roo.Component} this
15379              */
15380         beforedestroy : true,
15381         /**
15382          * @event destroy
15383          * Fires after the component is destroyed.
15384              * @param {Roo.Component} this
15385              */
15386         destroy : true
15387     });
15388     if(!this.id){
15389         this.id = "roo-comp-" + (++Roo.Component.AUTO_ID);
15390     }
15391     Roo.ComponentMgr.register(this);
15392     Roo.Component.superclass.constructor.call(this);
15393     this.initComponent();
15394     if(this.renderTo){ // not supported by all components yet. use at your own risk!
15395         this.render(this.renderTo);
15396         delete this.renderTo;
15397     }
15398 };
15399
15400 /** @private */
15401 Roo.Component.AUTO_ID = 1000;
15402
15403 Roo.extend(Roo.Component, Roo.util.Observable, {
15404     /**
15405      * @scope Roo.Component.prototype
15406      * @type {Boolean}
15407      * true if this component is hidden. Read-only.
15408      */
15409     hidden : false,
15410     /**
15411      * @type {Boolean}
15412      * true if this component is disabled. Read-only.
15413      */
15414     disabled : false,
15415     /**
15416      * @type {Boolean}
15417      * true if this component has been rendered. Read-only.
15418      */
15419     rendered : false,
15420     
15421     /** @cfg {String} disableClass
15422      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
15423      */
15424     disabledClass : "x-item-disabled",
15425         /** @cfg {Boolean} allowDomMove
15426          * Whether the component can move the Dom node when rendering (defaults to true).
15427          */
15428     allowDomMove : true,
15429     /** @cfg {String} hideMode (display|visibility)
15430      * How this component should hidden. Supported values are
15431      * "visibility" (css visibility), "offsets" (negative offset position) and
15432      * "display" (css display) - defaults to "display".
15433      */
15434     hideMode: 'display',
15435
15436     /** @private */
15437     ctype : "Roo.Component",
15438
15439     /**
15440      * @cfg {String} actionMode 
15441      * which property holds the element that used for  hide() / show() / disable() / enable()
15442      * default is 'el' for forms you probably want to set this to fieldEl 
15443      */
15444     actionMode : "el",
15445
15446     /** @private */
15447     getActionEl : function(){
15448         return this[this.actionMode];
15449     },
15450
15451     initComponent : Roo.emptyFn,
15452     /**
15453      * If this is a lazy rendering component, render it to its container element.
15454      * @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.
15455      */
15456     render : function(container, position){
15457         
15458         if(this.rendered){
15459             return this;
15460         }
15461         
15462         if(this.fireEvent("beforerender", this) === false){
15463             return false;
15464         }
15465         
15466         if(!container && this.el){
15467             this.el = Roo.get(this.el);
15468             container = this.el.dom.parentNode;
15469             this.allowDomMove = false;
15470         }
15471         this.container = Roo.get(container);
15472         this.rendered = true;
15473         if(position !== undefined){
15474             if(typeof position == 'number'){
15475                 position = this.container.dom.childNodes[position];
15476             }else{
15477                 position = Roo.getDom(position);
15478             }
15479         }
15480         this.onRender(this.container, position || null);
15481         if(this.cls){
15482             this.el.addClass(this.cls);
15483             delete this.cls;
15484         }
15485         if(this.style){
15486             this.el.applyStyles(this.style);
15487             delete this.style;
15488         }
15489         this.fireEvent("render", this);
15490         this.afterRender(this.container);
15491         if(this.hidden){
15492             this.hide();
15493         }
15494         if(this.disabled){
15495             this.disable();
15496         }
15497
15498         return this;
15499         
15500     },
15501
15502     /** @private */
15503     // default function is not really useful
15504     onRender : function(ct, position){
15505         if(this.el){
15506             this.el = Roo.get(this.el);
15507             if(this.allowDomMove !== false){
15508                 ct.dom.insertBefore(this.el.dom, position);
15509             }
15510         }
15511     },
15512
15513     /** @private */
15514     getAutoCreate : function(){
15515         var cfg = typeof this.autoCreate == "object" ?
15516                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
15517         if(this.id && !cfg.id){
15518             cfg.id = this.id;
15519         }
15520         return cfg;
15521     },
15522
15523     /** @private */
15524     afterRender : Roo.emptyFn,
15525
15526     /**
15527      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
15528      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
15529      */
15530     destroy : function(){
15531         if(this.fireEvent("beforedestroy", this) !== false){
15532             this.purgeListeners();
15533             this.beforeDestroy();
15534             if(this.rendered){
15535                 this.el.removeAllListeners();
15536                 this.el.remove();
15537                 if(this.actionMode == "container"){
15538                     this.container.remove();
15539                 }
15540             }
15541             this.onDestroy();
15542             Roo.ComponentMgr.unregister(this);
15543             this.fireEvent("destroy", this);
15544         }
15545     },
15546
15547         /** @private */
15548     beforeDestroy : function(){
15549
15550     },
15551
15552         /** @private */
15553         onDestroy : function(){
15554
15555     },
15556
15557     /**
15558      * Returns the underlying {@link Roo.Element}.
15559      * @return {Roo.Element} The element
15560      */
15561     getEl : function(){
15562         return this.el;
15563     },
15564
15565     /**
15566      * Returns the id of this component.
15567      * @return {String}
15568      */
15569     getId : function(){
15570         return this.id;
15571     },
15572
15573     /**
15574      * Try to focus this component.
15575      * @param {Boolean} selectText True to also select the text in this component (if applicable)
15576      * @return {Roo.Component} this
15577      */
15578     focus : function(selectText){
15579         if(this.rendered){
15580             this.el.focus();
15581             if(selectText === true){
15582                 this.el.dom.select();
15583             }
15584         }
15585         return this;
15586     },
15587
15588     /** @private */
15589     blur : function(){
15590         if(this.rendered){
15591             this.el.blur();
15592         }
15593         return this;
15594     },
15595
15596     /**
15597      * Disable this component.
15598      * @return {Roo.Component} this
15599      */
15600     disable : function(){
15601         if(this.rendered){
15602             this.onDisable();
15603         }
15604         this.disabled = true;
15605         this.fireEvent("disable", this);
15606         return this;
15607     },
15608
15609         // private
15610     onDisable : function(){
15611         this.getActionEl().addClass(this.disabledClass);
15612         this.el.dom.disabled = true;
15613     },
15614
15615     /**
15616      * Enable this component.
15617      * @return {Roo.Component} this
15618      */
15619     enable : function(){
15620         if(this.rendered){
15621             this.onEnable();
15622         }
15623         this.disabled = false;
15624         this.fireEvent("enable", this);
15625         return this;
15626     },
15627
15628         // private
15629     onEnable : function(){
15630         this.getActionEl().removeClass(this.disabledClass);
15631         this.el.dom.disabled = false;
15632     },
15633
15634     /**
15635      * Convenience function for setting disabled/enabled by boolean.
15636      * @param {Boolean} disabled
15637      */
15638     setDisabled : function(disabled){
15639         this[disabled ? "disable" : "enable"]();
15640     },
15641
15642     /**
15643      * Show this component.
15644      * @return {Roo.Component} this
15645      */
15646     show: function(){
15647         if(this.fireEvent("beforeshow", this) !== false){
15648             this.hidden = false;
15649             if(this.rendered){
15650                 this.onShow();
15651             }
15652             this.fireEvent("show", this);
15653         }
15654         return this;
15655     },
15656
15657     // private
15658     onShow : function(){
15659         var ae = this.getActionEl();
15660         if(this.hideMode == 'visibility'){
15661             ae.dom.style.visibility = "visible";
15662         }else if(this.hideMode == 'offsets'){
15663             ae.removeClass('x-hidden');
15664         }else{
15665             ae.dom.style.display = "";
15666         }
15667     },
15668
15669     /**
15670      * Hide this component.
15671      * @return {Roo.Component} this
15672      */
15673     hide: function(){
15674         if(this.fireEvent("beforehide", this) !== false){
15675             this.hidden = true;
15676             if(this.rendered){
15677                 this.onHide();
15678             }
15679             this.fireEvent("hide", this);
15680         }
15681         return this;
15682     },
15683
15684     // private
15685     onHide : function(){
15686         var ae = this.getActionEl();
15687         if(this.hideMode == 'visibility'){
15688             ae.dom.style.visibility = "hidden";
15689         }else if(this.hideMode == 'offsets'){
15690             ae.addClass('x-hidden');
15691         }else{
15692             ae.dom.style.display = "none";
15693         }
15694     },
15695
15696     /**
15697      * Convenience function to hide or show this component by boolean.
15698      * @param {Boolean} visible True to show, false to hide
15699      * @return {Roo.Component} this
15700      */
15701     setVisible: function(visible){
15702         if(visible) {
15703             this.show();
15704         }else{
15705             this.hide();
15706         }
15707         return this;
15708     },
15709
15710     /**
15711      * Returns true if this component is visible.
15712      */
15713     isVisible : function(){
15714         return this.getActionEl().isVisible();
15715     },
15716
15717     cloneConfig : function(overrides){
15718         overrides = overrides || {};
15719         var id = overrides.id || Roo.id();
15720         var cfg = Roo.applyIf(overrides, this.initialConfig);
15721         cfg.id = id; // prevent dup id
15722         return new this.constructor(cfg);
15723     }
15724 });/*
15725  * Based on:
15726  * Ext JS Library 1.1.1
15727  * Copyright(c) 2006-2007, Ext JS, LLC.
15728  *
15729  * Originally Released Under LGPL - original licence link has changed is not relivant.
15730  *
15731  * Fork - LGPL
15732  * <script type="text/javascript">
15733  */
15734
15735 /**
15736  * @class Roo.BoxComponent
15737  * @extends Roo.Component
15738  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
15739  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
15740  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
15741  * layout containers.
15742  * @constructor
15743  * @param {Roo.Element/String/Object} config The configuration options.
15744  */
15745 Roo.BoxComponent = function(config){
15746     Roo.Component.call(this, config);
15747     this.addEvents({
15748         /**
15749          * @event resize
15750          * Fires after the component is resized.
15751              * @param {Roo.Component} this
15752              * @param {Number} adjWidth The box-adjusted width that was set
15753              * @param {Number} adjHeight The box-adjusted height that was set
15754              * @param {Number} rawWidth The width that was originally specified
15755              * @param {Number} rawHeight The height that was originally specified
15756              */
15757         resize : true,
15758         /**
15759          * @event move
15760          * Fires after the component is moved.
15761              * @param {Roo.Component} this
15762              * @param {Number} x The new x position
15763              * @param {Number} y The new y position
15764              */
15765         move : true
15766     });
15767 };
15768
15769 Roo.extend(Roo.BoxComponent, Roo.Component, {
15770     // private, set in afterRender to signify that the component has been rendered
15771     boxReady : false,
15772     // private, used to defer height settings to subclasses
15773     deferHeight: false,
15774     /** @cfg {Number} width
15775      * width (optional) size of component
15776      */
15777      /** @cfg {Number} height
15778      * height (optional) size of component
15779      */
15780      
15781     /**
15782      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
15783      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
15784      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
15785      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
15786      * @return {Roo.BoxComponent} this
15787      */
15788     setSize : function(w, h){
15789         // support for standard size objects
15790         if(typeof w == 'object'){
15791             h = w.height;
15792             w = w.width;
15793         }
15794         // not rendered
15795         if(!this.boxReady){
15796             this.width = w;
15797             this.height = h;
15798             return this;
15799         }
15800
15801         // prevent recalcs when not needed
15802         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
15803             return this;
15804         }
15805         this.lastSize = {width: w, height: h};
15806
15807         var adj = this.adjustSize(w, h);
15808         var aw = adj.width, ah = adj.height;
15809         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
15810             var rz = this.getResizeEl();
15811             if(!this.deferHeight && aw !== undefined && ah !== undefined){
15812                 rz.setSize(aw, ah);
15813             }else if(!this.deferHeight && ah !== undefined){
15814                 rz.setHeight(ah);
15815             }else if(aw !== undefined){
15816                 rz.setWidth(aw);
15817             }
15818             this.onResize(aw, ah, w, h);
15819             this.fireEvent('resize', this, aw, ah, w, h);
15820         }
15821         return this;
15822     },
15823
15824     /**
15825      * Gets the current size of the component's underlying element.
15826      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
15827      */
15828     getSize : function(){
15829         return this.el.getSize();
15830     },
15831
15832     /**
15833      * Gets the current XY position of the component's underlying element.
15834      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15835      * @return {Array} The XY position of the element (e.g., [100, 200])
15836      */
15837     getPosition : function(local){
15838         if(local === true){
15839             return [this.el.getLeft(true), this.el.getTop(true)];
15840         }
15841         return this.xy || this.el.getXY();
15842     },
15843
15844     /**
15845      * Gets the current box measurements of the component's underlying element.
15846      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15847      * @returns {Object} box An object in the format {x, y, width, height}
15848      */
15849     getBox : function(local){
15850         var s = this.el.getSize();
15851         if(local){
15852             s.x = this.el.getLeft(true);
15853             s.y = this.el.getTop(true);
15854         }else{
15855             var xy = this.xy || this.el.getXY();
15856             s.x = xy[0];
15857             s.y = xy[1];
15858         }
15859         return s;
15860     },
15861
15862     /**
15863      * Sets the current box measurements of the component's underlying element.
15864      * @param {Object} box An object in the format {x, y, width, height}
15865      * @returns {Roo.BoxComponent} this
15866      */
15867     updateBox : function(box){
15868         this.setSize(box.width, box.height);
15869         this.setPagePosition(box.x, box.y);
15870         return this;
15871     },
15872
15873     // protected
15874     getResizeEl : function(){
15875         return this.resizeEl || this.el;
15876     },
15877
15878     // protected
15879     getPositionEl : function(){
15880         return this.positionEl || this.el;
15881     },
15882
15883     /**
15884      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
15885      * This method fires the move event.
15886      * @param {Number} left The new left
15887      * @param {Number} top The new top
15888      * @returns {Roo.BoxComponent} this
15889      */
15890     setPosition : function(x, y){
15891         this.x = x;
15892         this.y = y;
15893         if(!this.boxReady){
15894             return this;
15895         }
15896         var adj = this.adjustPosition(x, y);
15897         var ax = adj.x, ay = adj.y;
15898
15899         var el = this.getPositionEl();
15900         if(ax !== undefined || ay !== undefined){
15901             if(ax !== undefined && ay !== undefined){
15902                 el.setLeftTop(ax, ay);
15903             }else if(ax !== undefined){
15904                 el.setLeft(ax);
15905             }else if(ay !== undefined){
15906                 el.setTop(ay);
15907             }
15908             this.onPosition(ax, ay);
15909             this.fireEvent('move', this, ax, ay);
15910         }
15911         return this;
15912     },
15913
15914     /**
15915      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
15916      * This method fires the move event.
15917      * @param {Number} x The new x position
15918      * @param {Number} y The new y position
15919      * @returns {Roo.BoxComponent} this
15920      */
15921     setPagePosition : function(x, y){
15922         this.pageX = x;
15923         this.pageY = y;
15924         if(!this.boxReady){
15925             return;
15926         }
15927         if(x === undefined || y === undefined){ // cannot translate undefined points
15928             return;
15929         }
15930         var p = this.el.translatePoints(x, y);
15931         this.setPosition(p.left, p.top);
15932         return this;
15933     },
15934
15935     // private
15936     onRender : function(ct, position){
15937         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
15938         if(this.resizeEl){
15939             this.resizeEl = Roo.get(this.resizeEl);
15940         }
15941         if(this.positionEl){
15942             this.positionEl = Roo.get(this.positionEl);
15943         }
15944     },
15945
15946     // private
15947     afterRender : function(){
15948         Roo.BoxComponent.superclass.afterRender.call(this);
15949         this.boxReady = true;
15950         this.setSize(this.width, this.height);
15951         if(this.x || this.y){
15952             this.setPosition(this.x, this.y);
15953         }
15954         if(this.pageX || this.pageY){
15955             this.setPagePosition(this.pageX, this.pageY);
15956         }
15957     },
15958
15959     /**
15960      * Force the component's size to recalculate based on the underlying element's current height and width.
15961      * @returns {Roo.BoxComponent} this
15962      */
15963     syncSize : function(){
15964         delete this.lastSize;
15965         this.setSize(this.el.getWidth(), this.el.getHeight());
15966         return this;
15967     },
15968
15969     /**
15970      * Called after the component is resized, this method is empty by default but can be implemented by any
15971      * subclass that needs to perform custom logic after a resize occurs.
15972      * @param {Number} adjWidth The box-adjusted width that was set
15973      * @param {Number} adjHeight The box-adjusted height that was set
15974      * @param {Number} rawWidth The width that was originally specified
15975      * @param {Number} rawHeight The height that was originally specified
15976      */
15977     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
15978
15979     },
15980
15981     /**
15982      * Called after the component is moved, this method is empty by default but can be implemented by any
15983      * subclass that needs to perform custom logic after a move occurs.
15984      * @param {Number} x The new x position
15985      * @param {Number} y The new y position
15986      */
15987     onPosition : function(x, y){
15988
15989     },
15990
15991     // private
15992     adjustSize : function(w, h){
15993         if(this.autoWidth){
15994             w = 'auto';
15995         }
15996         if(this.autoHeight){
15997             h = 'auto';
15998         }
15999         return {width : w, height: h};
16000     },
16001
16002     // private
16003     adjustPosition : function(x, y){
16004         return {x : x, y: y};
16005     }
16006 });/*
16007  * Original code for Roojs - LGPL
16008  * <script type="text/javascript">
16009  */
16010  
16011 /**
16012  * @class Roo.XComponent
16013  * A delayed Element creator...
16014  * Or a way to group chunks of interface together.
16015  * technically this is a wrapper around a tree of Roo elements (which defines a 'module'),
16016  *  used in conjunction with XComponent.build() it will create an instance of each element,
16017  *  then call addxtype() to build the User interface.
16018  * 
16019  * Mypart.xyx = new Roo.XComponent({
16020
16021     parent : 'Mypart.xyz', // empty == document.element.!!
16022     order : '001',
16023     name : 'xxxx'
16024     region : 'xxxx'
16025     disabled : function() {} 
16026      
16027     tree : function() { // return an tree of xtype declared components
16028         var MODULE = this;
16029         return 
16030         {
16031             xtype : 'NestedLayoutPanel',
16032             // technicall
16033         }
16034      ]
16035  *})
16036  *
16037  *
16038  * It can be used to build a big heiracy, with parent etc.
16039  * or you can just use this to render a single compoent to a dom element
16040  * MYPART.render(Roo.Element | String(id) | dom_element )
16041  *
16042  *
16043  * Usage patterns.
16044  *
16045  * Classic Roo
16046  *
16047  * Roo is designed primarily as a single page application, so the UI build for a standard interface will
16048  * expect a single 'TOP' level module normally indicated by the 'parent' of the XComponent definition being defined as false.
16049  *
16050  * Each sub module is expected to have a parent pointing to the class name of it's parent module.
16051  *
16052  * When the top level is false, a 'Roo.BorderLayout' is created and the element is flagged as 'topModule'
16053  * - if mulitple topModules exist, the last one is defined as the top module.
16054  *
16055  * Embeded Roo
16056  * 
16057  * When the top level or multiple modules are to embedded into a existing HTML page,
16058  * the parent element can container '#id' of the element where the module will be drawn.
16059  *
16060  * Bootstrap Roo
16061  *
16062  * Unlike classic Roo, the bootstrap tends not to be used as a single page.
16063  * it relies more on a include mechanism, where sub modules are included into an outer page.
16064  * This is normally managed by the builder tools using Roo.apply( options, Included.Sub.Module )
16065  * 
16066  * Bootstrap Roo Included elements
16067  *
16068  * Our builder application needs the ability to preview these sub compoennts. They will normally have parent=false set,
16069  * hence confusing the component builder as it thinks there are multiple top level elements. 
16070  *
16071  * String Over-ride & Translations
16072  *
16073  * Our builder application writes all the strings as _strings and _named_strings. This is to enable the translation of elements,
16074  * and also the 'overlaying of string values - needed when different versions of the same application with different text content
16075  * are needed. @see Roo.XComponent.overlayString  
16076  * 
16077  * 
16078  * 
16079  * @extends Roo.util.Observable
16080  * @constructor
16081  * @param cfg {Object} configuration of component
16082  * 
16083  */
16084 Roo.XComponent = function(cfg) {
16085     Roo.apply(this, cfg);
16086     this.addEvents({ 
16087         /**
16088              * @event built
16089              * Fires when this the componnt is built
16090              * @param {Roo.XComponent} c the component
16091              */
16092         'built' : true
16093         
16094     });
16095     this.region = this.region || 'center'; // default..
16096     Roo.XComponent.register(this);
16097     this.modules = false;
16098     this.el = false; // where the layout goes..
16099     
16100     
16101 }
16102 Roo.extend(Roo.XComponent, Roo.util.Observable, {
16103     /**
16104      * @property el
16105      * The created element (with Roo.factory())
16106      * @type {Roo.Layout}
16107      */
16108     el  : false,
16109     
16110     /**
16111      * @property el
16112      * for BC  - use el in new code
16113      * @type {Roo.Layout}
16114      */
16115     panel : false,
16116     
16117     /**
16118      * @property layout
16119      * for BC  - use el in new code
16120      * @type {Roo.Layout}
16121      */
16122     layout : false,
16123     
16124      /**
16125      * @cfg {Function|boolean} disabled
16126      * If this module is disabled by some rule, return true from the funtion
16127      */
16128     disabled : false,
16129     
16130     /**
16131      * @cfg {String} parent 
16132      * Name of parent element which it get xtype added to..
16133      */
16134     parent: false,
16135     
16136     /**
16137      * @cfg {String} order
16138      * Used to set the order in which elements are created (usefull for multiple tabs)
16139      */
16140     
16141     order : false,
16142     /**
16143      * @cfg {String} name
16144      * String to display while loading.
16145      */
16146     name : false,
16147     /**
16148      * @cfg {String} region
16149      * Region to render component to (defaults to center)
16150      */
16151     region : 'center',
16152     
16153     /**
16154      * @cfg {Array} items
16155      * A single item array - the first element is the root of the tree..
16156      * It's done this way to stay compatible with the Xtype system...
16157      */
16158     items : false,
16159     
16160     /**
16161      * @property _tree
16162      * The method that retuns the tree of parts that make up this compoennt 
16163      * @type {function}
16164      */
16165     _tree  : false,
16166     
16167      /**
16168      * render
16169      * render element to dom or tree
16170      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
16171      */
16172     
16173     render : function(el)
16174     {
16175         
16176         el = el || false;
16177         var hp = this.parent ? 1 : 0;
16178         Roo.debug &&  Roo.log(this);
16179         
16180         var tree = this._tree ? this._tree() : this.tree();
16181
16182         
16183         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
16184             // if parent is a '#.....' string, then let's use that..
16185             var ename = this.parent.substr(1);
16186             this.parent = false;
16187             Roo.debug && Roo.log(ename);
16188             switch (ename) {
16189                 case 'bootstrap-body':
16190                     if (typeof(tree.el) != 'undefined' && tree.el == document.body)  {
16191                         // this is the BorderLayout standard?
16192                        this.parent = { el : true };
16193                        break;
16194                     }
16195                     if (["Nest", "Content", "Grid", "Tree"].indexOf(tree.xtype)  > -1)  {
16196                         // need to insert stuff...
16197                         this.parent =  {
16198                              el : new Roo.bootstrap.layout.Border({
16199                                  el : document.body, 
16200                      
16201                                  center: {
16202                                     titlebar: false,
16203                                     autoScroll:false,
16204                                     closeOnTab: true,
16205                                     tabPosition: 'top',
16206                                       //resizeTabs: true,
16207                                     alwaysShowTabs: true,
16208                                     hideTabs: false
16209                                      //minTabWidth: 140
16210                                  }
16211                              })
16212                         
16213                          };
16214                          break;
16215                     }
16216                          
16217                     if (typeof(Roo.bootstrap.Body) != 'undefined' ) {
16218                         this.parent = { el :  new  Roo.bootstrap.Body() };
16219                         Roo.debug && Roo.log("setting el to doc body");
16220                          
16221                     } else {
16222                         throw "Container is bootstrap body, but Roo.bootstrap.Body is not defined";
16223                     }
16224                     break;
16225                 case 'bootstrap':
16226                     this.parent = { el : true};
16227                     // fall through
16228                 default:
16229                     el = Roo.get(ename);
16230                     if (typeof(Roo.bootstrap) != 'undefined' && tree['|xns'] == 'Roo.bootstrap') {
16231                         this.parent = { el : true};
16232                     }
16233                     
16234                     break;
16235             }
16236                 
16237             
16238             if (!el && !this.parent) {
16239                 Roo.debug && Roo.log("Warning - element can not be found :#" + ename );
16240                 return;
16241             }
16242         }
16243         
16244         Roo.debug && Roo.log("EL:");
16245         Roo.debug && Roo.log(el);
16246         Roo.debug && Roo.log("this.parent.el:");
16247         Roo.debug && Roo.log(this.parent.el);
16248         
16249
16250         // altertive root elements ??? - we need a better way to indicate these.
16251         var is_alt = Roo.XComponent.is_alt ||
16252                     (typeof(tree.el) != 'undefined' && tree.el == document.body) ||
16253                     (typeof(Roo.bootstrap) != 'undefined' && tree.xns == Roo.bootstrap) ||
16254                     (typeof(Roo.mailer) != 'undefined' && tree.xns == Roo.mailer) ;
16255         
16256         
16257         
16258         if (!this.parent && is_alt) {
16259             //el = Roo.get(document.body);
16260             this.parent = { el : true };
16261         }
16262             
16263             
16264         
16265         if (!this.parent) {
16266             
16267             Roo.debug && Roo.log("no parent - creating one");
16268             
16269             el = el ? Roo.get(el) : false;      
16270             
16271             if (typeof(Roo.BorderLayout) == 'undefined' ) {
16272                 
16273                 this.parent =  {
16274                     el : new Roo.bootstrap.layout.Border({
16275                         el: el || document.body,
16276                     
16277                         center: {
16278                             titlebar: false,
16279                             autoScroll:false,
16280                             closeOnTab: true,
16281                             tabPosition: 'top',
16282                              //resizeTabs: true,
16283                             alwaysShowTabs: false,
16284                             hideTabs: true,
16285                             minTabWidth: 140,
16286                             overflow: 'visible'
16287                          }
16288                      })
16289                 };
16290             } else {
16291             
16292                 // it's a top level one..
16293                 this.parent =  {
16294                     el : new Roo.BorderLayout(el || document.body, {
16295                         center: {
16296                             titlebar: false,
16297                             autoScroll:false,
16298                             closeOnTab: true,
16299                             tabPosition: 'top',
16300                              //resizeTabs: true,
16301                             alwaysShowTabs: el && hp? false :  true,
16302                             hideTabs: el || !hp ? true :  false,
16303                             minTabWidth: 140
16304                          }
16305                     })
16306                 };
16307             }
16308         }
16309         
16310         if (!this.parent.el) {
16311                 // probably an old style ctor, which has been disabled.
16312                 return;
16313
16314         }
16315                 // The 'tree' method is  '_tree now' 
16316             
16317         tree.region = tree.region || this.region;
16318         var is_body = false;
16319         if (this.parent.el === true) {
16320             // bootstrap... - body..
16321             if (el) {
16322                 tree.el = el;
16323             }
16324             this.parent.el = Roo.factory(tree);
16325             is_body = true;
16326         }
16327         
16328         this.el = this.parent.el.addxtype(tree, undefined, is_body);
16329         this.fireEvent('built', this);
16330         
16331         this.panel = this.el;
16332         this.layout = this.panel.layout;
16333         this.parentLayout = this.parent.layout  || false;  
16334          
16335     }
16336     
16337 });
16338
16339 Roo.apply(Roo.XComponent, {
16340     /**
16341      * @property  hideProgress
16342      * true to disable the building progress bar.. usefull on single page renders.
16343      * @type Boolean
16344      */
16345     hideProgress : false,
16346     /**
16347      * @property  buildCompleted
16348      * True when the builder has completed building the interface.
16349      * @type Boolean
16350      */
16351     buildCompleted : false,
16352      
16353     /**
16354      * @property  topModule
16355      * the upper most module - uses document.element as it's constructor.
16356      * @type Object
16357      */
16358      
16359     topModule  : false,
16360       
16361     /**
16362      * @property  modules
16363      * array of modules to be created by registration system.
16364      * @type {Array} of Roo.XComponent
16365      */
16366     
16367     modules : [],
16368     /**
16369      * @property  elmodules
16370      * array of modules to be created by which use #ID 
16371      * @type {Array} of Roo.XComponent
16372      */
16373      
16374     elmodules : [],
16375
16376      /**
16377      * @property  is_alt
16378      * Is an alternative Root - normally used by bootstrap or other systems,
16379      *    where the top element in the tree can wrap 'body' 
16380      * @type {boolean}  (default false)
16381      */
16382      
16383     is_alt : false,
16384     /**
16385      * @property  build_from_html
16386      * Build elements from html - used by bootstrap HTML stuff 
16387      *    - this is cleared after build is completed
16388      * @type {boolean}    (default false)
16389      */
16390      
16391     build_from_html : false,
16392     /**
16393      * Register components to be built later.
16394      *
16395      * This solves the following issues
16396      * - Building is not done on page load, but after an authentication process has occured.
16397      * - Interface elements are registered on page load
16398      * - Parent Interface elements may not be loaded before child, so this handles that..
16399      * 
16400      *
16401      * example:
16402      * 
16403      * MyApp.register({
16404           order : '000001',
16405           module : 'Pman.Tab.projectMgr',
16406           region : 'center',
16407           parent : 'Pman.layout',
16408           disabled : false,  // or use a function..
16409         })
16410      
16411      * * @param {Object} details about module
16412      */
16413     register : function(obj) {
16414                 
16415         Roo.XComponent.event.fireEvent('register', obj);
16416         switch(typeof(obj.disabled) ) {
16417                 
16418             case 'undefined':
16419                 break;
16420             
16421             case 'function':
16422                 if ( obj.disabled() ) {
16423                         return;
16424                 }
16425                 break;
16426             
16427             default:
16428                 if (obj.disabled || obj.region == '#disabled') {
16429                         return;
16430                 }
16431                 break;
16432         }
16433                 
16434         this.modules.push(obj);
16435          
16436     },
16437     /**
16438      * convert a string to an object..
16439      * eg. 'AAA.BBB' -> finds AAA.BBB
16440
16441      */
16442     
16443     toObject : function(str)
16444     {
16445         if (!str || typeof(str) == 'object') {
16446             return str;
16447         }
16448         if (str.substring(0,1) == '#') {
16449             return str;
16450         }
16451
16452         var ar = str.split('.');
16453         var rt, o;
16454         rt = ar.shift();
16455             /** eval:var:o */
16456         try {
16457             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
16458         } catch (e) {
16459             throw "Module not found : " + str;
16460         }
16461         
16462         if (o === false) {
16463             throw "Module not found : " + str;
16464         }
16465         Roo.each(ar, function(e) {
16466             if (typeof(o[e]) == 'undefined') {
16467                 throw "Module not found : " + str;
16468             }
16469             o = o[e];
16470         });
16471         
16472         return o;
16473         
16474     },
16475     
16476     
16477     /**
16478      * move modules into their correct place in the tree..
16479      * 
16480      */
16481     preBuild : function ()
16482     {
16483         var _t = this;
16484         Roo.each(this.modules , function (obj)
16485         {
16486             Roo.XComponent.event.fireEvent('beforebuild', obj);
16487             
16488             var opar = obj.parent;
16489             try { 
16490                 obj.parent = this.toObject(opar);
16491             } catch(e) {
16492                 Roo.debug && Roo.log("parent:toObject failed: " + e.toString());
16493                 return;
16494             }
16495             
16496             if (!obj.parent) {
16497                 Roo.debug && Roo.log("GOT top level module");
16498                 Roo.debug && Roo.log(obj);
16499                 obj.modules = new Roo.util.MixedCollection(false, 
16500                     function(o) { return o.order + '' }
16501                 );
16502                 this.topModule = obj;
16503                 return;
16504             }
16505                         // parent is a string (usually a dom element name..)
16506             if (typeof(obj.parent) == 'string') {
16507                 this.elmodules.push(obj);
16508                 return;
16509             }
16510             if (obj.parent.constructor != Roo.XComponent) {
16511                 Roo.debug && Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
16512             }
16513             if (!obj.parent.modules) {
16514                 obj.parent.modules = new Roo.util.MixedCollection(false, 
16515                     function(o) { return o.order + '' }
16516                 );
16517             }
16518             if (obj.parent.disabled) {
16519                 obj.disabled = true;
16520             }
16521             obj.parent.modules.add(obj);
16522         }, this);
16523     },
16524     
16525      /**
16526      * make a list of modules to build.
16527      * @return {Array} list of modules. 
16528      */ 
16529     
16530     buildOrder : function()
16531     {
16532         var _this = this;
16533         var cmp = function(a,b) {   
16534             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
16535         };
16536         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
16537             throw "No top level modules to build";
16538         }
16539         
16540         // make a flat list in order of modules to build.
16541         var mods = this.topModule ? [ this.topModule ] : [];
16542                 
16543         
16544         // elmodules (is a list of DOM based modules )
16545         Roo.each(this.elmodules, function(e) {
16546             mods.push(e);
16547             if (!this.topModule &&
16548                 typeof(e.parent) == 'string' &&
16549                 e.parent.substring(0,1) == '#' &&
16550                 Roo.get(e.parent.substr(1))
16551                ) {
16552                 
16553                 _this.topModule = e;
16554             }
16555             
16556         });
16557
16558         
16559         // add modules to their parents..
16560         var addMod = function(m) {
16561             Roo.debug && Roo.log("build Order: add: " + m.name);
16562                 
16563             mods.push(m);
16564             if (m.modules && !m.disabled) {
16565                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
16566                 m.modules.keySort('ASC',  cmp );
16567                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
16568     
16569                 m.modules.each(addMod);
16570             } else {
16571                 Roo.debug && Roo.log("build Order: no child modules");
16572             }
16573             // not sure if this is used any more..
16574             if (m.finalize) {
16575                 m.finalize.name = m.name + " (clean up) ";
16576                 mods.push(m.finalize);
16577             }
16578             
16579         }
16580         if (this.topModule && this.topModule.modules) { 
16581             this.topModule.modules.keySort('ASC',  cmp );
16582             this.topModule.modules.each(addMod);
16583         } 
16584         return mods;
16585     },
16586     
16587      /**
16588      * Build the registered modules.
16589      * @param {Object} parent element.
16590      * @param {Function} optional method to call after module has been added.
16591      * 
16592      */ 
16593    
16594     build : function(opts) 
16595     {
16596         
16597         if (typeof(opts) != 'undefined') {
16598             Roo.apply(this,opts);
16599         }
16600         
16601         this.preBuild();
16602         var mods = this.buildOrder();
16603       
16604         //this.allmods = mods;
16605         //Roo.debug && Roo.log(mods);
16606         //return;
16607         if (!mods.length) { // should not happen
16608             throw "NO modules!!!";
16609         }
16610         
16611         
16612         var msg = "Building Interface...";
16613         // flash it up as modal - so we store the mask!?
16614         if (!this.hideProgress && Roo.MessageBox) {
16615             Roo.MessageBox.show({ title: 'loading' });
16616             Roo.MessageBox.show({
16617                title: "Please wait...",
16618                msg: msg,
16619                width:450,
16620                progress:true,
16621                buttons : false,
16622                closable:false,
16623                modal: false
16624               
16625             });
16626         }
16627         var total = mods.length;
16628         
16629         var _this = this;
16630         var progressRun = function() {
16631             if (!mods.length) {
16632                 Roo.debug && Roo.log('hide?');
16633                 if (!this.hideProgress && Roo.MessageBox) {
16634                     Roo.MessageBox.hide();
16635                 }
16636                 Roo.XComponent.build_from_html = false; // reset, so dialogs will be build from javascript
16637                 
16638                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
16639                 
16640                 // THE END...
16641                 return false;   
16642             }
16643             
16644             var m = mods.shift();
16645             
16646             
16647             Roo.debug && Roo.log(m);
16648             // not sure if this is supported any more.. - modules that are are just function
16649             if (typeof(m) == 'function') { 
16650                 m.call(this);
16651                 return progressRun.defer(10, _this);
16652             } 
16653             
16654             
16655             msg = "Building Interface " + (total  - mods.length) + 
16656                     " of " + total + 
16657                     (m.name ? (' - ' + m.name) : '');
16658                         Roo.debug && Roo.log(msg);
16659             if (!_this.hideProgress &&  Roo.MessageBox) { 
16660                 Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
16661             }
16662             
16663          
16664             // is the module disabled?
16665             var disabled = (typeof(m.disabled) == 'function') ?
16666                 m.disabled.call(m.module.disabled) : m.disabled;    
16667             
16668             
16669             if (disabled) {
16670                 return progressRun(); // we do not update the display!
16671             }
16672             
16673             // now build 
16674             
16675                         
16676                         
16677             m.render();
16678             // it's 10 on top level, and 1 on others??? why...
16679             return progressRun.defer(10, _this);
16680              
16681         }
16682         progressRun.defer(1, _this);
16683      
16684         
16685         
16686     },
16687     /**
16688      * Overlay a set of modified strings onto a component
16689      * This is dependant on our builder exporting the strings and 'named strings' elements.
16690      * 
16691      * @param {Object} element to overlay on - eg. Pman.Dialog.Login
16692      * @param {Object} associative array of 'named' string and it's new value.
16693      * 
16694      */
16695         overlayStrings : function( component, strings )
16696     {
16697         if (typeof(component['_named_strings']) == 'undefined') {
16698             throw "ERROR: component does not have _named_strings";
16699         }
16700         for ( var k in strings ) {
16701             var md = typeof(component['_named_strings'][k]) == 'undefined' ? false : component['_named_strings'][k];
16702             if (md !== false) {
16703                 component['_strings'][md] = strings[k];
16704             } else {
16705                 Roo.log('could not find named string: ' + k + ' in');
16706                 Roo.log(component);
16707             }
16708             
16709         }
16710         
16711     },
16712     
16713         
16714         /**
16715          * Event Object.
16716          *
16717          *
16718          */
16719         event: false, 
16720     /**
16721          * wrapper for event.on - aliased later..  
16722          * Typically use to register a event handler for register:
16723          *
16724          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
16725          *
16726          */
16727     on : false
16728    
16729     
16730     
16731 });
16732
16733 Roo.XComponent.event = new Roo.util.Observable({
16734                 events : { 
16735                         /**
16736                          * @event register
16737                          * Fires when an Component is registered,
16738                          * set the disable property on the Component to stop registration.
16739                          * @param {Roo.XComponent} c the component being registerd.
16740                          * 
16741                          */
16742                         'register' : true,
16743             /**
16744                          * @event beforebuild
16745                          * Fires before each Component is built
16746                          * can be used to apply permissions.
16747                          * @param {Roo.XComponent} c the component being registerd.
16748                          * 
16749                          */
16750                         'beforebuild' : true,
16751                         /**
16752                          * @event buildcomplete
16753                          * Fires on the top level element when all elements have been built
16754                          * @param {Roo.XComponent} the top level component.
16755                          */
16756                         'buildcomplete' : true
16757                         
16758                 }
16759 });
16760
16761 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
16762  //
16763  /**
16764  * marked - a markdown parser
16765  * Copyright (c) 2011-2014, Christopher Jeffrey. (MIT Licensed)
16766  * https://github.com/chjj/marked
16767  */
16768
16769
16770 /**
16771  *
16772  * Roo.Markdown - is a very crude wrapper around marked..
16773  *
16774  * usage:
16775  * 
16776  * alert( Roo.Markdown.toHtml("Markdown *rocks*.") );
16777  * 
16778  * Note: move the sample code to the bottom of this
16779  * file before uncommenting it.
16780  *
16781  */
16782
16783 Roo.Markdown = {};
16784 Roo.Markdown.toHtml = function(text) {
16785     
16786     var c = new Roo.Markdown.marked.setOptions({
16787             renderer: new Roo.Markdown.marked.Renderer(),
16788             gfm: true,
16789             tables: true,
16790             breaks: false,
16791             pedantic: false,
16792             sanitize: false,
16793             smartLists: true,
16794             smartypants: false
16795           });
16796     // A FEW HACKS!!?
16797     
16798     text = text.replace(/\\\n/g,' ');
16799     return Roo.Markdown.marked(text);
16800 };
16801 //
16802 // converter
16803 //
16804 // Wraps all "globals" so that the only thing
16805 // exposed is makeHtml().
16806 //
16807 (function() {
16808     
16809      /**
16810          * eval:var:escape
16811          * eval:var:unescape
16812          * eval:var:replace
16813          */
16814       
16815     /**
16816      * Helpers
16817      */
16818     
16819     var escape = function (html, encode) {
16820       return html
16821         .replace(!encode ? /&(?!#?\w+;)/g : /&/g, '&amp;')
16822         .replace(/</g, '&lt;')
16823         .replace(/>/g, '&gt;')
16824         .replace(/"/g, '&quot;')
16825         .replace(/'/g, '&#39;');
16826     }
16827     
16828     var unescape = function (html) {
16829         // explicitly match decimal, hex, and named HTML entities 
16830       return html.replace(/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/g, function(_, n) {
16831         n = n.toLowerCase();
16832         if (n === 'colon') { return ':'; }
16833         if (n.charAt(0) === '#') {
16834           return n.charAt(1) === 'x'
16835             ? String.fromCharCode(parseInt(n.substring(2), 16))
16836             : String.fromCharCode(+n.substring(1));
16837         }
16838         return '';
16839       });
16840     }
16841     
16842     var replace = function (regex, opt) {
16843       regex = regex.source;
16844       opt = opt || '';
16845       return function self(name, val) {
16846         if (!name) { return new RegExp(regex, opt); }
16847         val = val.source || val;
16848         val = val.replace(/(^|[^\[])\^/g, '$1');
16849         regex = regex.replace(name, val);
16850         return self;
16851       };
16852     }
16853
16854
16855          /**
16856          * eval:var:noop
16857     */
16858     var noop = function () {}
16859     noop.exec = noop;
16860     
16861          /**
16862          * eval:var:merge
16863     */
16864     var merge = function (obj) {
16865       var i = 1
16866         , target
16867         , key;
16868     
16869       for (; i < arguments.length; i++) {
16870         target = arguments[i];
16871         for (key in target) {
16872           if (Object.prototype.hasOwnProperty.call(target, key)) {
16873             obj[key] = target[key];
16874           }
16875         }
16876       }
16877     
16878       return obj;
16879     }
16880     
16881     
16882     /**
16883      * Block-Level Grammar
16884      */
16885     
16886     
16887     
16888     
16889     var block = {
16890       newline: /^\n+/,
16891       code: /^( {4}[^\n]+\n*)+/,
16892       fences: noop,
16893       hr: /^( *[-*_]){3,} *(?:\n+|$)/,
16894       heading: /^ *(#{1,6}) *([^\n]+?) *#* *(?:\n+|$)/,
16895       nptable: noop,
16896       lheading: /^([^\n]+)\n *(=|-){2,} *(?:\n+|$)/,
16897       blockquote: /^( *>[^\n]+(\n(?!def)[^\n]+)*\n*)+/,
16898       list: /^( *)(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,
16899       html: /^ *(?:comment *(?:\n|\s*$)|closed *(?:\n{2,}|\s*$)|closing *(?:\n{2,}|\s*$))/,
16900       def: /^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +["(]([^\n]+)[")])? *(?:\n+|$)/,
16901       table: noop,
16902       paragraph: /^((?:[^\n]+\n?(?!hr|heading|lheading|blockquote|tag|def))+)\n*/,
16903       text: /^[^\n]+/
16904     };
16905     
16906     block.bullet = /(?:[*+-]|\d+\.)/;
16907     block.item = /^( *)(bull) [^\n]*(?:\n(?!\1bull )[^\n]*)*/;
16908     block.item = replace(block.item, 'gm')
16909       (/bull/g, block.bullet)
16910       ();
16911     
16912     block.list = replace(block.list)
16913       (/bull/g, block.bullet)
16914       ('hr', '\\n+(?=\\1?(?:[-*_] *){3,}(?:\\n+|$))')
16915       ('def', '\\n+(?=' + block.def.source + ')')
16916       ();
16917     
16918     block.blockquote = replace(block.blockquote)
16919       ('def', block.def)
16920       ();
16921     
16922     block._tag = '(?!(?:'
16923       + 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code'
16924       + '|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo'
16925       + '|span|br|wbr|ins|del|img)\\b)\\w+(?!:/|[^\\w\\s@]*@)\\b';
16926     
16927     block.html = replace(block.html)
16928       ('comment', /<!--[\s\S]*?-->/)
16929       ('closed', /<(tag)[\s\S]+?<\/\1>/)
16930       ('closing', /<tag(?:"[^"]*"|'[^']*'|[^'">])*?>/)
16931       (/tag/g, block._tag)
16932       ();
16933     
16934     block.paragraph = replace(block.paragraph)
16935       ('hr', block.hr)
16936       ('heading', block.heading)
16937       ('lheading', block.lheading)
16938       ('blockquote', block.blockquote)
16939       ('tag', '<' + block._tag)
16940       ('def', block.def)
16941       ();
16942     
16943     /**
16944      * Normal Block Grammar
16945      */
16946     
16947     block.normal = merge({}, block);
16948     
16949     /**
16950      * GFM Block Grammar
16951      */
16952     
16953     block.gfm = merge({}, block.normal, {
16954       fences: /^ *(`{3,}|~{3,})[ \.]*(\S+)? *\n([\s\S]*?)\s*\1 *(?:\n+|$)/,
16955       paragraph: /^/,
16956       heading: /^ *(#{1,6}) +([^\n]+?) *#* *(?:\n+|$)/
16957     });
16958     
16959     block.gfm.paragraph = replace(block.paragraph)
16960       ('(?!', '(?!'
16961         + block.gfm.fences.source.replace('\\1', '\\2') + '|'
16962         + block.list.source.replace('\\1', '\\3') + '|')
16963       ();
16964     
16965     /**
16966      * GFM + Tables Block Grammar
16967      */
16968     
16969     block.tables = merge({}, block.gfm, {
16970       nptable: /^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*/,
16971       table: /^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)\n*/
16972     });
16973     
16974     /**
16975      * Block Lexer
16976      */
16977     
16978     var Lexer = function (options) {
16979       this.tokens = [];
16980       this.tokens.links = {};
16981       this.options = options || marked.defaults;
16982       this.rules = block.normal;
16983     
16984       if (this.options.gfm) {
16985         if (this.options.tables) {
16986           this.rules = block.tables;
16987         } else {
16988           this.rules = block.gfm;
16989         }
16990       }
16991     }
16992     
16993     /**
16994      * Expose Block Rules
16995      */
16996     
16997     Lexer.rules = block;
16998     
16999     /**
17000      * Static Lex Method
17001      */
17002     
17003     Lexer.lex = function(src, options) {
17004       var lexer = new Lexer(options);
17005       return lexer.lex(src);
17006     };
17007     
17008     /**
17009      * Preprocessing
17010      */
17011     
17012     Lexer.prototype.lex = function(src) {
17013       src = src
17014         .replace(/\r\n|\r/g, '\n')
17015         .replace(/\t/g, '    ')
17016         .replace(/\u00a0/g, ' ')
17017         .replace(/\u2424/g, '\n');
17018     
17019       return this.token(src, true);
17020     };
17021     
17022     /**
17023      * Lexing
17024      */
17025     
17026     Lexer.prototype.token = function(src, top, bq) {
17027       var src = src.replace(/^ +$/gm, '')
17028         , next
17029         , loose
17030         , cap
17031         , bull
17032         , b
17033         , item
17034         , space
17035         , i
17036         , l;
17037     
17038       while (src) {
17039         // newline
17040         if (cap = this.rules.newline.exec(src)) {
17041           src = src.substring(cap[0].length);
17042           if (cap[0].length > 1) {
17043             this.tokens.push({
17044               type: 'space'
17045             });
17046           }
17047         }
17048     
17049         // code
17050         if (cap = this.rules.code.exec(src)) {
17051           src = src.substring(cap[0].length);
17052           cap = cap[0].replace(/^ {4}/gm, '');
17053           this.tokens.push({
17054             type: 'code',
17055             text: !this.options.pedantic
17056               ? cap.replace(/\n+$/, '')
17057               : cap
17058           });
17059           continue;
17060         }
17061     
17062         // fences (gfm)
17063         if (cap = this.rules.fences.exec(src)) {
17064           src = src.substring(cap[0].length);
17065           this.tokens.push({
17066             type: 'code',
17067             lang: cap[2],
17068             text: cap[3] || ''
17069           });
17070           continue;
17071         }
17072     
17073         // heading
17074         if (cap = this.rules.heading.exec(src)) {
17075           src = src.substring(cap[0].length);
17076           this.tokens.push({
17077             type: 'heading',
17078             depth: cap[1].length,
17079             text: cap[2]
17080           });
17081           continue;
17082         }
17083     
17084         // table no leading pipe (gfm)
17085         if (top && (cap = this.rules.nptable.exec(src))) {
17086           src = src.substring(cap[0].length);
17087     
17088           item = {
17089             type: 'table',
17090             header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
17091             align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
17092             cells: cap[3].replace(/\n$/, '').split('\n')
17093           };
17094     
17095           for (i = 0; i < item.align.length; i++) {
17096             if (/^ *-+: *$/.test(item.align[i])) {
17097               item.align[i] = 'right';
17098             } else if (/^ *:-+: *$/.test(item.align[i])) {
17099               item.align[i] = 'center';
17100             } else if (/^ *:-+ *$/.test(item.align[i])) {
17101               item.align[i] = 'left';
17102             } else {
17103               item.align[i] = null;
17104             }
17105           }
17106     
17107           for (i = 0; i < item.cells.length; i++) {
17108             item.cells[i] = item.cells[i].split(/ *\| */);
17109           }
17110     
17111           this.tokens.push(item);
17112     
17113           continue;
17114         }
17115     
17116         // lheading
17117         if (cap = this.rules.lheading.exec(src)) {
17118           src = src.substring(cap[0].length);
17119           this.tokens.push({
17120             type: 'heading',
17121             depth: cap[2] === '=' ? 1 : 2,
17122             text: cap[1]
17123           });
17124           continue;
17125         }
17126     
17127         // hr
17128         if (cap = this.rules.hr.exec(src)) {
17129           src = src.substring(cap[0].length);
17130           this.tokens.push({
17131             type: 'hr'
17132           });
17133           continue;
17134         }
17135     
17136         // blockquote
17137         if (cap = this.rules.blockquote.exec(src)) {
17138           src = src.substring(cap[0].length);
17139     
17140           this.tokens.push({
17141             type: 'blockquote_start'
17142           });
17143     
17144           cap = cap[0].replace(/^ *> ?/gm, '');
17145     
17146           // Pass `top` to keep the current
17147           // "toplevel" state. This is exactly
17148           // how markdown.pl works.
17149           this.token(cap, top, true);
17150     
17151           this.tokens.push({
17152             type: 'blockquote_end'
17153           });
17154     
17155           continue;
17156         }
17157     
17158         // list
17159         if (cap = this.rules.list.exec(src)) {
17160           src = src.substring(cap[0].length);
17161           bull = cap[2];
17162     
17163           this.tokens.push({
17164             type: 'list_start',
17165             ordered: bull.length > 1
17166           });
17167     
17168           // Get each top-level item.
17169           cap = cap[0].match(this.rules.item);
17170     
17171           next = false;
17172           l = cap.length;
17173           i = 0;
17174     
17175           for (; i < l; i++) {
17176             item = cap[i];
17177     
17178             // Remove the list item's bullet
17179             // so it is seen as the next token.
17180             space = item.length;
17181             item = item.replace(/^ *([*+-]|\d+\.) +/, '');
17182     
17183             // Outdent whatever the
17184             // list item contains. Hacky.
17185             if (~item.indexOf('\n ')) {
17186               space -= item.length;
17187               item = !this.options.pedantic
17188                 ? item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '')
17189                 : item.replace(/^ {1,4}/gm, '');
17190             }
17191     
17192             // Determine whether the next list item belongs here.
17193             // Backpedal if it does not belong in this list.
17194             if (this.options.smartLists && i !== l - 1) {
17195               b = block.bullet.exec(cap[i + 1])[0];
17196               if (bull !== b && !(bull.length > 1 && b.length > 1)) {
17197                 src = cap.slice(i + 1).join('\n') + src;
17198                 i = l - 1;
17199               }
17200             }
17201     
17202             // Determine whether item is loose or not.
17203             // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/
17204             // for discount behavior.
17205             loose = next || /\n\n(?!\s*$)/.test(item);
17206             if (i !== l - 1) {
17207               next = item.charAt(item.length - 1) === '\n';
17208               if (!loose) { loose = next; }
17209             }
17210     
17211             this.tokens.push({
17212               type: loose
17213                 ? 'loose_item_start'
17214                 : 'list_item_start'
17215             });
17216     
17217             // Recurse.
17218             this.token(item, false, bq);
17219     
17220             this.tokens.push({
17221               type: 'list_item_end'
17222             });
17223           }
17224     
17225           this.tokens.push({
17226             type: 'list_end'
17227           });
17228     
17229           continue;
17230         }
17231     
17232         // html
17233         if (cap = this.rules.html.exec(src)) {
17234           src = src.substring(cap[0].length);
17235           this.tokens.push({
17236             type: this.options.sanitize
17237               ? 'paragraph'
17238               : 'html',
17239             pre: !this.options.sanitizer
17240               && (cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style'),
17241             text: cap[0]
17242           });
17243           continue;
17244         }
17245     
17246         // def
17247         if ((!bq && top) && (cap = this.rules.def.exec(src))) {
17248           src = src.substring(cap[0].length);
17249           this.tokens.links[cap[1].toLowerCase()] = {
17250             href: cap[2],
17251             title: cap[3]
17252           };
17253           continue;
17254         }
17255     
17256         // table (gfm)
17257         if (top && (cap = this.rules.table.exec(src))) {
17258           src = src.substring(cap[0].length);
17259     
17260           item = {
17261             type: 'table',
17262             header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
17263             align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
17264             cells: cap[3].replace(/(?: *\| *)?\n$/, '').split('\n')
17265           };
17266     
17267           for (i = 0; i < item.align.length; i++) {
17268             if (/^ *-+: *$/.test(item.align[i])) {
17269               item.align[i] = 'right';
17270             } else if (/^ *:-+: *$/.test(item.align[i])) {
17271               item.align[i] = 'center';
17272             } else if (/^ *:-+ *$/.test(item.align[i])) {
17273               item.align[i] = 'left';
17274             } else {
17275               item.align[i] = null;
17276             }
17277           }
17278     
17279           for (i = 0; i < item.cells.length; i++) {
17280             item.cells[i] = item.cells[i]
17281               .replace(/^ *\| *| *\| *$/g, '')
17282               .split(/ *\| */);
17283           }
17284     
17285           this.tokens.push(item);
17286     
17287           continue;
17288         }
17289     
17290         // top-level paragraph
17291         if (top && (cap = this.rules.paragraph.exec(src))) {
17292           src = src.substring(cap[0].length);
17293           this.tokens.push({
17294             type: 'paragraph',
17295             text: cap[1].charAt(cap[1].length - 1) === '\n'
17296               ? cap[1].slice(0, -1)
17297               : cap[1]
17298           });
17299           continue;
17300         }
17301     
17302         // text
17303         if (cap = this.rules.text.exec(src)) {
17304           // Top-level should never reach here.
17305           src = src.substring(cap[0].length);
17306           this.tokens.push({
17307             type: 'text',
17308             text: cap[0]
17309           });
17310           continue;
17311         }
17312     
17313         if (src) {
17314           throw new
17315             Error('Infinite loop on byte: ' + src.charCodeAt(0));
17316         }
17317       }
17318     
17319       return this.tokens;
17320     };
17321     
17322     /**
17323      * Inline-Level Grammar
17324      */
17325     
17326     var inline = {
17327       escape: /^\\([\\`*{}\[\]()#+\-.!_>])/,
17328       autolink: /^<([^ >]+(@|:\/)[^ >]+)>/,
17329       url: noop,
17330       tag: /^<!--[\s\S]*?-->|^<\/?\w+(?:"[^"]*"|'[^']*'|[^'">])*?>/,
17331       link: /^!?\[(inside)\]\(href\)/,
17332       reflink: /^!?\[(inside)\]\s*\[([^\]]*)\]/,
17333       nolink: /^!?\[((?:\[[^\]]*\]|[^\[\]])*)\]/,
17334       strong: /^__([\s\S]+?)__(?!_)|^\*\*([\s\S]+?)\*\*(?!\*)/,
17335       em: /^\b_((?:[^_]|__)+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/,
17336       code: /^(`+)\s*([\s\S]*?[^`])\s*\1(?!`)/,
17337       br: /^ {2,}\n(?!\s*$)/,
17338       del: noop,
17339       text: /^[\s\S]+?(?=[\\<!\[_*`]| {2,}\n|$)/
17340     };
17341     
17342     inline._inside = /(?:\[[^\]]*\]|[^\[\]]|\](?=[^\[]*\]))*/;
17343     inline._href = /\s*<?([\s\S]*?)>?(?:\s+['"]([\s\S]*?)['"])?\s*/;
17344     
17345     inline.link = replace(inline.link)
17346       ('inside', inline._inside)
17347       ('href', inline._href)
17348       ();
17349     
17350     inline.reflink = replace(inline.reflink)
17351       ('inside', inline._inside)
17352       ();
17353     
17354     /**
17355      * Normal Inline Grammar
17356      */
17357     
17358     inline.normal = merge({}, inline);
17359     
17360     /**
17361      * Pedantic Inline Grammar
17362      */
17363     
17364     inline.pedantic = merge({}, inline.normal, {
17365       strong: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,
17366       em: /^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/
17367     });
17368     
17369     /**
17370      * GFM Inline Grammar
17371      */
17372     
17373     inline.gfm = merge({}, inline.normal, {
17374       escape: replace(inline.escape)('])', '~|])')(),
17375       url: /^(https?:\/\/[^\s<]+[^<.,:;"')\]\s])/,
17376       del: /^~~(?=\S)([\s\S]*?\S)~~/,
17377       text: replace(inline.text)
17378         (']|', '~]|')
17379         ('|', '|https?://|')
17380         ()
17381     });
17382     
17383     /**
17384      * GFM + Line Breaks Inline Grammar
17385      */
17386     
17387     inline.breaks = merge({}, inline.gfm, {
17388       br: replace(inline.br)('{2,}', '*')(),
17389       text: replace(inline.gfm.text)('{2,}', '*')()
17390     });
17391     
17392     /**
17393      * Inline Lexer & Compiler
17394      */
17395     
17396     var InlineLexer  = function (links, options) {
17397       this.options = options || marked.defaults;
17398       this.links = links;
17399       this.rules = inline.normal;
17400       this.renderer = this.options.renderer || new Renderer;
17401       this.renderer.options = this.options;
17402     
17403       if (!this.links) {
17404         throw new
17405           Error('Tokens array requires a `links` property.');
17406       }
17407     
17408       if (this.options.gfm) {
17409         if (this.options.breaks) {
17410           this.rules = inline.breaks;
17411         } else {
17412           this.rules = inline.gfm;
17413         }
17414       } else if (this.options.pedantic) {
17415         this.rules = inline.pedantic;
17416       }
17417     }
17418     
17419     /**
17420      * Expose Inline Rules
17421      */
17422     
17423     InlineLexer.rules = inline;
17424     
17425     /**
17426      * Static Lexing/Compiling Method
17427      */
17428     
17429     InlineLexer.output = function(src, links, options) {
17430       var inline = new InlineLexer(links, options);
17431       return inline.output(src);
17432     };
17433     
17434     /**
17435      * Lexing/Compiling
17436      */
17437     
17438     InlineLexer.prototype.output = function(src) {
17439       var out = ''
17440         , link
17441         , text
17442         , href
17443         , cap;
17444     
17445       while (src) {
17446         // escape
17447         if (cap = this.rules.escape.exec(src)) {
17448           src = src.substring(cap[0].length);
17449           out += cap[1];
17450           continue;
17451         }
17452     
17453         // autolink
17454         if (cap = this.rules.autolink.exec(src)) {
17455           src = src.substring(cap[0].length);
17456           if (cap[2] === '@') {
17457             text = cap[1].charAt(6) === ':'
17458               ? this.mangle(cap[1].substring(7))
17459               : this.mangle(cap[1]);
17460             href = this.mangle('mailto:') + text;
17461           } else {
17462             text = escape(cap[1]);
17463             href = text;
17464           }
17465           out += this.renderer.link(href, null, text);
17466           continue;
17467         }
17468     
17469         // url (gfm)
17470         if (!this.inLink && (cap = this.rules.url.exec(src))) {
17471           src = src.substring(cap[0].length);
17472           text = escape(cap[1]);
17473           href = text;
17474           out += this.renderer.link(href, null, text);
17475           continue;
17476         }
17477     
17478         // tag
17479         if (cap = this.rules.tag.exec(src)) {
17480           if (!this.inLink && /^<a /i.test(cap[0])) {
17481             this.inLink = true;
17482           } else if (this.inLink && /^<\/a>/i.test(cap[0])) {
17483             this.inLink = false;
17484           }
17485           src = src.substring(cap[0].length);
17486           out += this.options.sanitize
17487             ? this.options.sanitizer
17488               ? this.options.sanitizer(cap[0])
17489               : escape(cap[0])
17490             : cap[0];
17491           continue;
17492         }
17493     
17494         // link
17495         if (cap = this.rules.link.exec(src)) {
17496           src = src.substring(cap[0].length);
17497           this.inLink = true;
17498           out += this.outputLink(cap, {
17499             href: cap[2],
17500             title: cap[3]
17501           });
17502           this.inLink = false;
17503           continue;
17504         }
17505     
17506         // reflink, nolink
17507         if ((cap = this.rules.reflink.exec(src))
17508             || (cap = this.rules.nolink.exec(src))) {
17509           src = src.substring(cap[0].length);
17510           link = (cap[2] || cap[1]).replace(/\s+/g, ' ');
17511           link = this.links[link.toLowerCase()];
17512           if (!link || !link.href) {
17513             out += cap[0].charAt(0);
17514             src = cap[0].substring(1) + src;
17515             continue;
17516           }
17517           this.inLink = true;
17518           out += this.outputLink(cap, link);
17519           this.inLink = false;
17520           continue;
17521         }
17522     
17523         // strong
17524         if (cap = this.rules.strong.exec(src)) {
17525           src = src.substring(cap[0].length);
17526           out += this.renderer.strong(this.output(cap[2] || cap[1]));
17527           continue;
17528         }
17529     
17530         // em
17531         if (cap = this.rules.em.exec(src)) {
17532           src = src.substring(cap[0].length);
17533           out += this.renderer.em(this.output(cap[2] || cap[1]));
17534           continue;
17535         }
17536     
17537         // code
17538         if (cap = this.rules.code.exec(src)) {
17539           src = src.substring(cap[0].length);
17540           out += this.renderer.codespan(escape(cap[2], true));
17541           continue;
17542         }
17543     
17544         // br
17545         if (cap = this.rules.br.exec(src)) {
17546           src = src.substring(cap[0].length);
17547           out += this.renderer.br();
17548           continue;
17549         }
17550     
17551         // del (gfm)
17552         if (cap = this.rules.del.exec(src)) {
17553           src = src.substring(cap[0].length);
17554           out += this.renderer.del(this.output(cap[1]));
17555           continue;
17556         }
17557     
17558         // text
17559         if (cap = this.rules.text.exec(src)) {
17560           src = src.substring(cap[0].length);
17561           out += this.renderer.text(escape(this.smartypants(cap[0])));
17562           continue;
17563         }
17564     
17565         if (src) {
17566           throw new
17567             Error('Infinite loop on byte: ' + src.charCodeAt(0));
17568         }
17569       }
17570     
17571       return out;
17572     };
17573     
17574     /**
17575      * Compile Link
17576      */
17577     
17578     InlineLexer.prototype.outputLink = function(cap, link) {
17579       var href = escape(link.href)
17580         , title = link.title ? escape(link.title) : null;
17581     
17582       return cap[0].charAt(0) !== '!'
17583         ? this.renderer.link(href, title, this.output(cap[1]))
17584         : this.renderer.image(href, title, escape(cap[1]));
17585     };
17586     
17587     /**
17588      * Smartypants Transformations
17589      */
17590     
17591     InlineLexer.prototype.smartypants = function(text) {
17592       if (!this.options.smartypants)  { return text; }
17593       return text
17594         // em-dashes
17595         .replace(/---/g, '\u2014')
17596         // en-dashes
17597         .replace(/--/g, '\u2013')
17598         // opening singles
17599         .replace(/(^|[-\u2014/(\[{"\s])'/g, '$1\u2018')
17600         // closing singles & apostrophes
17601         .replace(/'/g, '\u2019')
17602         // opening doubles
17603         .replace(/(^|[-\u2014/(\[{\u2018\s])"/g, '$1\u201c')
17604         // closing doubles
17605         .replace(/"/g, '\u201d')
17606         // ellipses
17607         .replace(/\.{3}/g, '\u2026');
17608     };
17609     
17610     /**
17611      * Mangle Links
17612      */
17613     
17614     InlineLexer.prototype.mangle = function(text) {
17615       if (!this.options.mangle) { return text; }
17616       var out = ''
17617         , l = text.length
17618         , i = 0
17619         , ch;
17620     
17621       for (; i < l; i++) {
17622         ch = text.charCodeAt(i);
17623         if (Math.random() > 0.5) {
17624           ch = 'x' + ch.toString(16);
17625         }
17626         out += '&#' + ch + ';';
17627       }
17628     
17629       return out;
17630     };
17631     
17632     /**
17633      * Renderer
17634      */
17635     
17636      /**
17637          * eval:var:Renderer
17638     */
17639     
17640     var Renderer   = function (options) {
17641       this.options = options || {};
17642     }
17643     
17644     Renderer.prototype.code = function(code, lang, escaped) {
17645       if (this.options.highlight) {
17646         var out = this.options.highlight(code, lang);
17647         if (out != null && out !== code) {
17648           escaped = true;
17649           code = out;
17650         }
17651       } else {
17652             // hack!!! - it's already escapeD?
17653             escaped = true;
17654       }
17655     
17656       if (!lang) {
17657         return '<pre><code>'
17658           + (escaped ? code : escape(code, true))
17659           + '\n</code></pre>';
17660       }
17661     
17662       return '<pre><code class="'
17663         + this.options.langPrefix
17664         + escape(lang, true)
17665         + '">'
17666         + (escaped ? code : escape(code, true))
17667         + '\n</code></pre>\n';
17668     };
17669     
17670     Renderer.prototype.blockquote = function(quote) {
17671       return '<blockquote>\n' + quote + '</blockquote>\n';
17672     };
17673     
17674     Renderer.prototype.html = function(html) {
17675       return html;
17676     };
17677     
17678     Renderer.prototype.heading = function(text, level, raw) {
17679       return '<h'
17680         + level
17681         + ' id="'
17682         + this.options.headerPrefix
17683         + raw.toLowerCase().replace(/[^\w]+/g, '-')
17684         + '">'
17685         + text
17686         + '</h'
17687         + level
17688         + '>\n';
17689     };
17690     
17691     Renderer.prototype.hr = function() {
17692       return this.options.xhtml ? '<hr/>\n' : '<hr>\n';
17693     };
17694     
17695     Renderer.prototype.list = function(body, ordered) {
17696       var type = ordered ? 'ol' : 'ul';
17697       return '<' + type + '>\n' + body + '</' + type + '>\n';
17698     };
17699     
17700     Renderer.prototype.listitem = function(text) {
17701       return '<li>' + text + '</li>\n';
17702     };
17703     
17704     Renderer.prototype.paragraph = function(text) {
17705       return '<p>' + text + '</p>\n';
17706     };
17707     
17708     Renderer.prototype.table = function(header, body) {
17709       return '<table class="table table-striped">\n'
17710         + '<thead>\n'
17711         + header
17712         + '</thead>\n'
17713         + '<tbody>\n'
17714         + body
17715         + '</tbody>\n'
17716         + '</table>\n';
17717     };
17718     
17719     Renderer.prototype.tablerow = function(content) {
17720       return '<tr>\n' + content + '</tr>\n';
17721     };
17722     
17723     Renderer.prototype.tablecell = function(content, flags) {
17724       var type = flags.header ? 'th' : 'td';
17725       var tag = flags.align
17726         ? '<' + type + ' style="text-align:' + flags.align + '">'
17727         : '<' + type + '>';
17728       return tag + content + '</' + type + '>\n';
17729     };
17730     
17731     // span level renderer
17732     Renderer.prototype.strong = function(text) {
17733       return '<strong>' + text + '</strong>';
17734     };
17735     
17736     Renderer.prototype.em = function(text) {
17737       return '<em>' + text + '</em>';
17738     };
17739     
17740     Renderer.prototype.codespan = function(text) {
17741       return '<code>' + text + '</code>';
17742     };
17743     
17744     Renderer.prototype.br = function() {
17745       return this.options.xhtml ? '<br/>' : '<br>';
17746     };
17747     
17748     Renderer.prototype.del = function(text) {
17749       return '<del>' + text + '</del>';
17750     };
17751     
17752     Renderer.prototype.link = function(href, title, text) {
17753       if (this.options.sanitize) {
17754         try {
17755           var prot = decodeURIComponent(unescape(href))
17756             .replace(/[^\w:]/g, '')
17757             .toLowerCase();
17758         } catch (e) {
17759           return '';
17760         }
17761         if (prot.indexOf('javascript:') === 0 || prot.indexOf('vbscript:') === 0) {
17762           return '';
17763         }
17764       }
17765       var out = '<a href="' + href + '"';
17766       if (title) {
17767         out += ' title="' + title + '"';
17768       }
17769       out += '>' + text + '</a>';
17770       return out;
17771     };
17772     
17773     Renderer.prototype.image = function(href, title, text) {
17774       var out = '<img src="' + href + '" alt="' + text + '"';
17775       if (title) {
17776         out += ' title="' + title + '"';
17777       }
17778       out += this.options.xhtml ? '/>' : '>';
17779       return out;
17780     };
17781     
17782     Renderer.prototype.text = function(text) {
17783       return text;
17784     };
17785     
17786     /**
17787      * Parsing & Compiling
17788      */
17789          /**
17790          * eval:var:Parser
17791     */
17792     
17793     var Parser= function (options) {
17794       this.tokens = [];
17795       this.token = null;
17796       this.options = options || marked.defaults;
17797       this.options.renderer = this.options.renderer || new Renderer;
17798       this.renderer = this.options.renderer;
17799       this.renderer.options = this.options;
17800     }
17801     
17802     /**
17803      * Static Parse Method
17804      */
17805     
17806     Parser.parse = function(src, options, renderer) {
17807       var parser = new Parser(options, renderer);
17808       return parser.parse(src);
17809     };
17810     
17811     /**
17812      * Parse Loop
17813      */
17814     
17815     Parser.prototype.parse = function(src) {
17816       this.inline = new InlineLexer(src.links, this.options, this.renderer);
17817       this.tokens = src.reverse();
17818     
17819       var out = '';
17820       while (this.next()) {
17821         out += this.tok();
17822       }
17823     
17824       return out;
17825     };
17826     
17827     /**
17828      * Next Token
17829      */
17830     
17831     Parser.prototype.next = function() {
17832       return this.token = this.tokens.pop();
17833     };
17834     
17835     /**
17836      * Preview Next Token
17837      */
17838     
17839     Parser.prototype.peek = function() {
17840       return this.tokens[this.tokens.length - 1] || 0;
17841     };
17842     
17843     /**
17844      * Parse Text Tokens
17845      */
17846     
17847     Parser.prototype.parseText = function() {
17848       var body = this.token.text;
17849     
17850       while (this.peek().type === 'text') {
17851         body += '\n' + this.next().text;
17852       }
17853     
17854       return this.inline.output(body);
17855     };
17856     
17857     /**
17858      * Parse Current Token
17859      */
17860     
17861     Parser.prototype.tok = function() {
17862       switch (this.token.type) {
17863         case 'space': {
17864           return '';
17865         }
17866         case 'hr': {
17867           return this.renderer.hr();
17868         }
17869         case 'heading': {
17870           return this.renderer.heading(
17871             this.inline.output(this.token.text),
17872             this.token.depth,
17873             this.token.text);
17874         }
17875         case 'code': {
17876           return this.renderer.code(this.token.text,
17877             this.token.lang,
17878             this.token.escaped);
17879         }
17880         case 'table': {
17881           var header = ''
17882             , body = ''
17883             , i
17884             , row
17885             , cell
17886             , flags
17887             , j;
17888     
17889           // header
17890           cell = '';
17891           for (i = 0; i < this.token.header.length; i++) {
17892             flags = { header: true, align: this.token.align[i] };
17893             cell += this.renderer.tablecell(
17894               this.inline.output(this.token.header[i]),
17895               { header: true, align: this.token.align[i] }
17896             );
17897           }
17898           header += this.renderer.tablerow(cell);
17899     
17900           for (i = 0; i < this.token.cells.length; i++) {
17901             row = this.token.cells[i];
17902     
17903             cell = '';
17904             for (j = 0; j < row.length; j++) {
17905               cell += this.renderer.tablecell(
17906                 this.inline.output(row[j]),
17907                 { header: false, align: this.token.align[j] }
17908               );
17909             }
17910     
17911             body += this.renderer.tablerow(cell);
17912           }
17913           return this.renderer.table(header, body);
17914         }
17915         case 'blockquote_start': {
17916           var body = '';
17917     
17918           while (this.next().type !== 'blockquote_end') {
17919             body += this.tok();
17920           }
17921     
17922           return this.renderer.blockquote(body);
17923         }
17924         case 'list_start': {
17925           var body = ''
17926             , ordered = this.token.ordered;
17927     
17928           while (this.next().type !== 'list_end') {
17929             body += this.tok();
17930           }
17931     
17932           return this.renderer.list(body, ordered);
17933         }
17934         case 'list_item_start': {
17935           var body = '';
17936     
17937           while (this.next().type !== 'list_item_end') {
17938             body += this.token.type === 'text'
17939               ? this.parseText()
17940               : this.tok();
17941           }
17942     
17943           return this.renderer.listitem(body);
17944         }
17945         case 'loose_item_start': {
17946           var body = '';
17947     
17948           while (this.next().type !== 'list_item_end') {
17949             body += this.tok();
17950           }
17951     
17952           return this.renderer.listitem(body);
17953         }
17954         case 'html': {
17955           var html = !this.token.pre && !this.options.pedantic
17956             ? this.inline.output(this.token.text)
17957             : this.token.text;
17958           return this.renderer.html(html);
17959         }
17960         case 'paragraph': {
17961           return this.renderer.paragraph(this.inline.output(this.token.text));
17962         }
17963         case 'text': {
17964           return this.renderer.paragraph(this.parseText());
17965         }
17966       }
17967     };
17968   
17969     
17970     /**
17971      * Marked
17972      */
17973          /**
17974          * eval:var:marked
17975     */
17976     var marked = function (src, opt, callback) {
17977       if (callback || typeof opt === 'function') {
17978         if (!callback) {
17979           callback = opt;
17980           opt = null;
17981         }
17982     
17983         opt = merge({}, marked.defaults, opt || {});
17984     
17985         var highlight = opt.highlight
17986           , tokens
17987           , pending
17988           , i = 0;
17989     
17990         try {
17991           tokens = Lexer.lex(src, opt)
17992         } catch (e) {
17993           return callback(e);
17994         }
17995     
17996         pending = tokens.length;
17997          /**
17998          * eval:var:done
17999     */
18000         var done = function(err) {
18001           if (err) {
18002             opt.highlight = highlight;
18003             return callback(err);
18004           }
18005     
18006           var out;
18007     
18008           try {
18009             out = Parser.parse(tokens, opt);
18010           } catch (e) {
18011             err = e;
18012           }
18013     
18014           opt.highlight = highlight;
18015     
18016           return err
18017             ? callback(err)
18018             : callback(null, out);
18019         };
18020     
18021         if (!highlight || highlight.length < 3) {
18022           return done();
18023         }
18024     
18025         delete opt.highlight;
18026     
18027         if (!pending) { return done(); }
18028     
18029         for (; i < tokens.length; i++) {
18030           (function(token) {
18031             if (token.type !== 'code') {
18032               return --pending || done();
18033             }
18034             return highlight(token.text, token.lang, function(err, code) {
18035               if (err) { return done(err); }
18036               if (code == null || code === token.text) {
18037                 return --pending || done();
18038               }
18039               token.text = code;
18040               token.escaped = true;
18041               --pending || done();
18042             });
18043           })(tokens[i]);
18044         }
18045     
18046         return;
18047       }
18048       try {
18049         if (opt) { opt = merge({}, marked.defaults, opt); }
18050         return Parser.parse(Lexer.lex(src, opt), opt);
18051       } catch (e) {
18052         e.message += '\nPlease report this to https://github.com/chjj/marked.';
18053         if ((opt || marked.defaults).silent) {
18054           return '<p>An error occured:</p><pre>'
18055             + escape(e.message + '', true)
18056             + '</pre>';
18057         }
18058         throw e;
18059       }
18060     }
18061     
18062     /**
18063      * Options
18064      */
18065     
18066     marked.options =
18067     marked.setOptions = function(opt) {
18068       merge(marked.defaults, opt);
18069       return marked;
18070     };
18071     
18072     marked.defaults = {
18073       gfm: true,
18074       tables: true,
18075       breaks: false,
18076       pedantic: false,
18077       sanitize: false,
18078       sanitizer: null,
18079       mangle: true,
18080       smartLists: false,
18081       silent: false,
18082       highlight: null,
18083       langPrefix: 'lang-',
18084       smartypants: false,
18085       headerPrefix: '',
18086       renderer: new Renderer,
18087       xhtml: false
18088     };
18089     
18090     /**
18091      * Expose
18092      */
18093     
18094     marked.Parser = Parser;
18095     marked.parser = Parser.parse;
18096     
18097     marked.Renderer = Renderer;
18098     
18099     marked.Lexer = Lexer;
18100     marked.lexer = Lexer.lex;
18101     
18102     marked.InlineLexer = InlineLexer;
18103     marked.inlineLexer = InlineLexer.output;
18104     
18105     marked.parse = marked;
18106     
18107     Roo.Markdown.marked = marked;
18108
18109 })();/*
18110  * Based on:
18111  * Ext JS Library 1.1.1
18112  * Copyright(c) 2006-2007, Ext JS, LLC.
18113  *
18114  * Originally Released Under LGPL - original licence link has changed is not relivant.
18115  *
18116  * Fork - LGPL
18117  * <script type="text/javascript">
18118  */
18119
18120
18121
18122 /*
18123  * These classes are derivatives of the similarly named classes in the YUI Library.
18124  * The original license:
18125  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
18126  * Code licensed under the BSD License:
18127  * http://developer.yahoo.net/yui/license.txt
18128  */
18129
18130 (function() {
18131
18132 var Event=Roo.EventManager;
18133 var Dom=Roo.lib.Dom;
18134
18135 /**
18136  * @class Roo.dd.DragDrop
18137  * @extends Roo.util.Observable
18138  * Defines the interface and base operation of items that that can be
18139  * dragged or can be drop targets.  It was designed to be extended, overriding
18140  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
18141  * Up to three html elements can be associated with a DragDrop instance:
18142  * <ul>
18143  * <li>linked element: the element that is passed into the constructor.
18144  * This is the element which defines the boundaries for interaction with
18145  * other DragDrop objects.</li>
18146  * <li>handle element(s): The drag operation only occurs if the element that
18147  * was clicked matches a handle element.  By default this is the linked
18148  * element, but there are times that you will want only a portion of the
18149  * linked element to initiate the drag operation, and the setHandleElId()
18150  * method provides a way to define this.</li>
18151  * <li>drag element: this represents the element that would be moved along
18152  * with the cursor during a drag operation.  By default, this is the linked
18153  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
18154  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
18155  * </li>
18156  * </ul>
18157  * This class should not be instantiated until the onload event to ensure that
18158  * the associated elements are available.
18159  * The following would define a DragDrop obj that would interact with any
18160  * other DragDrop obj in the "group1" group:
18161  * <pre>
18162  *  dd = new Roo.dd.DragDrop("div1", "group1");
18163  * </pre>
18164  * Since none of the event handlers have been implemented, nothing would
18165  * actually happen if you were to run the code above.  Normally you would
18166  * override this class or one of the default implementations, but you can
18167  * also override the methods you want on an instance of the class...
18168  * <pre>
18169  *  dd.onDragDrop = function(e, id) {
18170  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
18171  *  }
18172  * </pre>
18173  * @constructor
18174  * @param {String} id of the element that is linked to this instance
18175  * @param {String} sGroup the group of related DragDrop objects
18176  * @param {object} config an object containing configurable attributes
18177  *                Valid properties for DragDrop:
18178  *                    padding, isTarget, maintainOffset, primaryButtonOnly
18179  */
18180 Roo.dd.DragDrop = function(id, sGroup, config) {
18181     if (id) {
18182         this.init(id, sGroup, config);
18183     }
18184     
18185 };
18186
18187 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
18188
18189     /**
18190      * The id of the element associated with this object.  This is what we
18191      * refer to as the "linked element" because the size and position of
18192      * this element is used to determine when the drag and drop objects have
18193      * interacted.
18194      * @property id
18195      * @type String
18196      */
18197     id: null,
18198
18199     /**
18200      * Configuration attributes passed into the constructor
18201      * @property config
18202      * @type object
18203      */
18204     config: null,
18205
18206     /**
18207      * The id of the element that will be dragged.  By default this is same
18208      * as the linked element , but could be changed to another element. Ex:
18209      * Roo.dd.DDProxy
18210      * @property dragElId
18211      * @type String
18212      * @private
18213      */
18214     dragElId: null,
18215
18216     /**
18217      * the id of the element that initiates the drag operation.  By default
18218      * this is the linked element, but could be changed to be a child of this
18219      * element.  This lets us do things like only starting the drag when the
18220      * header element within the linked html element is clicked.
18221      * @property handleElId
18222      * @type String
18223      * @private
18224      */
18225     handleElId: null,
18226
18227     /**
18228      * An associative array of HTML tags that will be ignored if clicked.
18229      * @property invalidHandleTypes
18230      * @type {string: string}
18231      */
18232     invalidHandleTypes: null,
18233
18234     /**
18235      * An associative array of ids for elements that will be ignored if clicked
18236      * @property invalidHandleIds
18237      * @type {string: string}
18238      */
18239     invalidHandleIds: null,
18240
18241     /**
18242      * An indexted array of css class names for elements that will be ignored
18243      * if clicked.
18244      * @property invalidHandleClasses
18245      * @type string[]
18246      */
18247     invalidHandleClasses: null,
18248
18249     /**
18250      * The linked element's absolute X position at the time the drag was
18251      * started
18252      * @property startPageX
18253      * @type int
18254      * @private
18255      */
18256     startPageX: 0,
18257
18258     /**
18259      * The linked element's absolute X position at the time the drag was
18260      * started
18261      * @property startPageY
18262      * @type int
18263      * @private
18264      */
18265     startPageY: 0,
18266
18267     /**
18268      * The group defines a logical collection of DragDrop objects that are
18269      * related.  Instances only get events when interacting with other
18270      * DragDrop object in the same group.  This lets us define multiple
18271      * groups using a single DragDrop subclass if we want.
18272      * @property groups
18273      * @type {string: string}
18274      */
18275     groups: null,
18276
18277     /**
18278      * Individual drag/drop instances can be locked.  This will prevent
18279      * onmousedown start drag.
18280      * @property locked
18281      * @type boolean
18282      * @private
18283      */
18284     locked: false,
18285
18286     /**
18287      * Lock this instance
18288      * @method lock
18289      */
18290     lock: function() { this.locked = true; },
18291
18292     /**
18293      * Unlock this instace
18294      * @method unlock
18295      */
18296     unlock: function() { this.locked = false; },
18297
18298     /**
18299      * By default, all insances can be a drop target.  This can be disabled by
18300      * setting isTarget to false.
18301      * @method isTarget
18302      * @type boolean
18303      */
18304     isTarget: true,
18305
18306     /**
18307      * The padding configured for this drag and drop object for calculating
18308      * the drop zone intersection with this object.
18309      * @method padding
18310      * @type int[]
18311      */
18312     padding: null,
18313
18314     /**
18315      * Cached reference to the linked element
18316      * @property _domRef
18317      * @private
18318      */
18319     _domRef: null,
18320
18321     /**
18322      * Internal typeof flag
18323      * @property __ygDragDrop
18324      * @private
18325      */
18326     __ygDragDrop: true,
18327
18328     /**
18329      * Set to true when horizontal contraints are applied
18330      * @property constrainX
18331      * @type boolean
18332      * @private
18333      */
18334     constrainX: false,
18335
18336     /**
18337      * Set to true when vertical contraints are applied
18338      * @property constrainY
18339      * @type boolean
18340      * @private
18341      */
18342     constrainY: false,
18343
18344     /**
18345      * The left constraint
18346      * @property minX
18347      * @type int
18348      * @private
18349      */
18350     minX: 0,
18351
18352     /**
18353      * The right constraint
18354      * @property maxX
18355      * @type int
18356      * @private
18357      */
18358     maxX: 0,
18359
18360     /**
18361      * The up constraint
18362      * @property minY
18363      * @type int
18364      * @type int
18365      * @private
18366      */
18367     minY: 0,
18368
18369     /**
18370      * The down constraint
18371      * @property maxY
18372      * @type int
18373      * @private
18374      */
18375     maxY: 0,
18376
18377     /**
18378      * Maintain offsets when we resetconstraints.  Set to true when you want
18379      * the position of the element relative to its parent to stay the same
18380      * when the page changes
18381      *
18382      * @property maintainOffset
18383      * @type boolean
18384      */
18385     maintainOffset: false,
18386
18387     /**
18388      * Array of pixel locations the element will snap to if we specified a
18389      * horizontal graduation/interval.  This array is generated automatically
18390      * when you define a tick interval.
18391      * @property xTicks
18392      * @type int[]
18393      */
18394     xTicks: null,
18395
18396     /**
18397      * Array of pixel locations the element will snap to if we specified a
18398      * vertical graduation/interval.  This array is generated automatically
18399      * when you define a tick interval.
18400      * @property yTicks
18401      * @type int[]
18402      */
18403     yTicks: null,
18404
18405     /**
18406      * By default the drag and drop instance will only respond to the primary
18407      * button click (left button for a right-handed mouse).  Set to true to
18408      * allow drag and drop to start with any mouse click that is propogated
18409      * by the browser
18410      * @property primaryButtonOnly
18411      * @type boolean
18412      */
18413     primaryButtonOnly: true,
18414
18415     /**
18416      * The availabe property is false until the linked dom element is accessible.
18417      * @property available
18418      * @type boolean
18419      */
18420     available: false,
18421
18422     /**
18423      * By default, drags can only be initiated if the mousedown occurs in the
18424      * region the linked element is.  This is done in part to work around a
18425      * bug in some browsers that mis-report the mousedown if the previous
18426      * mouseup happened outside of the window.  This property is set to true
18427      * if outer handles are defined.
18428      *
18429      * @property hasOuterHandles
18430      * @type boolean
18431      * @default false
18432      */
18433     hasOuterHandles: false,
18434
18435     /**
18436      * Code that executes immediately before the startDrag event
18437      * @method b4StartDrag
18438      * @private
18439      */
18440     b4StartDrag: function(x, y) { },
18441
18442     /**
18443      * Abstract method called after a drag/drop object is clicked
18444      * and the drag or mousedown time thresholds have beeen met.
18445      * @method startDrag
18446      * @param {int} X click location
18447      * @param {int} Y click location
18448      */
18449     startDrag: function(x, y) { /* override this */ },
18450
18451     /**
18452      * Code that executes immediately before the onDrag event
18453      * @method b4Drag
18454      * @private
18455      */
18456     b4Drag: function(e) { },
18457
18458     /**
18459      * Abstract method called during the onMouseMove event while dragging an
18460      * object.
18461      * @method onDrag
18462      * @param {Event} e the mousemove event
18463      */
18464     onDrag: function(e) { /* override this */ },
18465
18466     /**
18467      * Abstract method called when this element fist begins hovering over
18468      * another DragDrop obj
18469      * @method onDragEnter
18470      * @param {Event} e the mousemove event
18471      * @param {String|DragDrop[]} id In POINT mode, the element
18472      * id this is hovering over.  In INTERSECT mode, an array of one or more
18473      * dragdrop items being hovered over.
18474      */
18475     onDragEnter: function(e, id) { /* override this */ },
18476
18477     /**
18478      * Code that executes immediately before the onDragOver event
18479      * @method b4DragOver
18480      * @private
18481      */
18482     b4DragOver: function(e) { },
18483
18484     /**
18485      * Abstract method called when this element is hovering over another
18486      * DragDrop obj
18487      * @method onDragOver
18488      * @param {Event} e the mousemove event
18489      * @param {String|DragDrop[]} id In POINT mode, the element
18490      * id this is hovering over.  In INTERSECT mode, an array of dd items
18491      * being hovered over.
18492      */
18493     onDragOver: function(e, id) { /* override this */ },
18494
18495     /**
18496      * Code that executes immediately before the onDragOut event
18497      * @method b4DragOut
18498      * @private
18499      */
18500     b4DragOut: function(e) { },
18501
18502     /**
18503      * Abstract method called when we are no longer hovering over an element
18504      * @method onDragOut
18505      * @param {Event} e the mousemove event
18506      * @param {String|DragDrop[]} id In POINT mode, the element
18507      * id this was hovering over.  In INTERSECT mode, an array of dd items
18508      * that the mouse is no longer over.
18509      */
18510     onDragOut: function(e, id) { /* override this */ },
18511
18512     /**
18513      * Code that executes immediately before the onDragDrop event
18514      * @method b4DragDrop
18515      * @private
18516      */
18517     b4DragDrop: function(e) { },
18518
18519     /**
18520      * Abstract method called when this item is dropped on another DragDrop
18521      * obj
18522      * @method onDragDrop
18523      * @param {Event} e the mouseup event
18524      * @param {String|DragDrop[]} id In POINT mode, the element
18525      * id this was dropped on.  In INTERSECT mode, an array of dd items this
18526      * was dropped on.
18527      */
18528     onDragDrop: function(e, id) { /* override this */ },
18529
18530     /**
18531      * Abstract method called when this item is dropped on an area with no
18532      * drop target
18533      * @method onInvalidDrop
18534      * @param {Event} e the mouseup event
18535      */
18536     onInvalidDrop: function(e) { /* override this */ },
18537
18538     /**
18539      * Code that executes immediately before the endDrag event
18540      * @method b4EndDrag
18541      * @private
18542      */
18543     b4EndDrag: function(e) { },
18544
18545     /**
18546      * Fired when we are done dragging the object
18547      * @method endDrag
18548      * @param {Event} e the mouseup event
18549      */
18550     endDrag: function(e) { /* override this */ },
18551
18552     /**
18553      * Code executed immediately before the onMouseDown event
18554      * @method b4MouseDown
18555      * @param {Event} e the mousedown event
18556      * @private
18557      */
18558     b4MouseDown: function(e) {  },
18559
18560     /**
18561      * Event handler that fires when a drag/drop obj gets a mousedown
18562      * @method onMouseDown
18563      * @param {Event} e the mousedown event
18564      */
18565     onMouseDown: function(e) { /* override this */ },
18566
18567     /**
18568      * Event handler that fires when a drag/drop obj gets a mouseup
18569      * @method onMouseUp
18570      * @param {Event} e the mouseup event
18571      */
18572     onMouseUp: function(e) { /* override this */ },
18573
18574     /**
18575      * Override the onAvailable method to do what is needed after the initial
18576      * position was determined.
18577      * @method onAvailable
18578      */
18579     onAvailable: function () {
18580     },
18581
18582     /*
18583      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
18584      * @type Object
18585      */
18586     defaultPadding : {left:0, right:0, top:0, bottom:0},
18587
18588     /*
18589      * Initializes the drag drop object's constraints to restrict movement to a certain element.
18590  *
18591  * Usage:
18592  <pre><code>
18593  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
18594                 { dragElId: "existingProxyDiv" });
18595  dd.startDrag = function(){
18596      this.constrainTo("parent-id");
18597  };
18598  </code></pre>
18599  * Or you can initalize it using the {@link Roo.Element} object:
18600  <pre><code>
18601  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
18602      startDrag : function(){
18603          this.constrainTo("parent-id");
18604      }
18605  });
18606  </code></pre>
18607      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
18608      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
18609      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
18610      * an object containing the sides to pad. For example: {right:10, bottom:10}
18611      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
18612      */
18613     constrainTo : function(constrainTo, pad, inContent){
18614         if(typeof pad == "number"){
18615             pad = {left: pad, right:pad, top:pad, bottom:pad};
18616         }
18617         pad = pad || this.defaultPadding;
18618         var b = Roo.get(this.getEl()).getBox();
18619         var ce = Roo.get(constrainTo);
18620         var s = ce.getScroll();
18621         var c, cd = ce.dom;
18622         if(cd == document.body){
18623             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
18624         }else{
18625             xy = ce.getXY();
18626             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
18627         }
18628
18629
18630         var topSpace = b.y - c.y;
18631         var leftSpace = b.x - c.x;
18632
18633         this.resetConstraints();
18634         this.setXConstraint(leftSpace - (pad.left||0), // left
18635                 c.width - leftSpace - b.width - (pad.right||0) //right
18636         );
18637         this.setYConstraint(topSpace - (pad.top||0), //top
18638                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
18639         );
18640     },
18641
18642     /**
18643      * Returns a reference to the linked element
18644      * @method getEl
18645      * @return {HTMLElement} the html element
18646      */
18647     getEl: function() {
18648         if (!this._domRef) {
18649             this._domRef = Roo.getDom(this.id);
18650         }
18651
18652         return this._domRef;
18653     },
18654
18655     /**
18656      * Returns a reference to the actual element to drag.  By default this is
18657      * the same as the html element, but it can be assigned to another
18658      * element. An example of this can be found in Roo.dd.DDProxy
18659      * @method getDragEl
18660      * @return {HTMLElement} the html element
18661      */
18662     getDragEl: function() {
18663         return Roo.getDom(this.dragElId);
18664     },
18665
18666     /**
18667      * Sets up the DragDrop object.  Must be called in the constructor of any
18668      * Roo.dd.DragDrop subclass
18669      * @method init
18670      * @param id the id of the linked element
18671      * @param {String} sGroup the group of related items
18672      * @param {object} config configuration attributes
18673      */
18674     init: function(id, sGroup, config) {
18675         this.initTarget(id, sGroup, config);
18676         if (!Roo.isTouch) {
18677             Event.on(this.id, "mousedown", this.handleMouseDown, this);
18678         }
18679         Event.on(this.id, "touchstart", this.handleMouseDown, this);
18680         // Event.on(this.id, "selectstart", Event.preventDefault);
18681     },
18682
18683     /**
18684      * Initializes Targeting functionality only... the object does not
18685      * get a mousedown handler.
18686      * @method initTarget
18687      * @param id the id of the linked element
18688      * @param {String} sGroup the group of related items
18689      * @param {object} config configuration attributes
18690      */
18691     initTarget: function(id, sGroup, config) {
18692
18693         // configuration attributes
18694         this.config = config || {};
18695
18696         // create a local reference to the drag and drop manager
18697         this.DDM = Roo.dd.DDM;
18698         // initialize the groups array
18699         this.groups = {};
18700
18701         // assume that we have an element reference instead of an id if the
18702         // parameter is not a string
18703         if (typeof id !== "string") {
18704             id = Roo.id(id);
18705         }
18706
18707         // set the id
18708         this.id = id;
18709
18710         // add to an interaction group
18711         this.addToGroup((sGroup) ? sGroup : "default");
18712
18713         // We don't want to register this as the handle with the manager
18714         // so we just set the id rather than calling the setter.
18715         this.handleElId = id;
18716
18717         // the linked element is the element that gets dragged by default
18718         this.setDragElId(id);
18719
18720         // by default, clicked anchors will not start drag operations.
18721         this.invalidHandleTypes = { A: "A" };
18722         this.invalidHandleIds = {};
18723         this.invalidHandleClasses = [];
18724
18725         this.applyConfig();
18726
18727         this.handleOnAvailable();
18728     },
18729
18730     /**
18731      * Applies the configuration parameters that were passed into the constructor.
18732      * This is supposed to happen at each level through the inheritance chain.  So
18733      * a DDProxy implentation will execute apply config on DDProxy, DD, and
18734      * DragDrop in order to get all of the parameters that are available in
18735      * each object.
18736      * @method applyConfig
18737      */
18738     applyConfig: function() {
18739
18740         // configurable properties:
18741         //    padding, isTarget, maintainOffset, primaryButtonOnly
18742         this.padding           = this.config.padding || [0, 0, 0, 0];
18743         this.isTarget          = (this.config.isTarget !== false);
18744         this.maintainOffset    = (this.config.maintainOffset);
18745         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
18746
18747     },
18748
18749     /**
18750      * Executed when the linked element is available
18751      * @method handleOnAvailable
18752      * @private
18753      */
18754     handleOnAvailable: function() {
18755         this.available = true;
18756         this.resetConstraints();
18757         this.onAvailable();
18758     },
18759
18760      /**
18761      * Configures the padding for the target zone in px.  Effectively expands
18762      * (or reduces) the virtual object size for targeting calculations.
18763      * Supports css-style shorthand; if only one parameter is passed, all sides
18764      * will have that padding, and if only two are passed, the top and bottom
18765      * will have the first param, the left and right the second.
18766      * @method setPadding
18767      * @param {int} iTop    Top pad
18768      * @param {int} iRight  Right pad
18769      * @param {int} iBot    Bot pad
18770      * @param {int} iLeft   Left pad
18771      */
18772     setPadding: function(iTop, iRight, iBot, iLeft) {
18773         // this.padding = [iLeft, iRight, iTop, iBot];
18774         if (!iRight && 0 !== iRight) {
18775             this.padding = [iTop, iTop, iTop, iTop];
18776         } else if (!iBot && 0 !== iBot) {
18777             this.padding = [iTop, iRight, iTop, iRight];
18778         } else {
18779             this.padding = [iTop, iRight, iBot, iLeft];
18780         }
18781     },
18782
18783     /**
18784      * Stores the initial placement of the linked element.
18785      * @method setInitialPosition
18786      * @param {int} diffX   the X offset, default 0
18787      * @param {int} diffY   the Y offset, default 0
18788      */
18789     setInitPosition: function(diffX, diffY) {
18790         var el = this.getEl();
18791
18792         if (!this.DDM.verifyEl(el)) {
18793             return;
18794         }
18795
18796         var dx = diffX || 0;
18797         var dy = diffY || 0;
18798
18799         var p = Dom.getXY( el );
18800
18801         this.initPageX = p[0] - dx;
18802         this.initPageY = p[1] - dy;
18803
18804         this.lastPageX = p[0];
18805         this.lastPageY = p[1];
18806
18807
18808         this.setStartPosition(p);
18809     },
18810
18811     /**
18812      * Sets the start position of the element.  This is set when the obj
18813      * is initialized, the reset when a drag is started.
18814      * @method setStartPosition
18815      * @param pos current position (from previous lookup)
18816      * @private
18817      */
18818     setStartPosition: function(pos) {
18819         var p = pos || Dom.getXY( this.getEl() );
18820         this.deltaSetXY = null;
18821
18822         this.startPageX = p[0];
18823         this.startPageY = p[1];
18824     },
18825
18826     /**
18827      * Add this instance to a group of related drag/drop objects.  All
18828      * instances belong to at least one group, and can belong to as many
18829      * groups as needed.
18830      * @method addToGroup
18831      * @param sGroup {string} the name of the group
18832      */
18833     addToGroup: function(sGroup) {
18834         this.groups[sGroup] = true;
18835         this.DDM.regDragDrop(this, sGroup);
18836     },
18837
18838     /**
18839      * Remove's this instance from the supplied interaction group
18840      * @method removeFromGroup
18841      * @param {string}  sGroup  The group to drop
18842      */
18843     removeFromGroup: function(sGroup) {
18844         if (this.groups[sGroup]) {
18845             delete this.groups[sGroup];
18846         }
18847
18848         this.DDM.removeDDFromGroup(this, sGroup);
18849     },
18850
18851     /**
18852      * Allows you to specify that an element other than the linked element
18853      * will be moved with the cursor during a drag
18854      * @method setDragElId
18855      * @param id {string} the id of the element that will be used to initiate the drag
18856      */
18857     setDragElId: function(id) {
18858         this.dragElId = id;
18859     },
18860
18861     /**
18862      * Allows you to specify a child of the linked element that should be
18863      * used to initiate the drag operation.  An example of this would be if
18864      * you have a content div with text and links.  Clicking anywhere in the
18865      * content area would normally start the drag operation.  Use this method
18866      * to specify that an element inside of the content div is the element
18867      * that starts the drag operation.
18868      * @method setHandleElId
18869      * @param id {string} the id of the element that will be used to
18870      * initiate the drag.
18871      */
18872     setHandleElId: function(id) {
18873         if (typeof id !== "string") {
18874             id = Roo.id(id);
18875         }
18876         this.handleElId = id;
18877         this.DDM.regHandle(this.id, id);
18878     },
18879
18880     /**
18881      * Allows you to set an element outside of the linked element as a drag
18882      * handle
18883      * @method setOuterHandleElId
18884      * @param id the id of the element that will be used to initiate the drag
18885      */
18886     setOuterHandleElId: function(id) {
18887         if (typeof id !== "string") {
18888             id = Roo.id(id);
18889         }
18890         Event.on(id, "mousedown",
18891                 this.handleMouseDown, this);
18892         this.setHandleElId(id);
18893
18894         this.hasOuterHandles = true;
18895     },
18896
18897     /**
18898      * Remove all drag and drop hooks for this element
18899      * @method unreg
18900      */
18901     unreg: function() {
18902         Event.un(this.id, "mousedown",
18903                 this.handleMouseDown);
18904         Event.un(this.id, "touchstart",
18905                 this.handleMouseDown);
18906         this._domRef = null;
18907         this.DDM._remove(this);
18908     },
18909
18910     destroy : function(){
18911         this.unreg();
18912     },
18913
18914     /**
18915      * Returns true if this instance is locked, or the drag drop mgr is locked
18916      * (meaning that all drag/drop is disabled on the page.)
18917      * @method isLocked
18918      * @return {boolean} true if this obj or all drag/drop is locked, else
18919      * false
18920      */
18921     isLocked: function() {
18922         return (this.DDM.isLocked() || this.locked);
18923     },
18924
18925     /**
18926      * Fired when this object is clicked
18927      * @method handleMouseDown
18928      * @param {Event} e
18929      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
18930      * @private
18931      */
18932     handleMouseDown: function(e, oDD){
18933      
18934         if (!Roo.isTouch && this.primaryButtonOnly && e.button != 0) {
18935             //Roo.log('not touch/ button !=0');
18936             return;
18937         }
18938         if (e.browserEvent.touches && e.browserEvent.touches.length != 1) {
18939             return; // double touch..
18940         }
18941         
18942
18943         if (this.isLocked()) {
18944             //Roo.log('locked');
18945             return;
18946         }
18947
18948         this.DDM.refreshCache(this.groups);
18949 //        Roo.log([Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e)]);
18950         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
18951         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
18952             //Roo.log('no outer handes or not over target');
18953                 // do nothing.
18954         } else {
18955 //            Roo.log('check validator');
18956             if (this.clickValidator(e)) {
18957 //                Roo.log('validate success');
18958                 // set the initial element position
18959                 this.setStartPosition();
18960
18961
18962                 this.b4MouseDown(e);
18963                 this.onMouseDown(e);
18964
18965                 this.DDM.handleMouseDown(e, this);
18966
18967                 this.DDM.stopEvent(e);
18968             } else {
18969
18970
18971             }
18972         }
18973     },
18974
18975     clickValidator: function(e) {
18976         var target = e.getTarget();
18977         return ( this.isValidHandleChild(target) &&
18978                     (this.id == this.handleElId ||
18979                         this.DDM.handleWasClicked(target, this.id)) );
18980     },
18981
18982     /**
18983      * Allows you to specify a tag name that should not start a drag operation
18984      * when clicked.  This is designed to facilitate embedding links within a
18985      * drag handle that do something other than start the drag.
18986      * @method addInvalidHandleType
18987      * @param {string} tagName the type of element to exclude
18988      */
18989     addInvalidHandleType: function(tagName) {
18990         var type = tagName.toUpperCase();
18991         this.invalidHandleTypes[type] = type;
18992     },
18993
18994     /**
18995      * Lets you to specify an element id for a child of a drag handle
18996      * that should not initiate a drag
18997      * @method addInvalidHandleId
18998      * @param {string} id the element id of the element you wish to ignore
18999      */
19000     addInvalidHandleId: function(id) {
19001         if (typeof id !== "string") {
19002             id = Roo.id(id);
19003         }
19004         this.invalidHandleIds[id] = id;
19005     },
19006
19007     /**
19008      * Lets you specify a css class of elements that will not initiate a drag
19009      * @method addInvalidHandleClass
19010      * @param {string} cssClass the class of the elements you wish to ignore
19011      */
19012     addInvalidHandleClass: function(cssClass) {
19013         this.invalidHandleClasses.push(cssClass);
19014     },
19015
19016     /**
19017      * Unsets an excluded tag name set by addInvalidHandleType
19018      * @method removeInvalidHandleType
19019      * @param {string} tagName the type of element to unexclude
19020      */
19021     removeInvalidHandleType: function(tagName) {
19022         var type = tagName.toUpperCase();
19023         // this.invalidHandleTypes[type] = null;
19024         delete this.invalidHandleTypes[type];
19025     },
19026
19027     /**
19028      * Unsets an invalid handle id
19029      * @method removeInvalidHandleId
19030      * @param {string} id the id of the element to re-enable
19031      */
19032     removeInvalidHandleId: function(id) {
19033         if (typeof id !== "string") {
19034             id = Roo.id(id);
19035         }
19036         delete this.invalidHandleIds[id];
19037     },
19038
19039     /**
19040      * Unsets an invalid css class
19041      * @method removeInvalidHandleClass
19042      * @param {string} cssClass the class of the element(s) you wish to
19043      * re-enable
19044      */
19045     removeInvalidHandleClass: function(cssClass) {
19046         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
19047             if (this.invalidHandleClasses[i] == cssClass) {
19048                 delete this.invalidHandleClasses[i];
19049             }
19050         }
19051     },
19052
19053     /**
19054      * Checks the tag exclusion list to see if this click should be ignored
19055      * @method isValidHandleChild
19056      * @param {HTMLElement} node the HTMLElement to evaluate
19057      * @return {boolean} true if this is a valid tag type, false if not
19058      */
19059     isValidHandleChild: function(node) {
19060
19061         var valid = true;
19062         // var n = (node.nodeName == "#text") ? node.parentNode : node;
19063         var nodeName;
19064         try {
19065             nodeName = node.nodeName.toUpperCase();
19066         } catch(e) {
19067             nodeName = node.nodeName;
19068         }
19069         valid = valid && !this.invalidHandleTypes[nodeName];
19070         valid = valid && !this.invalidHandleIds[node.id];
19071
19072         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
19073             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
19074         }
19075
19076
19077         return valid;
19078
19079     },
19080
19081     /**
19082      * Create the array of horizontal tick marks if an interval was specified
19083      * in setXConstraint().
19084      * @method setXTicks
19085      * @private
19086      */
19087     setXTicks: function(iStartX, iTickSize) {
19088         this.xTicks = [];
19089         this.xTickSize = iTickSize;
19090
19091         var tickMap = {};
19092
19093         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
19094             if (!tickMap[i]) {
19095                 this.xTicks[this.xTicks.length] = i;
19096                 tickMap[i] = true;
19097             }
19098         }
19099
19100         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
19101             if (!tickMap[i]) {
19102                 this.xTicks[this.xTicks.length] = i;
19103                 tickMap[i] = true;
19104             }
19105         }
19106
19107         this.xTicks.sort(this.DDM.numericSort) ;
19108     },
19109
19110     /**
19111      * Create the array of vertical tick marks if an interval was specified in
19112      * setYConstraint().
19113      * @method setYTicks
19114      * @private
19115      */
19116     setYTicks: function(iStartY, iTickSize) {
19117         this.yTicks = [];
19118         this.yTickSize = iTickSize;
19119
19120         var tickMap = {};
19121
19122         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
19123             if (!tickMap[i]) {
19124                 this.yTicks[this.yTicks.length] = i;
19125                 tickMap[i] = true;
19126             }
19127         }
19128
19129         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
19130             if (!tickMap[i]) {
19131                 this.yTicks[this.yTicks.length] = i;
19132                 tickMap[i] = true;
19133             }
19134         }
19135
19136         this.yTicks.sort(this.DDM.numericSort) ;
19137     },
19138
19139     /**
19140      * By default, the element can be dragged any place on the screen.  Use
19141      * this method to limit the horizontal travel of the element.  Pass in
19142      * 0,0 for the parameters if you want to lock the drag to the y axis.
19143      * @method setXConstraint
19144      * @param {int} iLeft the number of pixels the element can move to the left
19145      * @param {int} iRight the number of pixels the element can move to the
19146      * right
19147      * @param {int} iTickSize optional parameter for specifying that the
19148      * element
19149      * should move iTickSize pixels at a time.
19150      */
19151     setXConstraint: function(iLeft, iRight, iTickSize) {
19152         this.leftConstraint = iLeft;
19153         this.rightConstraint = iRight;
19154
19155         this.minX = this.initPageX - iLeft;
19156         this.maxX = this.initPageX + iRight;
19157         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
19158
19159         this.constrainX = true;
19160     },
19161
19162     /**
19163      * Clears any constraints applied to this instance.  Also clears ticks
19164      * since they can't exist independent of a constraint at this time.
19165      * @method clearConstraints
19166      */
19167     clearConstraints: function() {
19168         this.constrainX = false;
19169         this.constrainY = false;
19170         this.clearTicks();
19171     },
19172
19173     /**
19174      * Clears any tick interval defined for this instance
19175      * @method clearTicks
19176      */
19177     clearTicks: function() {
19178         this.xTicks = null;
19179         this.yTicks = null;
19180         this.xTickSize = 0;
19181         this.yTickSize = 0;
19182     },
19183
19184     /**
19185      * By default, the element can be dragged any place on the screen.  Set
19186      * this to limit the vertical travel of the element.  Pass in 0,0 for the
19187      * parameters if you want to lock the drag to the x axis.
19188      * @method setYConstraint
19189      * @param {int} iUp the number of pixels the element can move up
19190      * @param {int} iDown the number of pixels the element can move down
19191      * @param {int} iTickSize optional parameter for specifying that the
19192      * element should move iTickSize pixels at a time.
19193      */
19194     setYConstraint: function(iUp, iDown, iTickSize) {
19195         this.topConstraint = iUp;
19196         this.bottomConstraint = iDown;
19197
19198         this.minY = this.initPageY - iUp;
19199         this.maxY = this.initPageY + iDown;
19200         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
19201
19202         this.constrainY = true;
19203
19204     },
19205
19206     /**
19207      * resetConstraints must be called if you manually reposition a dd element.
19208      * @method resetConstraints
19209      * @param {boolean} maintainOffset
19210      */
19211     resetConstraints: function() {
19212
19213
19214         // Maintain offsets if necessary
19215         if (this.initPageX || this.initPageX === 0) {
19216             // figure out how much this thing has moved
19217             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
19218             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
19219
19220             this.setInitPosition(dx, dy);
19221
19222         // This is the first time we have detected the element's position
19223         } else {
19224             this.setInitPosition();
19225         }
19226
19227         if (this.constrainX) {
19228             this.setXConstraint( this.leftConstraint,
19229                                  this.rightConstraint,
19230                                  this.xTickSize        );
19231         }
19232
19233         if (this.constrainY) {
19234             this.setYConstraint( this.topConstraint,
19235                                  this.bottomConstraint,
19236                                  this.yTickSize         );
19237         }
19238     },
19239
19240     /**
19241      * Normally the drag element is moved pixel by pixel, but we can specify
19242      * that it move a number of pixels at a time.  This method resolves the
19243      * location when we have it set up like this.
19244      * @method getTick
19245      * @param {int} val where we want to place the object
19246      * @param {int[]} tickArray sorted array of valid points
19247      * @return {int} the closest tick
19248      * @private
19249      */
19250     getTick: function(val, tickArray) {
19251
19252         if (!tickArray) {
19253             // If tick interval is not defined, it is effectively 1 pixel,
19254             // so we return the value passed to us.
19255             return val;
19256         } else if (tickArray[0] >= val) {
19257             // The value is lower than the first tick, so we return the first
19258             // tick.
19259             return tickArray[0];
19260         } else {
19261             for (var i=0, len=tickArray.length; i<len; ++i) {
19262                 var next = i + 1;
19263                 if (tickArray[next] && tickArray[next] >= val) {
19264                     var diff1 = val - tickArray[i];
19265                     var diff2 = tickArray[next] - val;
19266                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
19267                 }
19268             }
19269
19270             // The value is larger than the last tick, so we return the last
19271             // tick.
19272             return tickArray[tickArray.length - 1];
19273         }
19274     },
19275
19276     /**
19277      * toString method
19278      * @method toString
19279      * @return {string} string representation of the dd obj
19280      */
19281     toString: function() {
19282         return ("DragDrop " + this.id);
19283     }
19284
19285 });
19286
19287 })();
19288 /*
19289  * Based on:
19290  * Ext JS Library 1.1.1
19291  * Copyright(c) 2006-2007, Ext JS, LLC.
19292  *
19293  * Originally Released Under LGPL - original licence link has changed is not relivant.
19294  *
19295  * Fork - LGPL
19296  * <script type="text/javascript">
19297  */
19298
19299
19300 /**
19301  * The drag and drop utility provides a framework for building drag and drop
19302  * applications.  In addition to enabling drag and drop for specific elements,
19303  * the drag and drop elements are tracked by the manager class, and the
19304  * interactions between the various elements are tracked during the drag and
19305  * the implementing code is notified about these important moments.
19306  */
19307
19308 // Only load the library once.  Rewriting the manager class would orphan
19309 // existing drag and drop instances.
19310 if (!Roo.dd.DragDropMgr) {
19311
19312 /**
19313  * @class Roo.dd.DragDropMgr
19314  * DragDropMgr is a singleton that tracks the element interaction for
19315  * all DragDrop items in the window.  Generally, you will not call
19316  * this class directly, but it does have helper methods that could
19317  * be useful in your DragDrop implementations.
19318  * @singleton
19319  */
19320 Roo.dd.DragDropMgr = function() {
19321
19322     var Event = Roo.EventManager;
19323
19324     return {
19325
19326         /**
19327          * Two dimensional Array of registered DragDrop objects.  The first
19328          * dimension is the DragDrop item group, the second the DragDrop
19329          * object.
19330          * @property ids
19331          * @type {string: string}
19332          * @private
19333          * @static
19334          */
19335         ids: {},
19336
19337         /**
19338          * Array of element ids defined as drag handles.  Used to determine
19339          * if the element that generated the mousedown event is actually the
19340          * handle and not the html element itself.
19341          * @property handleIds
19342          * @type {string: string}
19343          * @private
19344          * @static
19345          */
19346         handleIds: {},
19347
19348         /**
19349          * the DragDrop object that is currently being dragged
19350          * @property dragCurrent
19351          * @type DragDrop
19352          * @private
19353          * @static
19354          **/
19355         dragCurrent: null,
19356
19357         /**
19358          * the DragDrop object(s) that are being hovered over
19359          * @property dragOvers
19360          * @type Array
19361          * @private
19362          * @static
19363          */
19364         dragOvers: {},
19365
19366         /**
19367          * the X distance between the cursor and the object being dragged
19368          * @property deltaX
19369          * @type int
19370          * @private
19371          * @static
19372          */
19373         deltaX: 0,
19374
19375         /**
19376          * the Y distance between the cursor and the object being dragged
19377          * @property deltaY
19378          * @type int
19379          * @private
19380          * @static
19381          */
19382         deltaY: 0,
19383
19384         /**
19385          * Flag to determine if we should prevent the default behavior of the
19386          * events we define. By default this is true, but this can be set to
19387          * false if you need the default behavior (not recommended)
19388          * @property preventDefault
19389          * @type boolean
19390          * @static
19391          */
19392         preventDefault: true,
19393
19394         /**
19395          * Flag to determine if we should stop the propagation of the events
19396          * we generate. This is true by default but you may want to set it to
19397          * false if the html element contains other features that require the
19398          * mouse click.
19399          * @property stopPropagation
19400          * @type boolean
19401          * @static
19402          */
19403         stopPropagation: true,
19404
19405         /**
19406          * Internal flag that is set to true when drag and drop has been
19407          * intialized
19408          * @property initialized
19409          * @private
19410          * @static
19411          */
19412         initalized: false,
19413
19414         /**
19415          * All drag and drop can be disabled.
19416          * @property locked
19417          * @private
19418          * @static
19419          */
19420         locked: false,
19421
19422         /**
19423          * Called the first time an element is registered.
19424          * @method init
19425          * @private
19426          * @static
19427          */
19428         init: function() {
19429             this.initialized = true;
19430         },
19431
19432         /**
19433          * In point mode, drag and drop interaction is defined by the
19434          * location of the cursor during the drag/drop
19435          * @property POINT
19436          * @type int
19437          * @static
19438          */
19439         POINT: 0,
19440
19441         /**
19442          * In intersect mode, drag and drop interactio nis defined by the
19443          * overlap of two or more drag and drop objects.
19444          * @property INTERSECT
19445          * @type int
19446          * @static
19447          */
19448         INTERSECT: 1,
19449
19450         /**
19451          * The current drag and drop mode.  Default: POINT
19452          * @property mode
19453          * @type int
19454          * @static
19455          */
19456         mode: 0,
19457
19458         /**
19459          * Runs method on all drag and drop objects
19460          * @method _execOnAll
19461          * @private
19462          * @static
19463          */
19464         _execOnAll: function(sMethod, args) {
19465             for (var i in this.ids) {
19466                 for (var j in this.ids[i]) {
19467                     var oDD = this.ids[i][j];
19468                     if (! this.isTypeOfDD(oDD)) {
19469                         continue;
19470                     }
19471                     oDD[sMethod].apply(oDD, args);
19472                 }
19473             }
19474         },
19475
19476         /**
19477          * Drag and drop initialization.  Sets up the global event handlers
19478          * @method _onLoad
19479          * @private
19480          * @static
19481          */
19482         _onLoad: function() {
19483
19484             this.init();
19485
19486             if (!Roo.isTouch) {
19487                 Event.on(document, "mouseup",   this.handleMouseUp, this, true);
19488                 Event.on(document, "mousemove", this.handleMouseMove, this, true);
19489             }
19490             Event.on(document, "touchend",   this.handleMouseUp, this, true);
19491             Event.on(document, "touchmove", this.handleMouseMove, this, true);
19492             
19493             Event.on(window,   "unload",    this._onUnload, this, true);
19494             Event.on(window,   "resize",    this._onResize, this, true);
19495             // Event.on(window,   "mouseout",    this._test);
19496
19497         },
19498
19499         /**
19500          * Reset constraints on all drag and drop objs
19501          * @method _onResize
19502          * @private
19503          * @static
19504          */
19505         _onResize: function(e) {
19506             this._execOnAll("resetConstraints", []);
19507         },
19508
19509         /**
19510          * Lock all drag and drop functionality
19511          * @method lock
19512          * @static
19513          */
19514         lock: function() { this.locked = true; },
19515
19516         /**
19517          * Unlock all drag and drop functionality
19518          * @method unlock
19519          * @static
19520          */
19521         unlock: function() { this.locked = false; },
19522
19523         /**
19524          * Is drag and drop locked?
19525          * @method isLocked
19526          * @return {boolean} True if drag and drop is locked, false otherwise.
19527          * @static
19528          */
19529         isLocked: function() { return this.locked; },
19530
19531         /**
19532          * Location cache that is set for all drag drop objects when a drag is
19533          * initiated, cleared when the drag is finished.
19534          * @property locationCache
19535          * @private
19536          * @static
19537          */
19538         locationCache: {},
19539
19540         /**
19541          * Set useCache to false if you want to force object the lookup of each
19542          * drag and drop linked element constantly during a drag.
19543          * @property useCache
19544          * @type boolean
19545          * @static
19546          */
19547         useCache: true,
19548
19549         /**
19550          * The number of pixels that the mouse needs to move after the
19551          * mousedown before the drag is initiated.  Default=3;
19552          * @property clickPixelThresh
19553          * @type int
19554          * @static
19555          */
19556         clickPixelThresh: 3,
19557
19558         /**
19559          * The number of milliseconds after the mousedown event to initiate the
19560          * drag if we don't get a mouseup event. Default=1000
19561          * @property clickTimeThresh
19562          * @type int
19563          * @static
19564          */
19565         clickTimeThresh: 350,
19566
19567         /**
19568          * Flag that indicates that either the drag pixel threshold or the
19569          * mousdown time threshold has been met
19570          * @property dragThreshMet
19571          * @type boolean
19572          * @private
19573          * @static
19574          */
19575         dragThreshMet: false,
19576
19577         /**
19578          * Timeout used for the click time threshold
19579          * @property clickTimeout
19580          * @type Object
19581          * @private
19582          * @static
19583          */
19584         clickTimeout: null,
19585
19586         /**
19587          * The X position of the mousedown event stored for later use when a
19588          * drag threshold is met.
19589          * @property startX
19590          * @type int
19591          * @private
19592          * @static
19593          */
19594         startX: 0,
19595
19596         /**
19597          * The Y position of the mousedown event stored for later use when a
19598          * drag threshold is met.
19599          * @property startY
19600          * @type int
19601          * @private
19602          * @static
19603          */
19604         startY: 0,
19605
19606         /**
19607          * Each DragDrop instance must be registered with the DragDropMgr.
19608          * This is executed in DragDrop.init()
19609          * @method regDragDrop
19610          * @param {DragDrop} oDD the DragDrop object to register
19611          * @param {String} sGroup the name of the group this element belongs to
19612          * @static
19613          */
19614         regDragDrop: function(oDD, sGroup) {
19615             if (!this.initialized) { this.init(); }
19616
19617             if (!this.ids[sGroup]) {
19618                 this.ids[sGroup] = {};
19619             }
19620             this.ids[sGroup][oDD.id] = oDD;
19621         },
19622
19623         /**
19624          * Removes the supplied dd instance from the supplied group. Executed
19625          * by DragDrop.removeFromGroup, so don't call this function directly.
19626          * @method removeDDFromGroup
19627          * @private
19628          * @static
19629          */
19630         removeDDFromGroup: function(oDD, sGroup) {
19631             if (!this.ids[sGroup]) {
19632                 this.ids[sGroup] = {};
19633             }
19634
19635             var obj = this.ids[sGroup];
19636             if (obj && obj[oDD.id]) {
19637                 delete obj[oDD.id];
19638             }
19639         },
19640
19641         /**
19642          * Unregisters a drag and drop item.  This is executed in
19643          * DragDrop.unreg, use that method instead of calling this directly.
19644          * @method _remove
19645          * @private
19646          * @static
19647          */
19648         _remove: function(oDD) {
19649             for (var g in oDD.groups) {
19650                 if (g && this.ids[g][oDD.id]) {
19651                     delete this.ids[g][oDD.id];
19652                 }
19653             }
19654             delete this.handleIds[oDD.id];
19655         },
19656
19657         /**
19658          * Each DragDrop handle element must be registered.  This is done
19659          * automatically when executing DragDrop.setHandleElId()
19660          * @method regHandle
19661          * @param {String} sDDId the DragDrop id this element is a handle for
19662          * @param {String} sHandleId the id of the element that is the drag
19663          * handle
19664          * @static
19665          */
19666         regHandle: function(sDDId, sHandleId) {
19667             if (!this.handleIds[sDDId]) {
19668                 this.handleIds[sDDId] = {};
19669             }
19670             this.handleIds[sDDId][sHandleId] = sHandleId;
19671         },
19672
19673         /**
19674          * Utility function to determine if a given element has been
19675          * registered as a drag drop item.
19676          * @method isDragDrop
19677          * @param {String} id the element id to check
19678          * @return {boolean} true if this element is a DragDrop item,
19679          * false otherwise
19680          * @static
19681          */
19682         isDragDrop: function(id) {
19683             return ( this.getDDById(id) ) ? true : false;
19684         },
19685
19686         /**
19687          * Returns the drag and drop instances that are in all groups the
19688          * passed in instance belongs to.
19689          * @method getRelated
19690          * @param {DragDrop} p_oDD the obj to get related data for
19691          * @param {boolean} bTargetsOnly if true, only return targetable objs
19692          * @return {DragDrop[]} the related instances
19693          * @static
19694          */
19695         getRelated: function(p_oDD, bTargetsOnly) {
19696             var oDDs = [];
19697             for (var i in p_oDD.groups) {
19698                 for (j in this.ids[i]) {
19699                     var dd = this.ids[i][j];
19700                     if (! this.isTypeOfDD(dd)) {
19701                         continue;
19702                     }
19703                     if (!bTargetsOnly || dd.isTarget) {
19704                         oDDs[oDDs.length] = dd;
19705                     }
19706                 }
19707             }
19708
19709             return oDDs;
19710         },
19711
19712         /**
19713          * Returns true if the specified dd target is a legal target for
19714          * the specifice drag obj
19715          * @method isLegalTarget
19716          * @param {DragDrop} the drag obj
19717          * @param {DragDrop} the target
19718          * @return {boolean} true if the target is a legal target for the
19719          * dd obj
19720          * @static
19721          */
19722         isLegalTarget: function (oDD, oTargetDD) {
19723             var targets = this.getRelated(oDD, true);
19724             for (var i=0, len=targets.length;i<len;++i) {
19725                 if (targets[i].id == oTargetDD.id) {
19726                     return true;
19727                 }
19728             }
19729
19730             return false;
19731         },
19732
19733         /**
19734          * My goal is to be able to transparently determine if an object is
19735          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
19736          * returns "object", oDD.constructor.toString() always returns
19737          * "DragDrop" and not the name of the subclass.  So for now it just
19738          * evaluates a well-known variable in DragDrop.
19739          * @method isTypeOfDD
19740          * @param {Object} the object to evaluate
19741          * @return {boolean} true if typeof oDD = DragDrop
19742          * @static
19743          */
19744         isTypeOfDD: function (oDD) {
19745             return (oDD && oDD.__ygDragDrop);
19746         },
19747
19748         /**
19749          * Utility function to determine if a given element has been
19750          * registered as a drag drop handle for the given Drag Drop object.
19751          * @method isHandle
19752          * @param {String} id the element id to check
19753          * @return {boolean} true if this element is a DragDrop handle, false
19754          * otherwise
19755          * @static
19756          */
19757         isHandle: function(sDDId, sHandleId) {
19758             return ( this.handleIds[sDDId] &&
19759                             this.handleIds[sDDId][sHandleId] );
19760         },
19761
19762         /**
19763          * Returns the DragDrop instance for a given id
19764          * @method getDDById
19765          * @param {String} id the id of the DragDrop object
19766          * @return {DragDrop} the drag drop object, null if it is not found
19767          * @static
19768          */
19769         getDDById: function(id) {
19770             for (var i in this.ids) {
19771                 if (this.ids[i][id]) {
19772                     return this.ids[i][id];
19773                 }
19774             }
19775             return null;
19776         },
19777
19778         /**
19779          * Fired after a registered DragDrop object gets the mousedown event.
19780          * Sets up the events required to track the object being dragged
19781          * @method handleMouseDown
19782          * @param {Event} e the event
19783          * @param oDD the DragDrop object being dragged
19784          * @private
19785          * @static
19786          */
19787         handleMouseDown: function(e, oDD) {
19788             if(Roo.QuickTips){
19789                 Roo.QuickTips.disable();
19790             }
19791             this.currentTarget = e.getTarget();
19792
19793             this.dragCurrent = oDD;
19794
19795             var el = oDD.getEl();
19796
19797             // track start position
19798             this.startX = e.getPageX();
19799             this.startY = e.getPageY();
19800
19801             this.deltaX = this.startX - el.offsetLeft;
19802             this.deltaY = this.startY - el.offsetTop;
19803
19804             this.dragThreshMet = false;
19805
19806             this.clickTimeout = setTimeout(
19807                     function() {
19808                         var DDM = Roo.dd.DDM;
19809                         DDM.startDrag(DDM.startX, DDM.startY);
19810                     },
19811                     this.clickTimeThresh );
19812         },
19813
19814         /**
19815          * Fired when either the drag pixel threshol or the mousedown hold
19816          * time threshold has been met.
19817          * @method startDrag
19818          * @param x {int} the X position of the original mousedown
19819          * @param y {int} the Y position of the original mousedown
19820          * @static
19821          */
19822         startDrag: function(x, y) {
19823             clearTimeout(this.clickTimeout);
19824             if (this.dragCurrent) {
19825                 this.dragCurrent.b4StartDrag(x, y);
19826                 this.dragCurrent.startDrag(x, y);
19827             }
19828             this.dragThreshMet = true;
19829         },
19830
19831         /**
19832          * Internal function to handle the mouseup event.  Will be invoked
19833          * from the context of the document.
19834          * @method handleMouseUp
19835          * @param {Event} e the event
19836          * @private
19837          * @static
19838          */
19839         handleMouseUp: function(e) {
19840
19841             if(Roo.QuickTips){
19842                 Roo.QuickTips.enable();
19843             }
19844             if (! this.dragCurrent) {
19845                 return;
19846             }
19847
19848             clearTimeout(this.clickTimeout);
19849
19850             if (this.dragThreshMet) {
19851                 this.fireEvents(e, true);
19852             } else {
19853             }
19854
19855             this.stopDrag(e);
19856
19857             this.stopEvent(e);
19858         },
19859
19860         /**
19861          * Utility to stop event propagation and event default, if these
19862          * features are turned on.
19863          * @method stopEvent
19864          * @param {Event} e the event as returned by this.getEvent()
19865          * @static
19866          */
19867         stopEvent: function(e){
19868             if(this.stopPropagation) {
19869                 e.stopPropagation();
19870             }
19871
19872             if (this.preventDefault) {
19873                 e.preventDefault();
19874             }
19875         },
19876
19877         /**
19878          * Internal function to clean up event handlers after the drag
19879          * operation is complete
19880          * @method stopDrag
19881          * @param {Event} e the event
19882          * @private
19883          * @static
19884          */
19885         stopDrag: function(e) {
19886             // Fire the drag end event for the item that was dragged
19887             if (this.dragCurrent) {
19888                 if (this.dragThreshMet) {
19889                     this.dragCurrent.b4EndDrag(e);
19890                     this.dragCurrent.endDrag(e);
19891                 }
19892
19893                 this.dragCurrent.onMouseUp(e);
19894             }
19895
19896             this.dragCurrent = null;
19897             this.dragOvers = {};
19898         },
19899
19900         /**
19901          * Internal function to handle the mousemove event.  Will be invoked
19902          * from the context of the html element.
19903          *
19904          * @TODO figure out what we can do about mouse events lost when the
19905          * user drags objects beyond the window boundary.  Currently we can
19906          * detect this in internet explorer by verifying that the mouse is
19907          * down during the mousemove event.  Firefox doesn't give us the
19908          * button state on the mousemove event.
19909          * @method handleMouseMove
19910          * @param {Event} e the event
19911          * @private
19912          * @static
19913          */
19914         handleMouseMove: function(e) {
19915             if (! this.dragCurrent) {
19916                 return true;
19917             }
19918
19919             // var button = e.which || e.button;
19920
19921             // check for IE mouseup outside of page boundary
19922             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
19923                 this.stopEvent(e);
19924                 return this.handleMouseUp(e);
19925             }
19926
19927             if (!this.dragThreshMet) {
19928                 var diffX = Math.abs(this.startX - e.getPageX());
19929                 var diffY = Math.abs(this.startY - e.getPageY());
19930                 if (diffX > this.clickPixelThresh ||
19931                             diffY > this.clickPixelThresh) {
19932                     this.startDrag(this.startX, this.startY);
19933                 }
19934             }
19935
19936             if (this.dragThreshMet) {
19937                 this.dragCurrent.b4Drag(e);
19938                 this.dragCurrent.onDrag(e);
19939                 if(!this.dragCurrent.moveOnly){
19940                     this.fireEvents(e, false);
19941                 }
19942             }
19943
19944             this.stopEvent(e);
19945
19946             return true;
19947         },
19948
19949         /**
19950          * Iterates over all of the DragDrop elements to find ones we are
19951          * hovering over or dropping on
19952          * @method fireEvents
19953          * @param {Event} e the event
19954          * @param {boolean} isDrop is this a drop op or a mouseover op?
19955          * @private
19956          * @static
19957          */
19958         fireEvents: function(e, isDrop) {
19959             var dc = this.dragCurrent;
19960
19961             // If the user did the mouse up outside of the window, we could
19962             // get here even though we have ended the drag.
19963             if (!dc || dc.isLocked()) {
19964                 return;
19965             }
19966
19967             var pt = e.getPoint();
19968
19969             // cache the previous dragOver array
19970             var oldOvers = [];
19971
19972             var outEvts   = [];
19973             var overEvts  = [];
19974             var dropEvts  = [];
19975             var enterEvts = [];
19976
19977             // Check to see if the object(s) we were hovering over is no longer
19978             // being hovered over so we can fire the onDragOut event
19979             for (var i in this.dragOvers) {
19980
19981                 var ddo = this.dragOvers[i];
19982
19983                 if (! this.isTypeOfDD(ddo)) {
19984                     continue;
19985                 }
19986
19987                 if (! this.isOverTarget(pt, ddo, this.mode)) {
19988                     outEvts.push( ddo );
19989                 }
19990
19991                 oldOvers[i] = true;
19992                 delete this.dragOvers[i];
19993             }
19994
19995             for (var sGroup in dc.groups) {
19996
19997                 if ("string" != typeof sGroup) {
19998                     continue;
19999                 }
20000
20001                 for (i in this.ids[sGroup]) {
20002                     var oDD = this.ids[sGroup][i];
20003                     if (! this.isTypeOfDD(oDD)) {
20004                         continue;
20005                     }
20006
20007                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
20008                         if (this.isOverTarget(pt, oDD, this.mode)) {
20009                             // look for drop interactions
20010                             if (isDrop) {
20011                                 dropEvts.push( oDD );
20012                             // look for drag enter and drag over interactions
20013                             } else {
20014
20015                                 // initial drag over: dragEnter fires
20016                                 if (!oldOvers[oDD.id]) {
20017                                     enterEvts.push( oDD );
20018                                 // subsequent drag overs: dragOver fires
20019                                 } else {
20020                                     overEvts.push( oDD );
20021                                 }
20022
20023                                 this.dragOvers[oDD.id] = oDD;
20024                             }
20025                         }
20026                     }
20027                 }
20028             }
20029
20030             if (this.mode) {
20031                 if (outEvts.length) {
20032                     dc.b4DragOut(e, outEvts);
20033                     dc.onDragOut(e, outEvts);
20034                 }
20035
20036                 if (enterEvts.length) {
20037                     dc.onDragEnter(e, enterEvts);
20038                 }
20039
20040                 if (overEvts.length) {
20041                     dc.b4DragOver(e, overEvts);
20042                     dc.onDragOver(e, overEvts);
20043                 }
20044
20045                 if (dropEvts.length) {
20046                     dc.b4DragDrop(e, dropEvts);
20047                     dc.onDragDrop(e, dropEvts);
20048                 }
20049
20050             } else {
20051                 // fire dragout events
20052                 var len = 0;
20053                 for (i=0, len=outEvts.length; i<len; ++i) {
20054                     dc.b4DragOut(e, outEvts[i].id);
20055                     dc.onDragOut(e, outEvts[i].id);
20056                 }
20057
20058                 // fire enter events
20059                 for (i=0,len=enterEvts.length; i<len; ++i) {
20060                     // dc.b4DragEnter(e, oDD.id);
20061                     dc.onDragEnter(e, enterEvts[i].id);
20062                 }
20063
20064                 // fire over events
20065                 for (i=0,len=overEvts.length; i<len; ++i) {
20066                     dc.b4DragOver(e, overEvts[i].id);
20067                     dc.onDragOver(e, overEvts[i].id);
20068                 }
20069
20070                 // fire drop events
20071                 for (i=0, len=dropEvts.length; i<len; ++i) {
20072                     dc.b4DragDrop(e, dropEvts[i].id);
20073                     dc.onDragDrop(e, dropEvts[i].id);
20074                 }
20075
20076             }
20077
20078             // notify about a drop that did not find a target
20079             if (isDrop && !dropEvts.length) {
20080                 dc.onInvalidDrop(e);
20081             }
20082
20083         },
20084
20085         /**
20086          * Helper function for getting the best match from the list of drag
20087          * and drop objects returned by the drag and drop events when we are
20088          * in INTERSECT mode.  It returns either the first object that the
20089          * cursor is over, or the object that has the greatest overlap with
20090          * the dragged element.
20091          * @method getBestMatch
20092          * @param  {DragDrop[]} dds The array of drag and drop objects
20093          * targeted
20094          * @return {DragDrop}       The best single match
20095          * @static
20096          */
20097         getBestMatch: function(dds) {
20098             var winner = null;
20099             // Return null if the input is not what we expect
20100             //if (!dds || !dds.length || dds.length == 0) {
20101                // winner = null;
20102             // If there is only one item, it wins
20103             //} else if (dds.length == 1) {
20104
20105             var len = dds.length;
20106
20107             if (len == 1) {
20108                 winner = dds[0];
20109             } else {
20110                 // Loop through the targeted items
20111                 for (var i=0; i<len; ++i) {
20112                     var dd = dds[i];
20113                     // If the cursor is over the object, it wins.  If the
20114                     // cursor is over multiple matches, the first one we come
20115                     // to wins.
20116                     if (dd.cursorIsOver) {
20117                         winner = dd;
20118                         break;
20119                     // Otherwise the object with the most overlap wins
20120                     } else {
20121                         if (!winner ||
20122                             winner.overlap.getArea() < dd.overlap.getArea()) {
20123                             winner = dd;
20124                         }
20125                     }
20126                 }
20127             }
20128
20129             return winner;
20130         },
20131
20132         /**
20133          * Refreshes the cache of the top-left and bottom-right points of the
20134          * drag and drop objects in the specified group(s).  This is in the
20135          * format that is stored in the drag and drop instance, so typical
20136          * usage is:
20137          * <code>
20138          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
20139          * </code>
20140          * Alternatively:
20141          * <code>
20142          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
20143          * </code>
20144          * @TODO this really should be an indexed array.  Alternatively this
20145          * method could accept both.
20146          * @method refreshCache
20147          * @param {Object} groups an associative array of groups to refresh
20148          * @static
20149          */
20150         refreshCache: function(groups) {
20151             for (var sGroup in groups) {
20152                 if ("string" != typeof sGroup) {
20153                     continue;
20154                 }
20155                 for (var i in this.ids[sGroup]) {
20156                     var oDD = this.ids[sGroup][i];
20157
20158                     if (this.isTypeOfDD(oDD)) {
20159                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
20160                         var loc = this.getLocation(oDD);
20161                         if (loc) {
20162                             this.locationCache[oDD.id] = loc;
20163                         } else {
20164                             delete this.locationCache[oDD.id];
20165                             // this will unregister the drag and drop object if
20166                             // the element is not in a usable state
20167                             // oDD.unreg();
20168                         }
20169                     }
20170                 }
20171             }
20172         },
20173
20174         /**
20175          * This checks to make sure an element exists and is in the DOM.  The
20176          * main purpose is to handle cases where innerHTML is used to remove
20177          * drag and drop objects from the DOM.  IE provides an 'unspecified
20178          * error' when trying to access the offsetParent of such an element
20179          * @method verifyEl
20180          * @param {HTMLElement} el the element to check
20181          * @return {boolean} true if the element looks usable
20182          * @static
20183          */
20184         verifyEl: function(el) {
20185             if (el) {
20186                 var parent;
20187                 if(Roo.isIE){
20188                     try{
20189                         parent = el.offsetParent;
20190                     }catch(e){}
20191                 }else{
20192                     parent = el.offsetParent;
20193                 }
20194                 if (parent) {
20195                     return true;
20196                 }
20197             }
20198
20199             return false;
20200         },
20201
20202         /**
20203          * Returns a Region object containing the drag and drop element's position
20204          * and size, including the padding configured for it
20205          * @method getLocation
20206          * @param {DragDrop} oDD the drag and drop object to get the
20207          *                       location for
20208          * @return {Roo.lib.Region} a Region object representing the total area
20209          *                             the element occupies, including any padding
20210          *                             the instance is configured for.
20211          * @static
20212          */
20213         getLocation: function(oDD) {
20214             if (! this.isTypeOfDD(oDD)) {
20215                 return null;
20216             }
20217
20218             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
20219
20220             try {
20221                 pos= Roo.lib.Dom.getXY(el);
20222             } catch (e) { }
20223
20224             if (!pos) {
20225                 return null;
20226             }
20227
20228             x1 = pos[0];
20229             x2 = x1 + el.offsetWidth;
20230             y1 = pos[1];
20231             y2 = y1 + el.offsetHeight;
20232
20233             t = y1 - oDD.padding[0];
20234             r = x2 + oDD.padding[1];
20235             b = y2 + oDD.padding[2];
20236             l = x1 - oDD.padding[3];
20237
20238             return new Roo.lib.Region( t, r, b, l );
20239         },
20240
20241         /**
20242          * Checks the cursor location to see if it over the target
20243          * @method isOverTarget
20244          * @param {Roo.lib.Point} pt The point to evaluate
20245          * @param {DragDrop} oTarget the DragDrop object we are inspecting
20246          * @return {boolean} true if the mouse is over the target
20247          * @private
20248          * @static
20249          */
20250         isOverTarget: function(pt, oTarget, intersect) {
20251             // use cache if available
20252             var loc = this.locationCache[oTarget.id];
20253             if (!loc || !this.useCache) {
20254                 loc = this.getLocation(oTarget);
20255                 this.locationCache[oTarget.id] = loc;
20256
20257             }
20258
20259             if (!loc) {
20260                 return false;
20261             }
20262
20263             oTarget.cursorIsOver = loc.contains( pt );
20264
20265             // DragDrop is using this as a sanity check for the initial mousedown
20266             // in this case we are done.  In POINT mode, if the drag obj has no
20267             // contraints, we are also done. Otherwise we need to evaluate the
20268             // location of the target as related to the actual location of the
20269             // dragged element.
20270             var dc = this.dragCurrent;
20271             if (!dc || !dc.getTargetCoord ||
20272                     (!intersect && !dc.constrainX && !dc.constrainY)) {
20273                 return oTarget.cursorIsOver;
20274             }
20275
20276             oTarget.overlap = null;
20277
20278             // Get the current location of the drag element, this is the
20279             // location of the mouse event less the delta that represents
20280             // where the original mousedown happened on the element.  We
20281             // need to consider constraints and ticks as well.
20282             var pos = dc.getTargetCoord(pt.x, pt.y);
20283
20284             var el = dc.getDragEl();
20285             var curRegion = new Roo.lib.Region( pos.y,
20286                                                    pos.x + el.offsetWidth,
20287                                                    pos.y + el.offsetHeight,
20288                                                    pos.x );
20289
20290             var overlap = curRegion.intersect(loc);
20291
20292             if (overlap) {
20293                 oTarget.overlap = overlap;
20294                 return (intersect) ? true : oTarget.cursorIsOver;
20295             } else {
20296                 return false;
20297             }
20298         },
20299
20300         /**
20301          * unload event handler
20302          * @method _onUnload
20303          * @private
20304          * @static
20305          */
20306         _onUnload: function(e, me) {
20307             Roo.dd.DragDropMgr.unregAll();
20308         },
20309
20310         /**
20311          * Cleans up the drag and drop events and objects.
20312          * @method unregAll
20313          * @private
20314          * @static
20315          */
20316         unregAll: function() {
20317
20318             if (this.dragCurrent) {
20319                 this.stopDrag();
20320                 this.dragCurrent = null;
20321             }
20322
20323             this._execOnAll("unreg", []);
20324
20325             for (i in this.elementCache) {
20326                 delete this.elementCache[i];
20327             }
20328
20329             this.elementCache = {};
20330             this.ids = {};
20331         },
20332
20333         /**
20334          * A cache of DOM elements
20335          * @property elementCache
20336          * @private
20337          * @static
20338          */
20339         elementCache: {},
20340
20341         /**
20342          * Get the wrapper for the DOM element specified
20343          * @method getElWrapper
20344          * @param {String} id the id of the element to get
20345          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
20346          * @private
20347          * @deprecated This wrapper isn't that useful
20348          * @static
20349          */
20350         getElWrapper: function(id) {
20351             var oWrapper = this.elementCache[id];
20352             if (!oWrapper || !oWrapper.el) {
20353                 oWrapper = this.elementCache[id] =
20354                     new this.ElementWrapper(Roo.getDom(id));
20355             }
20356             return oWrapper;
20357         },
20358
20359         /**
20360          * Returns the actual DOM element
20361          * @method getElement
20362          * @param {String} id the id of the elment to get
20363          * @return {Object} The element
20364          * @deprecated use Roo.getDom instead
20365          * @static
20366          */
20367         getElement: function(id) {
20368             return Roo.getDom(id);
20369         },
20370
20371         /**
20372          * Returns the style property for the DOM element (i.e.,
20373          * document.getElById(id).style)
20374          * @method getCss
20375          * @param {String} id the id of the elment to get
20376          * @return {Object} The style property of the element
20377          * @deprecated use Roo.getDom instead
20378          * @static
20379          */
20380         getCss: function(id) {
20381             var el = Roo.getDom(id);
20382             return (el) ? el.style : null;
20383         },
20384
20385         /**
20386          * Inner class for cached elements
20387          * @class DragDropMgr.ElementWrapper
20388          * @for DragDropMgr
20389          * @private
20390          * @deprecated
20391          */
20392         ElementWrapper: function(el) {
20393                 /**
20394                  * The element
20395                  * @property el
20396                  */
20397                 this.el = el || null;
20398                 /**
20399                  * The element id
20400                  * @property id
20401                  */
20402                 this.id = this.el && el.id;
20403                 /**
20404                  * A reference to the style property
20405                  * @property css
20406                  */
20407                 this.css = this.el && el.style;
20408             },
20409
20410         /**
20411          * Returns the X position of an html element
20412          * @method getPosX
20413          * @param el the element for which to get the position
20414          * @return {int} the X coordinate
20415          * @for DragDropMgr
20416          * @deprecated use Roo.lib.Dom.getX instead
20417          * @static
20418          */
20419         getPosX: function(el) {
20420             return Roo.lib.Dom.getX(el);
20421         },
20422
20423         /**
20424          * Returns the Y position of an html element
20425          * @method getPosY
20426          * @param el the element for which to get the position
20427          * @return {int} the Y coordinate
20428          * @deprecated use Roo.lib.Dom.getY instead
20429          * @static
20430          */
20431         getPosY: function(el) {
20432             return Roo.lib.Dom.getY(el);
20433         },
20434
20435         /**
20436          * Swap two nodes.  In IE, we use the native method, for others we
20437          * emulate the IE behavior
20438          * @method swapNode
20439          * @param n1 the first node to swap
20440          * @param n2 the other node to swap
20441          * @static
20442          */
20443         swapNode: function(n1, n2) {
20444             if (n1.swapNode) {
20445                 n1.swapNode(n2);
20446             } else {
20447                 var p = n2.parentNode;
20448                 var s = n2.nextSibling;
20449
20450                 if (s == n1) {
20451                     p.insertBefore(n1, n2);
20452                 } else if (n2 == n1.nextSibling) {
20453                     p.insertBefore(n2, n1);
20454                 } else {
20455                     n1.parentNode.replaceChild(n2, n1);
20456                     p.insertBefore(n1, s);
20457                 }
20458             }
20459         },
20460
20461         /**
20462          * Returns the current scroll position
20463          * @method getScroll
20464          * @private
20465          * @static
20466          */
20467         getScroll: function () {
20468             var t, l, dde=document.documentElement, db=document.body;
20469             if (dde && (dde.scrollTop || dde.scrollLeft)) {
20470                 t = dde.scrollTop;
20471                 l = dde.scrollLeft;
20472             } else if (db) {
20473                 t = db.scrollTop;
20474                 l = db.scrollLeft;
20475             } else {
20476
20477             }
20478             return { top: t, left: l };
20479         },
20480
20481         /**
20482          * Returns the specified element style property
20483          * @method getStyle
20484          * @param {HTMLElement} el          the element
20485          * @param {string}      styleProp   the style property
20486          * @return {string} The value of the style property
20487          * @deprecated use Roo.lib.Dom.getStyle
20488          * @static
20489          */
20490         getStyle: function(el, styleProp) {
20491             return Roo.fly(el).getStyle(styleProp);
20492         },
20493
20494         /**
20495          * Gets the scrollTop
20496          * @method getScrollTop
20497          * @return {int} the document's scrollTop
20498          * @static
20499          */
20500         getScrollTop: function () { return this.getScroll().top; },
20501
20502         /**
20503          * Gets the scrollLeft
20504          * @method getScrollLeft
20505          * @return {int} the document's scrollTop
20506          * @static
20507          */
20508         getScrollLeft: function () { return this.getScroll().left; },
20509
20510         /**
20511          * Sets the x/y position of an element to the location of the
20512          * target element.
20513          * @method moveToEl
20514          * @param {HTMLElement} moveEl      The element to move
20515          * @param {HTMLElement} targetEl    The position reference element
20516          * @static
20517          */
20518         moveToEl: function (moveEl, targetEl) {
20519             var aCoord = Roo.lib.Dom.getXY(targetEl);
20520             Roo.lib.Dom.setXY(moveEl, aCoord);
20521         },
20522
20523         /**
20524          * Numeric array sort function
20525          * @method numericSort
20526          * @static
20527          */
20528         numericSort: function(a, b) { return (a - b); },
20529
20530         /**
20531          * Internal counter
20532          * @property _timeoutCount
20533          * @private
20534          * @static
20535          */
20536         _timeoutCount: 0,
20537
20538         /**
20539          * Trying to make the load order less important.  Without this we get
20540          * an error if this file is loaded before the Event Utility.
20541          * @method _addListeners
20542          * @private
20543          * @static
20544          */
20545         _addListeners: function() {
20546             var DDM = Roo.dd.DDM;
20547             if ( Roo.lib.Event && document ) {
20548                 DDM._onLoad();
20549             } else {
20550                 if (DDM._timeoutCount > 2000) {
20551                 } else {
20552                     setTimeout(DDM._addListeners, 10);
20553                     if (document && document.body) {
20554                         DDM._timeoutCount += 1;
20555                     }
20556                 }
20557             }
20558         },
20559
20560         /**
20561          * Recursively searches the immediate parent and all child nodes for
20562          * the handle element in order to determine wheter or not it was
20563          * clicked.
20564          * @method handleWasClicked
20565          * @param node the html element to inspect
20566          * @static
20567          */
20568         handleWasClicked: function(node, id) {
20569             if (this.isHandle(id, node.id)) {
20570                 return true;
20571             } else {
20572                 // check to see if this is a text node child of the one we want
20573                 var p = node.parentNode;
20574
20575                 while (p) {
20576                     if (this.isHandle(id, p.id)) {
20577                         return true;
20578                     } else {
20579                         p = p.parentNode;
20580                     }
20581                 }
20582             }
20583
20584             return false;
20585         }
20586
20587     };
20588
20589 }();
20590
20591 // shorter alias, save a few bytes
20592 Roo.dd.DDM = Roo.dd.DragDropMgr;
20593 Roo.dd.DDM._addListeners();
20594
20595 }/*
20596  * Based on:
20597  * Ext JS Library 1.1.1
20598  * Copyright(c) 2006-2007, Ext JS, LLC.
20599  *
20600  * Originally Released Under LGPL - original licence link has changed is not relivant.
20601  *
20602  * Fork - LGPL
20603  * <script type="text/javascript">
20604  */
20605
20606 /**
20607  * @class Roo.dd.DD
20608  * A DragDrop implementation where the linked element follows the
20609  * mouse cursor during a drag.
20610  * @extends Roo.dd.DragDrop
20611  * @constructor
20612  * @param {String} id the id of the linked element
20613  * @param {String} sGroup the group of related DragDrop items
20614  * @param {object} config an object containing configurable attributes
20615  *                Valid properties for DD:
20616  *                    scroll
20617  */
20618 Roo.dd.DD = function(id, sGroup, config) {
20619     if (id) {
20620         this.init(id, sGroup, config);
20621     }
20622 };
20623
20624 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
20625
20626     /**
20627      * When set to true, the utility automatically tries to scroll the browser
20628      * window wehn a drag and drop element is dragged near the viewport boundary.
20629      * Defaults to true.
20630      * @property scroll
20631      * @type boolean
20632      */
20633     scroll: true,
20634
20635     /**
20636      * Sets the pointer offset to the distance between the linked element's top
20637      * left corner and the location the element was clicked
20638      * @method autoOffset
20639      * @param {int} iPageX the X coordinate of the click
20640      * @param {int} iPageY the Y coordinate of the click
20641      */
20642     autoOffset: function(iPageX, iPageY) {
20643         var x = iPageX - this.startPageX;
20644         var y = iPageY - this.startPageY;
20645         this.setDelta(x, y);
20646     },
20647
20648     /**
20649      * Sets the pointer offset.  You can call this directly to force the
20650      * offset to be in a particular location (e.g., pass in 0,0 to set it
20651      * to the center of the object)
20652      * @method setDelta
20653      * @param {int} iDeltaX the distance from the left
20654      * @param {int} iDeltaY the distance from the top
20655      */
20656     setDelta: function(iDeltaX, iDeltaY) {
20657         this.deltaX = iDeltaX;
20658         this.deltaY = iDeltaY;
20659     },
20660
20661     /**
20662      * Sets the drag element to the location of the mousedown or click event,
20663      * maintaining the cursor location relative to the location on the element
20664      * that was clicked.  Override this if you want to place the element in a
20665      * location other than where the cursor is.
20666      * @method setDragElPos
20667      * @param {int} iPageX the X coordinate of the mousedown or drag event
20668      * @param {int} iPageY the Y coordinate of the mousedown or drag event
20669      */
20670     setDragElPos: function(iPageX, iPageY) {
20671         // the first time we do this, we are going to check to make sure
20672         // the element has css positioning
20673
20674         var el = this.getDragEl();
20675         this.alignElWithMouse(el, iPageX, iPageY);
20676     },
20677
20678     /**
20679      * Sets the element to the location of the mousedown or click event,
20680      * maintaining the cursor location relative to the location on the element
20681      * that was clicked.  Override this if you want to place the element in a
20682      * location other than where the cursor is.
20683      * @method alignElWithMouse
20684      * @param {HTMLElement} el the element to move
20685      * @param {int} iPageX the X coordinate of the mousedown or drag event
20686      * @param {int} iPageY the Y coordinate of the mousedown or drag event
20687      */
20688     alignElWithMouse: function(el, iPageX, iPageY) {
20689         var oCoord = this.getTargetCoord(iPageX, iPageY);
20690         var fly = el.dom ? el : Roo.fly(el);
20691         if (!this.deltaSetXY) {
20692             var aCoord = [oCoord.x, oCoord.y];
20693             fly.setXY(aCoord);
20694             var newLeft = fly.getLeft(true);
20695             var newTop  = fly.getTop(true);
20696             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
20697         } else {
20698             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
20699         }
20700
20701         this.cachePosition(oCoord.x, oCoord.y);
20702         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
20703         return oCoord;
20704     },
20705
20706     /**
20707      * Saves the most recent position so that we can reset the constraints and
20708      * tick marks on-demand.  We need to know this so that we can calculate the
20709      * number of pixels the element is offset from its original position.
20710      * @method cachePosition
20711      * @param iPageX the current x position (optional, this just makes it so we
20712      * don't have to look it up again)
20713      * @param iPageY the current y position (optional, this just makes it so we
20714      * don't have to look it up again)
20715      */
20716     cachePosition: function(iPageX, iPageY) {
20717         if (iPageX) {
20718             this.lastPageX = iPageX;
20719             this.lastPageY = iPageY;
20720         } else {
20721             var aCoord = Roo.lib.Dom.getXY(this.getEl());
20722             this.lastPageX = aCoord[0];
20723             this.lastPageY = aCoord[1];
20724         }
20725     },
20726
20727     /**
20728      * Auto-scroll the window if the dragged object has been moved beyond the
20729      * visible window boundary.
20730      * @method autoScroll
20731      * @param {int} x the drag element's x position
20732      * @param {int} y the drag element's y position
20733      * @param {int} h the height of the drag element
20734      * @param {int} w the width of the drag element
20735      * @private
20736      */
20737     autoScroll: function(x, y, h, w) {
20738
20739         if (this.scroll) {
20740             // The client height
20741             var clientH = Roo.lib.Dom.getViewWidth();
20742
20743             // The client width
20744             var clientW = Roo.lib.Dom.getViewHeight();
20745
20746             // The amt scrolled down
20747             var st = this.DDM.getScrollTop();
20748
20749             // The amt scrolled right
20750             var sl = this.DDM.getScrollLeft();
20751
20752             // Location of the bottom of the element
20753             var bot = h + y;
20754
20755             // Location of the right of the element
20756             var right = w + x;
20757
20758             // The distance from the cursor to the bottom of the visible area,
20759             // adjusted so that we don't scroll if the cursor is beyond the
20760             // element drag constraints
20761             var toBot = (clientH + st - y - this.deltaY);
20762
20763             // The distance from the cursor to the right of the visible area
20764             var toRight = (clientW + sl - x - this.deltaX);
20765
20766
20767             // How close to the edge the cursor must be before we scroll
20768             // var thresh = (document.all) ? 100 : 40;
20769             var thresh = 40;
20770
20771             // How many pixels to scroll per autoscroll op.  This helps to reduce
20772             // clunky scrolling. IE is more sensitive about this ... it needs this
20773             // value to be higher.
20774             var scrAmt = (document.all) ? 80 : 30;
20775
20776             // Scroll down if we are near the bottom of the visible page and the
20777             // obj extends below the crease
20778             if ( bot > clientH && toBot < thresh ) {
20779                 window.scrollTo(sl, st + scrAmt);
20780             }
20781
20782             // Scroll up if the window is scrolled down and the top of the object
20783             // goes above the top border
20784             if ( y < st && st > 0 && y - st < thresh ) {
20785                 window.scrollTo(sl, st - scrAmt);
20786             }
20787
20788             // Scroll right if the obj is beyond the right border and the cursor is
20789             // near the border.
20790             if ( right > clientW && toRight < thresh ) {
20791                 window.scrollTo(sl + scrAmt, st);
20792             }
20793
20794             // Scroll left if the window has been scrolled to the right and the obj
20795             // extends past the left border
20796             if ( x < sl && sl > 0 && x - sl < thresh ) {
20797                 window.scrollTo(sl - scrAmt, st);
20798             }
20799         }
20800     },
20801
20802     /**
20803      * Finds the location the element should be placed if we want to move
20804      * it to where the mouse location less the click offset would place us.
20805      * @method getTargetCoord
20806      * @param {int} iPageX the X coordinate of the click
20807      * @param {int} iPageY the Y coordinate of the click
20808      * @return an object that contains the coordinates (Object.x and Object.y)
20809      * @private
20810      */
20811     getTargetCoord: function(iPageX, iPageY) {
20812
20813
20814         var x = iPageX - this.deltaX;
20815         var y = iPageY - this.deltaY;
20816
20817         if (this.constrainX) {
20818             if (x < this.minX) { x = this.minX; }
20819             if (x > this.maxX) { x = this.maxX; }
20820         }
20821
20822         if (this.constrainY) {
20823             if (y < this.minY) { y = this.minY; }
20824             if (y > this.maxY) { y = this.maxY; }
20825         }
20826
20827         x = this.getTick(x, this.xTicks);
20828         y = this.getTick(y, this.yTicks);
20829
20830
20831         return {x:x, y:y};
20832     },
20833
20834     /*
20835      * Sets up config options specific to this class. Overrides
20836      * Roo.dd.DragDrop, but all versions of this method through the
20837      * inheritance chain are called
20838      */
20839     applyConfig: function() {
20840         Roo.dd.DD.superclass.applyConfig.call(this);
20841         this.scroll = (this.config.scroll !== false);
20842     },
20843
20844     /*
20845      * Event that fires prior to the onMouseDown event.  Overrides
20846      * Roo.dd.DragDrop.
20847      */
20848     b4MouseDown: function(e) {
20849         // this.resetConstraints();
20850         this.autoOffset(e.getPageX(),
20851                             e.getPageY());
20852     },
20853
20854     /*
20855      * Event that fires prior to the onDrag event.  Overrides
20856      * Roo.dd.DragDrop.
20857      */
20858     b4Drag: function(e) {
20859         this.setDragElPos(e.getPageX(),
20860                             e.getPageY());
20861     },
20862
20863     toString: function() {
20864         return ("DD " + this.id);
20865     }
20866
20867     //////////////////////////////////////////////////////////////////////////
20868     // Debugging ygDragDrop events that can be overridden
20869     //////////////////////////////////////////////////////////////////////////
20870     /*
20871     startDrag: function(x, y) {
20872     },
20873
20874     onDrag: function(e) {
20875     },
20876
20877     onDragEnter: function(e, id) {
20878     },
20879
20880     onDragOver: function(e, id) {
20881     },
20882
20883     onDragOut: function(e, id) {
20884     },
20885
20886     onDragDrop: function(e, id) {
20887     },
20888
20889     endDrag: function(e) {
20890     }
20891
20892     */
20893
20894 });/*
20895  * Based on:
20896  * Ext JS Library 1.1.1
20897  * Copyright(c) 2006-2007, Ext JS, LLC.
20898  *
20899  * Originally Released Under LGPL - original licence link has changed is not relivant.
20900  *
20901  * Fork - LGPL
20902  * <script type="text/javascript">
20903  */
20904
20905 /**
20906  * @class Roo.dd.DDProxy
20907  * A DragDrop implementation that inserts an empty, bordered div into
20908  * the document that follows the cursor during drag operations.  At the time of
20909  * the click, the frame div is resized to the dimensions of the linked html
20910  * element, and moved to the exact location of the linked element.
20911  *
20912  * References to the "frame" element refer to the single proxy element that
20913  * was created to be dragged in place of all DDProxy elements on the
20914  * page.
20915  *
20916  * @extends Roo.dd.DD
20917  * @constructor
20918  * @param {String} id the id of the linked html element
20919  * @param {String} sGroup the group of related DragDrop objects
20920  * @param {object} config an object containing configurable attributes
20921  *                Valid properties for DDProxy in addition to those in DragDrop:
20922  *                   resizeFrame, centerFrame, dragElId
20923  */
20924 Roo.dd.DDProxy = function(id, sGroup, config) {
20925     if (id) {
20926         this.init(id, sGroup, config);
20927         this.initFrame();
20928     }
20929 };
20930
20931 /**
20932  * The default drag frame div id
20933  * @property Roo.dd.DDProxy.dragElId
20934  * @type String
20935  * @static
20936  */
20937 Roo.dd.DDProxy.dragElId = "ygddfdiv";
20938
20939 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
20940
20941     /**
20942      * By default we resize the drag frame to be the same size as the element
20943      * we want to drag (this is to get the frame effect).  We can turn it off
20944      * if we want a different behavior.
20945      * @property resizeFrame
20946      * @type boolean
20947      */
20948     resizeFrame: true,
20949
20950     /**
20951      * By default the frame is positioned exactly where the drag element is, so
20952      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
20953      * you do not have constraints on the obj is to have the drag frame centered
20954      * around the cursor.  Set centerFrame to true for this effect.
20955      * @property centerFrame
20956      * @type boolean
20957      */
20958     centerFrame: false,
20959
20960     /**
20961      * Creates the proxy element if it does not yet exist
20962      * @method createFrame
20963      */
20964     createFrame: function() {
20965         var self = this;
20966         var body = document.body;
20967
20968         if (!body || !body.firstChild) {
20969             setTimeout( function() { self.createFrame(); }, 50 );
20970             return;
20971         }
20972
20973         var div = this.getDragEl();
20974
20975         if (!div) {
20976             div    = document.createElement("div");
20977             div.id = this.dragElId;
20978             var s  = div.style;
20979
20980             s.position   = "absolute";
20981             s.visibility = "hidden";
20982             s.cursor     = "move";
20983             s.border     = "2px solid #aaa";
20984             s.zIndex     = 999;
20985
20986             // appendChild can blow up IE if invoked prior to the window load event
20987             // while rendering a table.  It is possible there are other scenarios
20988             // that would cause this to happen as well.
20989             body.insertBefore(div, body.firstChild);
20990         }
20991     },
20992
20993     /**
20994      * Initialization for the drag frame element.  Must be called in the
20995      * constructor of all subclasses
20996      * @method initFrame
20997      */
20998     initFrame: function() {
20999         this.createFrame();
21000     },
21001
21002     applyConfig: function() {
21003         Roo.dd.DDProxy.superclass.applyConfig.call(this);
21004
21005         this.resizeFrame = (this.config.resizeFrame !== false);
21006         this.centerFrame = (this.config.centerFrame);
21007         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
21008     },
21009
21010     /**
21011      * Resizes the drag frame to the dimensions of the clicked object, positions
21012      * it over the object, and finally displays it
21013      * @method showFrame
21014      * @param {int} iPageX X click position
21015      * @param {int} iPageY Y click position
21016      * @private
21017      */
21018     showFrame: function(iPageX, iPageY) {
21019         var el = this.getEl();
21020         var dragEl = this.getDragEl();
21021         var s = dragEl.style;
21022
21023         this._resizeProxy();
21024
21025         if (this.centerFrame) {
21026             this.setDelta( Math.round(parseInt(s.width,  10)/2),
21027                            Math.round(parseInt(s.height, 10)/2) );
21028         }
21029
21030         this.setDragElPos(iPageX, iPageY);
21031
21032         Roo.fly(dragEl).show();
21033     },
21034
21035     /**
21036      * The proxy is automatically resized to the dimensions of the linked
21037      * element when a drag is initiated, unless resizeFrame is set to false
21038      * @method _resizeProxy
21039      * @private
21040      */
21041     _resizeProxy: function() {
21042         if (this.resizeFrame) {
21043             var el = this.getEl();
21044             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
21045         }
21046     },
21047
21048     // overrides Roo.dd.DragDrop
21049     b4MouseDown: function(e) {
21050         var x = e.getPageX();
21051         var y = e.getPageY();
21052         this.autoOffset(x, y);
21053         this.setDragElPos(x, y);
21054     },
21055
21056     // overrides Roo.dd.DragDrop
21057     b4StartDrag: function(x, y) {
21058         // show the drag frame
21059         this.showFrame(x, y);
21060     },
21061
21062     // overrides Roo.dd.DragDrop
21063     b4EndDrag: function(e) {
21064         Roo.fly(this.getDragEl()).hide();
21065     },
21066
21067     // overrides Roo.dd.DragDrop
21068     // By default we try to move the element to the last location of the frame.
21069     // This is so that the default behavior mirrors that of Roo.dd.DD.
21070     endDrag: function(e) {
21071
21072         var lel = this.getEl();
21073         var del = this.getDragEl();
21074
21075         // Show the drag frame briefly so we can get its position
21076         del.style.visibility = "";
21077
21078         this.beforeMove();
21079         // Hide the linked element before the move to get around a Safari
21080         // rendering bug.
21081         lel.style.visibility = "hidden";
21082         Roo.dd.DDM.moveToEl(lel, del);
21083         del.style.visibility = "hidden";
21084         lel.style.visibility = "";
21085
21086         this.afterDrag();
21087     },
21088
21089     beforeMove : function(){
21090
21091     },
21092
21093     afterDrag : function(){
21094
21095     },
21096
21097     toString: function() {
21098         return ("DDProxy " + this.id);
21099     }
21100
21101 });
21102 /*
21103  * Based on:
21104  * Ext JS Library 1.1.1
21105  * Copyright(c) 2006-2007, Ext JS, LLC.
21106  *
21107  * Originally Released Under LGPL - original licence link has changed is not relivant.
21108  *
21109  * Fork - LGPL
21110  * <script type="text/javascript">
21111  */
21112
21113  /**
21114  * @class Roo.dd.DDTarget
21115  * A DragDrop implementation that does not move, but can be a drop
21116  * target.  You would get the same result by simply omitting implementation
21117  * for the event callbacks, but this way we reduce the processing cost of the
21118  * event listener and the callbacks.
21119  * @extends Roo.dd.DragDrop
21120  * @constructor
21121  * @param {String} id the id of the element that is a drop target
21122  * @param {String} sGroup the group of related DragDrop objects
21123  * @param {object} config an object containing configurable attributes
21124  *                 Valid properties for DDTarget in addition to those in
21125  *                 DragDrop:
21126  *                    none
21127  */
21128 Roo.dd.DDTarget = function(id, sGroup, config) {
21129     if (id) {
21130         this.initTarget(id, sGroup, config);
21131     }
21132     if (config && (config.listeners || config.events)) { 
21133         Roo.dd.DragDrop.superclass.constructor.call(this,  { 
21134             listeners : config.listeners || {}, 
21135             events : config.events || {} 
21136         });    
21137     }
21138 };
21139
21140 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
21141 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
21142     toString: function() {
21143         return ("DDTarget " + this.id);
21144     }
21145 });
21146 /*
21147  * Based on:
21148  * Ext JS Library 1.1.1
21149  * Copyright(c) 2006-2007, Ext JS, LLC.
21150  *
21151  * Originally Released Under LGPL - original licence link has changed is not relivant.
21152  *
21153  * Fork - LGPL
21154  * <script type="text/javascript">
21155  */
21156  
21157
21158 /**
21159  * @class Roo.dd.ScrollManager
21160  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
21161  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
21162  * @singleton
21163  */
21164 Roo.dd.ScrollManager = function(){
21165     var ddm = Roo.dd.DragDropMgr;
21166     var els = {};
21167     var dragEl = null;
21168     var proc = {};
21169     
21170     
21171     
21172     var onStop = function(e){
21173         dragEl = null;
21174         clearProc();
21175     };
21176     
21177     var triggerRefresh = function(){
21178         if(ddm.dragCurrent){
21179              ddm.refreshCache(ddm.dragCurrent.groups);
21180         }
21181     };
21182     
21183     var doScroll = function(){
21184         if(ddm.dragCurrent){
21185             var dds = Roo.dd.ScrollManager;
21186             if(!dds.animate){
21187                 if(proc.el.scroll(proc.dir, dds.increment)){
21188                     triggerRefresh();
21189                 }
21190             }else{
21191                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
21192             }
21193         }
21194     };
21195     
21196     var clearProc = function(){
21197         if(proc.id){
21198             clearInterval(proc.id);
21199         }
21200         proc.id = 0;
21201         proc.el = null;
21202         proc.dir = "";
21203     };
21204     
21205     var startProc = function(el, dir){
21206          Roo.log('scroll startproc');
21207         clearProc();
21208         proc.el = el;
21209         proc.dir = dir;
21210         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
21211     };
21212     
21213     var onFire = function(e, isDrop){
21214        
21215         if(isDrop || !ddm.dragCurrent){ return; }
21216         var dds = Roo.dd.ScrollManager;
21217         if(!dragEl || dragEl != ddm.dragCurrent){
21218             dragEl = ddm.dragCurrent;
21219             // refresh regions on drag start
21220             dds.refreshCache();
21221         }
21222         
21223         var xy = Roo.lib.Event.getXY(e);
21224         var pt = new Roo.lib.Point(xy[0], xy[1]);
21225         for(var id in els){
21226             var el = els[id], r = el._region;
21227             if(r && r.contains(pt) && el.isScrollable()){
21228                 if(r.bottom - pt.y <= dds.thresh){
21229                     if(proc.el != el){
21230                         startProc(el, "down");
21231                     }
21232                     return;
21233                 }else if(r.right - pt.x <= dds.thresh){
21234                     if(proc.el != el){
21235                         startProc(el, "left");
21236                     }
21237                     return;
21238                 }else if(pt.y - r.top <= dds.thresh){
21239                     if(proc.el != el){
21240                         startProc(el, "up");
21241                     }
21242                     return;
21243                 }else if(pt.x - r.left <= dds.thresh){
21244                     if(proc.el != el){
21245                         startProc(el, "right");
21246                     }
21247                     return;
21248                 }
21249             }
21250         }
21251         clearProc();
21252     };
21253     
21254     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
21255     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
21256     
21257     return {
21258         /**
21259          * Registers new overflow element(s) to auto scroll
21260          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
21261          */
21262         register : function(el){
21263             if(el instanceof Array){
21264                 for(var i = 0, len = el.length; i < len; i++) {
21265                         this.register(el[i]);
21266                 }
21267             }else{
21268                 el = Roo.get(el);
21269                 els[el.id] = el;
21270             }
21271             Roo.dd.ScrollManager.els = els;
21272         },
21273         
21274         /**
21275          * Unregisters overflow element(s) so they are no longer scrolled
21276          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
21277          */
21278         unregister : function(el){
21279             if(el instanceof Array){
21280                 for(var i = 0, len = el.length; i < len; i++) {
21281                         this.unregister(el[i]);
21282                 }
21283             }else{
21284                 el = Roo.get(el);
21285                 delete els[el.id];
21286             }
21287         },
21288         
21289         /**
21290          * The number of pixels from the edge of a container the pointer needs to be to 
21291          * trigger scrolling (defaults to 25)
21292          * @type Number
21293          */
21294         thresh : 25,
21295         
21296         /**
21297          * The number of pixels to scroll in each scroll increment (defaults to 50)
21298          * @type Number
21299          */
21300         increment : 100,
21301         
21302         /**
21303          * The frequency of scrolls in milliseconds (defaults to 500)
21304          * @type Number
21305          */
21306         frequency : 500,
21307         
21308         /**
21309          * True to animate the scroll (defaults to true)
21310          * @type Boolean
21311          */
21312         animate: true,
21313         
21314         /**
21315          * The animation duration in seconds - 
21316          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
21317          * @type Number
21318          */
21319         animDuration: .4,
21320         
21321         /**
21322          * Manually trigger a cache refresh.
21323          */
21324         refreshCache : function(){
21325             for(var id in els){
21326                 if(typeof els[id] == 'object'){ // for people extending the object prototype
21327                     els[id]._region = els[id].getRegion();
21328                 }
21329             }
21330         }
21331     };
21332 }();/*
21333  * Based on:
21334  * Ext JS Library 1.1.1
21335  * Copyright(c) 2006-2007, Ext JS, LLC.
21336  *
21337  * Originally Released Under LGPL - original licence link has changed is not relivant.
21338  *
21339  * Fork - LGPL
21340  * <script type="text/javascript">
21341  */
21342  
21343
21344 /**
21345  * @class Roo.dd.Registry
21346  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
21347  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
21348  * @singleton
21349  */
21350 Roo.dd.Registry = function(){
21351     var elements = {}; 
21352     var handles = {}; 
21353     var autoIdSeed = 0;
21354
21355     var getId = function(el, autogen){
21356         if(typeof el == "string"){
21357             return el;
21358         }
21359         var id = el.id;
21360         if(!id && autogen !== false){
21361             id = "roodd-" + (++autoIdSeed);
21362             el.id = id;
21363         }
21364         return id;
21365     };
21366     
21367     return {
21368     /**
21369      * Register a drag drop element
21370      * @param {String|HTMLElement} element The id or DOM node to register
21371      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
21372      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
21373      * knows how to interpret, plus there are some specific properties known to the Registry that should be
21374      * populated in the data object (if applicable):
21375      * <pre>
21376 Value      Description<br />
21377 ---------  ------------------------------------------<br />
21378 handles    Array of DOM nodes that trigger dragging<br />
21379            for the element being registered<br />
21380 isHandle   True if the element passed in triggers<br />
21381            dragging itself, else false
21382 </pre>
21383      */
21384         register : function(el, data){
21385             data = data || {};
21386             if(typeof el == "string"){
21387                 el = document.getElementById(el);
21388             }
21389             data.ddel = el;
21390             elements[getId(el)] = data;
21391             if(data.isHandle !== false){
21392                 handles[data.ddel.id] = data;
21393             }
21394             if(data.handles){
21395                 var hs = data.handles;
21396                 for(var i = 0, len = hs.length; i < len; i++){
21397                         handles[getId(hs[i])] = data;
21398                 }
21399             }
21400         },
21401
21402     /**
21403      * Unregister a drag drop element
21404      * @param {String|HTMLElement}  element The id or DOM node to unregister
21405      */
21406         unregister : function(el){
21407             var id = getId(el, false);
21408             var data = elements[id];
21409             if(data){
21410                 delete elements[id];
21411                 if(data.handles){
21412                     var hs = data.handles;
21413                     for(var i = 0, len = hs.length; i < len; i++){
21414                         delete handles[getId(hs[i], false)];
21415                     }
21416                 }
21417             }
21418         },
21419
21420     /**
21421      * Returns the handle registered for a DOM Node by id
21422      * @param {String|HTMLElement} id The DOM node or id to look up
21423      * @return {Object} handle The custom handle data
21424      */
21425         getHandle : function(id){
21426             if(typeof id != "string"){ // must be element?
21427                 id = id.id;
21428             }
21429             return handles[id];
21430         },
21431
21432     /**
21433      * Returns the handle that is registered for the DOM node that is the target of the event
21434      * @param {Event} e The event
21435      * @return {Object} handle The custom handle data
21436      */
21437         getHandleFromEvent : function(e){
21438             var t = Roo.lib.Event.getTarget(e);
21439             return t ? handles[t.id] : null;
21440         },
21441
21442     /**
21443      * Returns a custom data object that is registered for a DOM node by id
21444      * @param {String|HTMLElement} id The DOM node or id to look up
21445      * @return {Object} data The custom data
21446      */
21447         getTarget : function(id){
21448             if(typeof id != "string"){ // must be element?
21449                 id = id.id;
21450             }
21451             return elements[id];
21452         },
21453
21454     /**
21455      * Returns a custom data object that is registered for the DOM node that is the target of the event
21456      * @param {Event} e The event
21457      * @return {Object} data The custom data
21458      */
21459         getTargetFromEvent : function(e){
21460             var t = Roo.lib.Event.getTarget(e);
21461             return t ? elements[t.id] || handles[t.id] : null;
21462         }
21463     };
21464 }();/*
21465  * Based on:
21466  * Ext JS Library 1.1.1
21467  * Copyright(c) 2006-2007, Ext JS, LLC.
21468  *
21469  * Originally Released Under LGPL - original licence link has changed is not relivant.
21470  *
21471  * Fork - LGPL
21472  * <script type="text/javascript">
21473  */
21474  
21475
21476 /**
21477  * @class Roo.dd.StatusProxy
21478  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
21479  * default drag proxy used by all Roo.dd components.
21480  * @constructor
21481  * @param {Object} config
21482  */
21483 Roo.dd.StatusProxy = function(config){
21484     Roo.apply(this, config);
21485     this.id = this.id || Roo.id();
21486     this.el = new Roo.Layer({
21487         dh: {
21488             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
21489                 {tag: "div", cls: "x-dd-drop-icon"},
21490                 {tag: "div", cls: "x-dd-drag-ghost"}
21491             ]
21492         }, 
21493         shadow: !config || config.shadow !== false
21494     });
21495     this.ghost = Roo.get(this.el.dom.childNodes[1]);
21496     this.dropStatus = this.dropNotAllowed;
21497 };
21498
21499 Roo.dd.StatusProxy.prototype = {
21500     /**
21501      * @cfg {String} dropAllowed
21502      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
21503      */
21504     dropAllowed : "x-dd-drop-ok",
21505     /**
21506      * @cfg {String} dropNotAllowed
21507      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
21508      */
21509     dropNotAllowed : "x-dd-drop-nodrop",
21510
21511     /**
21512      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
21513      * over the current target element.
21514      * @param {String} cssClass The css class for the new drop status indicator image
21515      */
21516     setStatus : function(cssClass){
21517         cssClass = cssClass || this.dropNotAllowed;
21518         if(this.dropStatus != cssClass){
21519             this.el.replaceClass(this.dropStatus, cssClass);
21520             this.dropStatus = cssClass;
21521         }
21522     },
21523
21524     /**
21525      * Resets the status indicator to the default dropNotAllowed value
21526      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
21527      */
21528     reset : function(clearGhost){
21529         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
21530         this.dropStatus = this.dropNotAllowed;
21531         if(clearGhost){
21532             this.ghost.update("");
21533         }
21534     },
21535
21536     /**
21537      * Updates the contents of the ghost element
21538      * @param {String} html The html that will replace the current innerHTML of the ghost element
21539      */
21540     update : function(html){
21541         if(typeof html == "string"){
21542             this.ghost.update(html);
21543         }else{
21544             this.ghost.update("");
21545             html.style.margin = "0";
21546             this.ghost.dom.appendChild(html);
21547         }
21548         // ensure float = none set?? cant remember why though.
21549         var el = this.ghost.dom.firstChild;
21550                 if(el){
21551                         Roo.fly(el).setStyle('float', 'none');
21552                 }
21553     },
21554     
21555     /**
21556      * Returns the underlying proxy {@link Roo.Layer}
21557      * @return {Roo.Layer} el
21558     */
21559     getEl : function(){
21560         return this.el;
21561     },
21562
21563     /**
21564      * Returns the ghost element
21565      * @return {Roo.Element} el
21566      */
21567     getGhost : function(){
21568         return this.ghost;
21569     },
21570
21571     /**
21572      * Hides the proxy
21573      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
21574      */
21575     hide : function(clear){
21576         this.el.hide();
21577         if(clear){
21578             this.reset(true);
21579         }
21580     },
21581
21582     /**
21583      * Stops the repair animation if it's currently running
21584      */
21585     stop : function(){
21586         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
21587             this.anim.stop();
21588         }
21589     },
21590
21591     /**
21592      * Displays this proxy
21593      */
21594     show : function(){
21595         this.el.show();
21596     },
21597
21598     /**
21599      * Force the Layer to sync its shadow and shim positions to the element
21600      */
21601     sync : function(){
21602         this.el.sync();
21603     },
21604
21605     /**
21606      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
21607      * invalid drop operation by the item being dragged.
21608      * @param {Array} xy The XY position of the element ([x, y])
21609      * @param {Function} callback The function to call after the repair is complete
21610      * @param {Object} scope The scope in which to execute the callback
21611      */
21612     repair : function(xy, callback, scope){
21613         this.callback = callback;
21614         this.scope = scope;
21615         if(xy && this.animRepair !== false){
21616             this.el.addClass("x-dd-drag-repair");
21617             this.el.hideUnders(true);
21618             this.anim = this.el.shift({
21619                 duration: this.repairDuration || .5,
21620                 easing: 'easeOut',
21621                 xy: xy,
21622                 stopFx: true,
21623                 callback: this.afterRepair,
21624                 scope: this
21625             });
21626         }else{
21627             this.afterRepair();
21628         }
21629     },
21630
21631     // private
21632     afterRepair : function(){
21633         this.hide(true);
21634         if(typeof this.callback == "function"){
21635             this.callback.call(this.scope || this);
21636         }
21637         this.callback = null;
21638         this.scope = null;
21639     }
21640 };/*
21641  * Based on:
21642  * Ext JS Library 1.1.1
21643  * Copyright(c) 2006-2007, Ext JS, LLC.
21644  *
21645  * Originally Released Under LGPL - original licence link has changed is not relivant.
21646  *
21647  * Fork - LGPL
21648  * <script type="text/javascript">
21649  */
21650
21651 /**
21652  * @class Roo.dd.DragSource
21653  * @extends Roo.dd.DDProxy
21654  * A simple class that provides the basic implementation needed to make any element draggable.
21655  * @constructor
21656  * @param {String/HTMLElement/Element} el The container element
21657  * @param {Object} config
21658  */
21659 Roo.dd.DragSource = function(el, config){
21660     this.el = Roo.get(el);
21661     this.dragData = {};
21662     
21663     Roo.apply(this, config);
21664     
21665     if(!this.proxy){
21666         this.proxy = new Roo.dd.StatusProxy();
21667     }
21668
21669     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
21670           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
21671     
21672     this.dragging = false;
21673 };
21674
21675 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
21676     /**
21677      * @cfg {String} dropAllowed
21678      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
21679      */
21680     dropAllowed : "x-dd-drop-ok",
21681     /**
21682      * @cfg {String} dropNotAllowed
21683      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
21684      */
21685     dropNotAllowed : "x-dd-drop-nodrop",
21686
21687     /**
21688      * Returns the data object associated with this drag source
21689      * @return {Object} data An object containing arbitrary data
21690      */
21691     getDragData : function(e){
21692         return this.dragData;
21693     },
21694
21695     // private
21696     onDragEnter : function(e, id){
21697         var target = Roo.dd.DragDropMgr.getDDById(id);
21698         this.cachedTarget = target;
21699         if(this.beforeDragEnter(target, e, id) !== false){
21700             if(target.isNotifyTarget){
21701                 var status = target.notifyEnter(this, e, this.dragData);
21702                 this.proxy.setStatus(status);
21703             }else{
21704                 this.proxy.setStatus(this.dropAllowed);
21705             }
21706             
21707             if(this.afterDragEnter){
21708                 /**
21709                  * An empty function by default, but provided so that you can perform a custom action
21710                  * when the dragged item enters the drop target by providing an implementation.
21711                  * @param {Roo.dd.DragDrop} target The drop target
21712                  * @param {Event} e The event object
21713                  * @param {String} id The id of the dragged element
21714                  * @method afterDragEnter
21715                  */
21716                 this.afterDragEnter(target, e, id);
21717             }
21718         }
21719     },
21720
21721     /**
21722      * An empty function by default, but provided so that you can perform a custom action
21723      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
21724      * @param {Roo.dd.DragDrop} target The drop target
21725      * @param {Event} e The event object
21726      * @param {String} id The id of the dragged element
21727      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
21728      */
21729     beforeDragEnter : function(target, e, id){
21730         return true;
21731     },
21732
21733     // private
21734     alignElWithMouse: function() {
21735         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
21736         this.proxy.sync();
21737     },
21738
21739     // private
21740     onDragOver : function(e, id){
21741         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
21742         if(this.beforeDragOver(target, e, id) !== false){
21743             if(target.isNotifyTarget){
21744                 var status = target.notifyOver(this, e, this.dragData);
21745                 this.proxy.setStatus(status);
21746             }
21747
21748             if(this.afterDragOver){
21749                 /**
21750                  * An empty function by default, but provided so that you can perform a custom action
21751                  * while the dragged item is over the drop target by providing an implementation.
21752                  * @param {Roo.dd.DragDrop} target The drop target
21753                  * @param {Event} e The event object
21754                  * @param {String} id The id of the dragged element
21755                  * @method afterDragOver
21756                  */
21757                 this.afterDragOver(target, e, id);
21758             }
21759         }
21760     },
21761
21762     /**
21763      * An empty function by default, but provided so that you can perform a custom action
21764      * while the dragged item is over the drop target and optionally cancel the onDragOver.
21765      * @param {Roo.dd.DragDrop} target The drop target
21766      * @param {Event} e The event object
21767      * @param {String} id The id of the dragged element
21768      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
21769      */
21770     beforeDragOver : function(target, e, id){
21771         return true;
21772     },
21773
21774     // private
21775     onDragOut : function(e, id){
21776         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
21777         if(this.beforeDragOut(target, e, id) !== false){
21778             if(target.isNotifyTarget){
21779                 target.notifyOut(this, e, this.dragData);
21780             }
21781             this.proxy.reset();
21782             if(this.afterDragOut){
21783                 /**
21784                  * An empty function by default, but provided so that you can perform a custom action
21785                  * after the dragged item is dragged out of the target without dropping.
21786                  * @param {Roo.dd.DragDrop} target The drop target
21787                  * @param {Event} e The event object
21788                  * @param {String} id The id of the dragged element
21789                  * @method afterDragOut
21790                  */
21791                 this.afterDragOut(target, e, id);
21792             }
21793         }
21794         this.cachedTarget = null;
21795     },
21796
21797     /**
21798      * An empty function by default, but provided so that you can perform a custom action before the dragged
21799      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
21800      * @param {Roo.dd.DragDrop} target The drop target
21801      * @param {Event} e The event object
21802      * @param {String} id The id of the dragged element
21803      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
21804      */
21805     beforeDragOut : function(target, e, id){
21806         return true;
21807     },
21808     
21809     // private
21810     onDragDrop : function(e, id){
21811         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
21812         if(this.beforeDragDrop(target, e, id) !== false){
21813             if(target.isNotifyTarget){
21814                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
21815                     this.onValidDrop(target, e, id);
21816                 }else{
21817                     this.onInvalidDrop(target, e, id);
21818                 }
21819             }else{
21820                 this.onValidDrop(target, e, id);
21821             }
21822             
21823             if(this.afterDragDrop){
21824                 /**
21825                  * An empty function by default, but provided so that you can perform a custom action
21826                  * after a valid drag drop has occurred by providing an implementation.
21827                  * @param {Roo.dd.DragDrop} target The drop target
21828                  * @param {Event} e The event object
21829                  * @param {String} id The id of the dropped element
21830                  * @method afterDragDrop
21831                  */
21832                 this.afterDragDrop(target, e, id);
21833             }
21834         }
21835         delete this.cachedTarget;
21836     },
21837
21838     /**
21839      * An empty function by default, but provided so that you can perform a custom action before the dragged
21840      * item is dropped onto the target and optionally cancel the onDragDrop.
21841      * @param {Roo.dd.DragDrop} target The drop target
21842      * @param {Event} e The event object
21843      * @param {String} id The id of the dragged element
21844      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
21845      */
21846     beforeDragDrop : function(target, e, id){
21847         return true;
21848     },
21849
21850     // private
21851     onValidDrop : function(target, e, id){
21852         this.hideProxy();
21853         if(this.afterValidDrop){
21854             /**
21855              * An empty function by default, but provided so that you can perform a custom action
21856              * after a valid drop has occurred by providing an implementation.
21857              * @param {Object} target The target DD 
21858              * @param {Event} e The event object
21859              * @param {String} id The id of the dropped element
21860              * @method afterInvalidDrop
21861              */
21862             this.afterValidDrop(target, e, id);
21863         }
21864     },
21865
21866     // private
21867     getRepairXY : function(e, data){
21868         return this.el.getXY();  
21869     },
21870
21871     // private
21872     onInvalidDrop : function(target, e, id){
21873         this.beforeInvalidDrop(target, e, id);
21874         if(this.cachedTarget){
21875             if(this.cachedTarget.isNotifyTarget){
21876                 this.cachedTarget.notifyOut(this, e, this.dragData);
21877             }
21878             this.cacheTarget = null;
21879         }
21880         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
21881
21882         if(this.afterInvalidDrop){
21883             /**
21884              * An empty function by default, but provided so that you can perform a custom action
21885              * after an invalid drop has occurred by providing an implementation.
21886              * @param {Event} e The event object
21887              * @param {String} id The id of the dropped element
21888              * @method afterInvalidDrop
21889              */
21890             this.afterInvalidDrop(e, id);
21891         }
21892     },
21893
21894     // private
21895     afterRepair : function(){
21896         if(Roo.enableFx){
21897             this.el.highlight(this.hlColor || "c3daf9");
21898         }
21899         this.dragging = false;
21900     },
21901
21902     /**
21903      * An empty function by default, but provided so that you can perform a custom action after an invalid
21904      * drop has occurred.
21905      * @param {Roo.dd.DragDrop} target The drop target
21906      * @param {Event} e The event object
21907      * @param {String} id The id of the dragged element
21908      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
21909      */
21910     beforeInvalidDrop : function(target, e, id){
21911         return true;
21912     },
21913
21914     // private
21915     handleMouseDown : function(e){
21916         if(this.dragging) {
21917             return;
21918         }
21919         var data = this.getDragData(e);
21920         if(data && this.onBeforeDrag(data, e) !== false){
21921             this.dragData = data;
21922             this.proxy.stop();
21923             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
21924         } 
21925     },
21926
21927     /**
21928      * An empty function by default, but provided so that you can perform a custom action before the initial
21929      * drag event begins and optionally cancel it.
21930      * @param {Object} data An object containing arbitrary data to be shared with drop targets
21931      * @param {Event} e The event object
21932      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
21933      */
21934     onBeforeDrag : function(data, e){
21935         return true;
21936     },
21937
21938     /**
21939      * An empty function by default, but provided so that you can perform a custom action once the initial
21940      * drag event has begun.  The drag cannot be canceled from this function.
21941      * @param {Number} x The x position of the click on the dragged object
21942      * @param {Number} y The y position of the click on the dragged object
21943      */
21944     onStartDrag : Roo.emptyFn,
21945
21946     // private - YUI override
21947     startDrag : function(x, y){
21948         this.proxy.reset();
21949         this.dragging = true;
21950         this.proxy.update("");
21951         this.onInitDrag(x, y);
21952         this.proxy.show();
21953     },
21954
21955     // private
21956     onInitDrag : function(x, y){
21957         var clone = this.el.dom.cloneNode(true);
21958         clone.id = Roo.id(); // prevent duplicate ids
21959         this.proxy.update(clone);
21960         this.onStartDrag(x, y);
21961         return true;
21962     },
21963
21964     /**
21965      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
21966      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
21967      */
21968     getProxy : function(){
21969         return this.proxy;  
21970     },
21971
21972     /**
21973      * Hides the drag source's {@link Roo.dd.StatusProxy}
21974      */
21975     hideProxy : function(){
21976         this.proxy.hide();  
21977         this.proxy.reset(true);
21978         this.dragging = false;
21979     },
21980
21981     // private
21982     triggerCacheRefresh : function(){
21983         Roo.dd.DDM.refreshCache(this.groups);
21984     },
21985
21986     // private - override to prevent hiding
21987     b4EndDrag: function(e) {
21988     },
21989
21990     // private - override to prevent moving
21991     endDrag : function(e){
21992         this.onEndDrag(this.dragData, e);
21993     },
21994
21995     // private
21996     onEndDrag : function(data, e){
21997     },
21998     
21999     // private - pin to cursor
22000     autoOffset : function(x, y) {
22001         this.setDelta(-12, -20);
22002     }    
22003 });/*
22004  * Based on:
22005  * Ext JS Library 1.1.1
22006  * Copyright(c) 2006-2007, Ext JS, LLC.
22007  *
22008  * Originally Released Under LGPL - original licence link has changed is not relivant.
22009  *
22010  * Fork - LGPL
22011  * <script type="text/javascript">
22012  */
22013
22014
22015 /**
22016  * @class Roo.dd.DropTarget
22017  * @extends Roo.dd.DDTarget
22018  * A simple class that provides the basic implementation needed to make any element a drop target that can have
22019  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
22020  * @constructor
22021  * @param {String/HTMLElement/Element} el The container element
22022  * @param {Object} config
22023  */
22024 Roo.dd.DropTarget = function(el, config){
22025     this.el = Roo.get(el);
22026     
22027     var listeners = false; ;
22028     if (config && config.listeners) {
22029         listeners= config.listeners;
22030         delete config.listeners;
22031     }
22032     Roo.apply(this, config);
22033     
22034     if(this.containerScroll){
22035         Roo.dd.ScrollManager.register(this.el);
22036     }
22037     this.addEvents( {
22038          /**
22039          * @scope Roo.dd.DropTarget
22040          */
22041          
22042          /**
22043          * @event enter
22044          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
22045          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
22046          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
22047          * 
22048          * IMPORTANT : it should set this.overClass and this.dropAllowed
22049          * 
22050          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
22051          * @param {Event} e The event
22052          * @param {Object} data An object containing arbitrary data supplied by the drag source
22053          */
22054         "enter" : true,
22055         
22056          /**
22057          * @event over
22058          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
22059          * This method will be called on every mouse movement while the drag source is over the drop target.
22060          * This default implementation simply returns the dropAllowed config value.
22061          * 
22062          * IMPORTANT : it should set this.dropAllowed
22063          * 
22064          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
22065          * @param {Event} e The event
22066          * @param {Object} data An object containing arbitrary data supplied by the drag source
22067          
22068          */
22069         "over" : true,
22070         /**
22071          * @event out
22072          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
22073          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
22074          * overClass (if any) from the drop element.
22075          * 
22076          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
22077          * @param {Event} e The event
22078          * @param {Object} data An object containing arbitrary data supplied by the drag source
22079          */
22080          "out" : true,
22081          
22082         /**
22083          * @event drop
22084          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
22085          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
22086          * implementation that does something to process the drop event and returns true so that the drag source's
22087          * repair action does not run.
22088          * 
22089          * IMPORTANT : it should set this.success
22090          * 
22091          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
22092          * @param {Event} e The event
22093          * @param {Object} data An object containing arbitrary data supplied by the drag source
22094         */
22095          "drop" : true
22096     });
22097             
22098      
22099     Roo.dd.DropTarget.superclass.constructor.call(  this, 
22100         this.el.dom, 
22101         this.ddGroup || this.group,
22102         {
22103             isTarget: true,
22104             listeners : listeners || {} 
22105            
22106         
22107         }
22108     );
22109
22110 };
22111
22112 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
22113     /**
22114      * @cfg {String} overClass
22115      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
22116      */
22117      /**
22118      * @cfg {String} ddGroup
22119      * The drag drop group to handle drop events for
22120      */
22121      
22122     /**
22123      * @cfg {String} dropAllowed
22124      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
22125      */
22126     dropAllowed : "x-dd-drop-ok",
22127     /**
22128      * @cfg {String} dropNotAllowed
22129      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
22130      */
22131     dropNotAllowed : "x-dd-drop-nodrop",
22132     /**
22133      * @cfg {boolean} success
22134      * set this after drop listener.. 
22135      */
22136     success : false,
22137     /**
22138      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
22139      * if the drop point is valid for over/enter..
22140      */
22141     valid : false,
22142     // private
22143     isTarget : true,
22144
22145     // private
22146     isNotifyTarget : true,
22147     
22148     /**
22149      * @hide
22150      */
22151     notifyEnter : function(dd, e, data)
22152     {
22153         this.valid = true;
22154         this.fireEvent('enter', dd, e, data);
22155         if(this.overClass){
22156             this.el.addClass(this.overClass);
22157         }
22158         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
22159             this.valid ? this.dropAllowed : this.dropNotAllowed
22160         );
22161     },
22162
22163     /**
22164      * @hide
22165      */
22166     notifyOver : function(dd, e, data)
22167     {
22168         this.valid = true;
22169         this.fireEvent('over', dd, e, data);
22170         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
22171             this.valid ? this.dropAllowed : this.dropNotAllowed
22172         );
22173     },
22174
22175     /**
22176      * @hide
22177      */
22178     notifyOut : function(dd, e, data)
22179     {
22180         this.fireEvent('out', dd, e, data);
22181         if(this.overClass){
22182             this.el.removeClass(this.overClass);
22183         }
22184     },
22185
22186     /**
22187      * @hide
22188      */
22189     notifyDrop : function(dd, e, data)
22190     {
22191         this.success = false;
22192         this.fireEvent('drop', dd, e, data);
22193         return this.success;
22194     }
22195 });/*
22196  * Based on:
22197  * Ext JS Library 1.1.1
22198  * Copyright(c) 2006-2007, Ext JS, LLC.
22199  *
22200  * Originally Released Under LGPL - original licence link has changed is not relivant.
22201  *
22202  * Fork - LGPL
22203  * <script type="text/javascript">
22204  */
22205
22206
22207 /**
22208  * @class Roo.dd.DragZone
22209  * @extends Roo.dd.DragSource
22210  * This class provides a container DD instance that proxies for multiple child node sources.<br />
22211  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
22212  * @constructor
22213  * @param {String/HTMLElement/Element} el The container element
22214  * @param {Object} config
22215  */
22216 Roo.dd.DragZone = function(el, config){
22217     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
22218     if(this.containerScroll){
22219         Roo.dd.ScrollManager.register(this.el);
22220     }
22221 };
22222
22223 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
22224     /**
22225      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
22226      * for auto scrolling during drag operations.
22227      */
22228     /**
22229      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
22230      * method after a failed drop (defaults to "c3daf9" - light blue)
22231      */
22232
22233     /**
22234      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
22235      * for a valid target to drag based on the mouse down. Override this method
22236      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
22237      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
22238      * @param {EventObject} e The mouse down event
22239      * @return {Object} The dragData
22240      */
22241     getDragData : function(e){
22242         return Roo.dd.Registry.getHandleFromEvent(e);
22243     },
22244     
22245     /**
22246      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
22247      * this.dragData.ddel
22248      * @param {Number} x The x position of the click on the dragged object
22249      * @param {Number} y The y position of the click on the dragged object
22250      * @return {Boolean} true to continue the drag, false to cancel
22251      */
22252     onInitDrag : function(x, y){
22253         this.proxy.update(this.dragData.ddel.cloneNode(true));
22254         this.onStartDrag(x, y);
22255         return true;
22256     },
22257     
22258     /**
22259      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
22260      */
22261     afterRepair : function(){
22262         if(Roo.enableFx){
22263             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
22264         }
22265         this.dragging = false;
22266     },
22267
22268     /**
22269      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
22270      * the XY of this.dragData.ddel
22271      * @param {EventObject} e The mouse up event
22272      * @return {Array} The xy location (e.g. [100, 200])
22273      */
22274     getRepairXY : function(e){
22275         return Roo.Element.fly(this.dragData.ddel).getXY();  
22276     }
22277 });/*
22278  * Based on:
22279  * Ext JS Library 1.1.1
22280  * Copyright(c) 2006-2007, Ext JS, LLC.
22281  *
22282  * Originally Released Under LGPL - original licence link has changed is not relivant.
22283  *
22284  * Fork - LGPL
22285  * <script type="text/javascript">
22286  */
22287 /**
22288  * @class Roo.dd.DropZone
22289  * @extends Roo.dd.DropTarget
22290  * This class provides a container DD instance that proxies for multiple child node targets.<br />
22291  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
22292  * @constructor
22293  * @param {String/HTMLElement/Element} el The container element
22294  * @param {Object} config
22295  */
22296 Roo.dd.DropZone = function(el, config){
22297     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
22298 };
22299
22300 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
22301     /**
22302      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
22303      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
22304      * provide your own custom lookup.
22305      * @param {Event} e The event
22306      * @return {Object} data The custom data
22307      */
22308     getTargetFromEvent : function(e){
22309         return Roo.dd.Registry.getTargetFromEvent(e);
22310     },
22311
22312     /**
22313      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
22314      * that it has registered.  This method has no default implementation and should be overridden to provide
22315      * node-specific processing if necessary.
22316      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
22317      * {@link #getTargetFromEvent} for this node)
22318      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22319      * @param {Event} e The event
22320      * @param {Object} data An object containing arbitrary data supplied by the drag source
22321      */
22322     onNodeEnter : function(n, dd, e, data){
22323         
22324     },
22325
22326     /**
22327      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
22328      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
22329      * overridden to provide the proper feedback.
22330      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
22331      * {@link #getTargetFromEvent} for this node)
22332      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22333      * @param {Event} e The event
22334      * @param {Object} data An object containing arbitrary data supplied by the drag source
22335      * @return {String} status The CSS class that communicates the drop status back to the source so that the
22336      * underlying {@link Roo.dd.StatusProxy} can be updated
22337      */
22338     onNodeOver : function(n, dd, e, data){
22339         return this.dropAllowed;
22340     },
22341
22342     /**
22343      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
22344      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
22345      * node-specific processing if necessary.
22346      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
22347      * {@link #getTargetFromEvent} for this node)
22348      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22349      * @param {Event} e The event
22350      * @param {Object} data An object containing arbitrary data supplied by the drag source
22351      */
22352     onNodeOut : function(n, dd, e, data){
22353         
22354     },
22355
22356     /**
22357      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
22358      * the drop node.  The default implementation returns false, so it should be overridden to provide the
22359      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
22360      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
22361      * {@link #getTargetFromEvent} for this node)
22362      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22363      * @param {Event} e The event
22364      * @param {Object} data An object containing arbitrary data supplied by the drag source
22365      * @return {Boolean} True if the drop was valid, else false
22366      */
22367     onNodeDrop : function(n, dd, e, data){
22368         return false;
22369     },
22370
22371     /**
22372      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
22373      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
22374      * it should be overridden to provide the proper feedback if necessary.
22375      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22376      * @param {Event} e The event
22377      * @param {Object} data An object containing arbitrary data supplied by the drag source
22378      * @return {String} status The CSS class that communicates the drop status back to the source so that the
22379      * underlying {@link Roo.dd.StatusProxy} can be updated
22380      */
22381     onContainerOver : function(dd, e, data){
22382         return this.dropNotAllowed;
22383     },
22384
22385     /**
22386      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
22387      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
22388      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
22389      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
22390      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22391      * @param {Event} e The event
22392      * @param {Object} data An object containing arbitrary data supplied by the drag source
22393      * @return {Boolean} True if the drop was valid, else false
22394      */
22395     onContainerDrop : function(dd, e, data){
22396         return false;
22397     },
22398
22399     /**
22400      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
22401      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
22402      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
22403      * you should override this method and provide a custom implementation.
22404      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22405      * @param {Event} e The event
22406      * @param {Object} data An object containing arbitrary data supplied by the drag source
22407      * @return {String} status The CSS class that communicates the drop status back to the source so that the
22408      * underlying {@link Roo.dd.StatusProxy} can be updated
22409      */
22410     notifyEnter : function(dd, e, data){
22411         return this.dropNotAllowed;
22412     },
22413
22414     /**
22415      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
22416      * This method will be called on every mouse movement while the drag source is over the drop zone.
22417      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
22418      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
22419      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
22420      * registered node, it will call {@link #onContainerOver}.
22421      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22422      * @param {Event} e The event
22423      * @param {Object} data An object containing arbitrary data supplied by the drag source
22424      * @return {String} status The CSS class that communicates the drop status back to the source so that the
22425      * underlying {@link Roo.dd.StatusProxy} can be updated
22426      */
22427     notifyOver : function(dd, e, data){
22428         var n = this.getTargetFromEvent(e);
22429         if(!n){ // not over valid drop target
22430             if(this.lastOverNode){
22431                 this.onNodeOut(this.lastOverNode, dd, e, data);
22432                 this.lastOverNode = null;
22433             }
22434             return this.onContainerOver(dd, e, data);
22435         }
22436         if(this.lastOverNode != n){
22437             if(this.lastOverNode){
22438                 this.onNodeOut(this.lastOverNode, dd, e, data);
22439             }
22440             this.onNodeEnter(n, dd, e, data);
22441             this.lastOverNode = n;
22442         }
22443         return this.onNodeOver(n, dd, e, data);
22444     },
22445
22446     /**
22447      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
22448      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
22449      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
22450      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
22451      * @param {Event} e The event
22452      * @param {Object} data An object containing arbitrary data supplied by the drag zone
22453      */
22454     notifyOut : function(dd, e, data){
22455         if(this.lastOverNode){
22456             this.onNodeOut(this.lastOverNode, dd, e, data);
22457             this.lastOverNode = null;
22458         }
22459     },
22460
22461     /**
22462      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
22463      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
22464      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
22465      * otherwise it will call {@link #onContainerDrop}.
22466      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22467      * @param {Event} e The event
22468      * @param {Object} data An object containing arbitrary data supplied by the drag source
22469      * @return {Boolean} True if the drop was valid, else false
22470      */
22471     notifyDrop : function(dd, e, data){
22472         if(this.lastOverNode){
22473             this.onNodeOut(this.lastOverNode, dd, e, data);
22474             this.lastOverNode = null;
22475         }
22476         var n = this.getTargetFromEvent(e);
22477         return n ?
22478             this.onNodeDrop(n, dd, e, data) :
22479             this.onContainerDrop(dd, e, data);
22480     },
22481
22482     // private
22483     triggerCacheRefresh : function(){
22484         Roo.dd.DDM.refreshCache(this.groups);
22485     }  
22486 });/*
22487  * Based on:
22488  * Ext JS Library 1.1.1
22489  * Copyright(c) 2006-2007, Ext JS, LLC.
22490  *
22491  * Originally Released Under LGPL - original licence link has changed is not relivant.
22492  *
22493  * Fork - LGPL
22494  * <script type="text/javascript">
22495  */
22496
22497
22498 /**
22499  * @class Roo.data.SortTypes
22500  * @singleton
22501  * Defines the default sorting (casting?) comparison functions used when sorting data.
22502  */
22503 Roo.data.SortTypes = {
22504     /**
22505      * Default sort that does nothing
22506      * @param {Mixed} s The value being converted
22507      * @return {Mixed} The comparison value
22508      */
22509     none : function(s){
22510         return s;
22511     },
22512     
22513     /**
22514      * The regular expression used to strip tags
22515      * @type {RegExp}
22516      * @property
22517      */
22518     stripTagsRE : /<\/?[^>]+>/gi,
22519     
22520     /**
22521      * Strips all HTML tags to sort on text only
22522      * @param {Mixed} s The value being converted
22523      * @return {String} The comparison value
22524      */
22525     asText : function(s){
22526         return String(s).replace(this.stripTagsRE, "");
22527     },
22528     
22529     /**
22530      * Strips all HTML tags to sort on text only - Case insensitive
22531      * @param {Mixed} s The value being converted
22532      * @return {String} The comparison value
22533      */
22534     asUCText : function(s){
22535         return String(s).toUpperCase().replace(this.stripTagsRE, "");
22536     },
22537     
22538     /**
22539      * Case insensitive string
22540      * @param {Mixed} s The value being converted
22541      * @return {String} The comparison value
22542      */
22543     asUCString : function(s) {
22544         return String(s).toUpperCase();
22545     },
22546     
22547     /**
22548      * Date sorting
22549      * @param {Mixed} s The value being converted
22550      * @return {Number} The comparison value
22551      */
22552     asDate : function(s) {
22553         if(!s){
22554             return 0;
22555         }
22556         if(s instanceof Date){
22557             return s.getTime();
22558         }
22559         return Date.parse(String(s));
22560     },
22561     
22562     /**
22563      * Float sorting
22564      * @param {Mixed} s The value being converted
22565      * @return {Float} The comparison value
22566      */
22567     asFloat : function(s) {
22568         var val = parseFloat(String(s).replace(/,/g, ""));
22569         if(isNaN(val)) {
22570             val = 0;
22571         }
22572         return val;
22573     },
22574     
22575     /**
22576      * Integer sorting
22577      * @param {Mixed} s The value being converted
22578      * @return {Number} The comparison value
22579      */
22580     asInt : function(s) {
22581         var val = parseInt(String(s).replace(/,/g, ""));
22582         if(isNaN(val)) {
22583             val = 0;
22584         }
22585         return val;
22586     }
22587 };/*
22588  * Based on:
22589  * Ext JS Library 1.1.1
22590  * Copyright(c) 2006-2007, Ext JS, LLC.
22591  *
22592  * Originally Released Under LGPL - original licence link has changed is not relivant.
22593  *
22594  * Fork - LGPL
22595  * <script type="text/javascript">
22596  */
22597
22598 /**
22599 * @class Roo.data.Record
22600  * Instances of this class encapsulate both record <em>definition</em> information, and record
22601  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
22602  * to access Records cached in an {@link Roo.data.Store} object.<br>
22603  * <p>
22604  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
22605  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
22606  * objects.<br>
22607  * <p>
22608  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
22609  * @constructor
22610  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
22611  * {@link #create}. The parameters are the same.
22612  * @param {Array} data An associative Array of data values keyed by the field name.
22613  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
22614  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
22615  * not specified an integer id is generated.
22616  */
22617 Roo.data.Record = function(data, id){
22618     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
22619     this.data = data;
22620 };
22621
22622 /**
22623  * Generate a constructor for a specific record layout.
22624  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
22625  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
22626  * Each field definition object may contain the following properties: <ul>
22627  * <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,
22628  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
22629  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
22630  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
22631  * is being used, then this is a string containing the javascript expression to reference the data relative to 
22632  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
22633  * to the data item relative to the record element. If the mapping expression is the same as the field name,
22634  * this may be omitted.</p></li>
22635  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
22636  * <ul><li>auto (Default, implies no conversion)</li>
22637  * <li>string</li>
22638  * <li>int</li>
22639  * <li>float</li>
22640  * <li>boolean</li>
22641  * <li>date</li></ul></p></li>
22642  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
22643  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
22644  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
22645  * by the Reader into an object that will be stored in the Record. It is passed the
22646  * following parameters:<ul>
22647  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
22648  * </ul></p></li>
22649  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
22650  * </ul>
22651  * <br>usage:<br><pre><code>
22652 var TopicRecord = Roo.data.Record.create(
22653     {name: 'title', mapping: 'topic_title'},
22654     {name: 'author', mapping: 'username'},
22655     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
22656     {name: 'lastPost', mapping: 'post_time', type: 'date'},
22657     {name: 'lastPoster', mapping: 'user2'},
22658     {name: 'excerpt', mapping: 'post_text'}
22659 );
22660
22661 var myNewRecord = new TopicRecord({
22662     title: 'Do my job please',
22663     author: 'noobie',
22664     totalPosts: 1,
22665     lastPost: new Date(),
22666     lastPoster: 'Animal',
22667     excerpt: 'No way dude!'
22668 });
22669 myStore.add(myNewRecord);
22670 </code></pre>
22671  * @method create
22672  * @static
22673  */
22674 Roo.data.Record.create = function(o){
22675     var f = function(){
22676         f.superclass.constructor.apply(this, arguments);
22677     };
22678     Roo.extend(f, Roo.data.Record);
22679     var p = f.prototype;
22680     p.fields = new Roo.util.MixedCollection(false, function(field){
22681         return field.name;
22682     });
22683     for(var i = 0, len = o.length; i < len; i++){
22684         p.fields.add(new Roo.data.Field(o[i]));
22685     }
22686     f.getField = function(name){
22687         return p.fields.get(name);  
22688     };
22689     return f;
22690 };
22691
22692 Roo.data.Record.AUTO_ID = 1000;
22693 Roo.data.Record.EDIT = 'edit';
22694 Roo.data.Record.REJECT = 'reject';
22695 Roo.data.Record.COMMIT = 'commit';
22696
22697 Roo.data.Record.prototype = {
22698     /**
22699      * Readonly flag - true if this record has been modified.
22700      * @type Boolean
22701      */
22702     dirty : false,
22703     editing : false,
22704     error: null,
22705     modified: null,
22706
22707     // private
22708     join : function(store){
22709         this.store = store;
22710     },
22711
22712     /**
22713      * Set the named field to the specified value.
22714      * @param {String} name The name of the field to set.
22715      * @param {Object} value The value to set the field to.
22716      */
22717     set : function(name, value){
22718         if(this.data[name] == value){
22719             return;
22720         }
22721         this.dirty = true;
22722         if(!this.modified){
22723             this.modified = {};
22724         }
22725         if(typeof this.modified[name] == 'undefined'){
22726             this.modified[name] = this.data[name];
22727         }
22728         this.data[name] = value;
22729         if(!this.editing && this.store){
22730             this.store.afterEdit(this);
22731         }       
22732     },
22733
22734     /**
22735      * Get the value of the named field.
22736      * @param {String} name The name of the field to get the value of.
22737      * @return {Object} The value of the field.
22738      */
22739     get : function(name){
22740         return this.data[name]; 
22741     },
22742
22743     // private
22744     beginEdit : function(){
22745         this.editing = true;
22746         this.modified = {}; 
22747     },
22748
22749     // private
22750     cancelEdit : function(){
22751         this.editing = false;
22752         delete this.modified;
22753     },
22754
22755     // private
22756     endEdit : function(){
22757         this.editing = false;
22758         if(this.dirty && this.store){
22759             this.store.afterEdit(this);
22760         }
22761     },
22762
22763     /**
22764      * Usually called by the {@link Roo.data.Store} which owns the Record.
22765      * Rejects all changes made to the Record since either creation, or the last commit operation.
22766      * Modified fields are reverted to their original values.
22767      * <p>
22768      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
22769      * of reject operations.
22770      */
22771     reject : function(){
22772         var m = this.modified;
22773         for(var n in m){
22774             if(typeof m[n] != "function"){
22775                 this.data[n] = m[n];
22776             }
22777         }
22778         this.dirty = false;
22779         delete this.modified;
22780         this.editing = false;
22781         if(this.store){
22782             this.store.afterReject(this);
22783         }
22784     },
22785
22786     /**
22787      * Usually called by the {@link Roo.data.Store} which owns the Record.
22788      * Commits all changes made to the Record since either creation, or the last commit operation.
22789      * <p>
22790      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
22791      * of commit operations.
22792      */
22793     commit : function(){
22794         this.dirty = false;
22795         delete this.modified;
22796         this.editing = false;
22797         if(this.store){
22798             this.store.afterCommit(this);
22799         }
22800     },
22801
22802     // private
22803     hasError : function(){
22804         return this.error != null;
22805     },
22806
22807     // private
22808     clearError : function(){
22809         this.error = null;
22810     },
22811
22812     /**
22813      * Creates a copy of this record.
22814      * @param {String} id (optional) A new record id if you don't want to use this record's id
22815      * @return {Record}
22816      */
22817     copy : function(newId) {
22818         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
22819     }
22820 };/*
22821  * Based on:
22822  * Ext JS Library 1.1.1
22823  * Copyright(c) 2006-2007, Ext JS, LLC.
22824  *
22825  * Originally Released Under LGPL - original licence link has changed is not relivant.
22826  *
22827  * Fork - LGPL
22828  * <script type="text/javascript">
22829  */
22830
22831
22832
22833 /**
22834  * @class Roo.data.Store
22835  * @extends Roo.util.Observable
22836  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
22837  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
22838  * <p>
22839  * 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
22840  * has no knowledge of the format of the data returned by the Proxy.<br>
22841  * <p>
22842  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
22843  * instances from the data object. These records are cached and made available through accessor functions.
22844  * @constructor
22845  * Creates a new Store.
22846  * @param {Object} config A config object containing the objects needed for the Store to access data,
22847  * and read the data into Records.
22848  */
22849 Roo.data.Store = function(config){
22850     this.data = new Roo.util.MixedCollection(false);
22851     this.data.getKey = function(o){
22852         return o.id;
22853     };
22854     this.baseParams = {};
22855     // private
22856     this.paramNames = {
22857         "start" : "start",
22858         "limit" : "limit",
22859         "sort" : "sort",
22860         "dir" : "dir",
22861         "multisort" : "_multisort"
22862     };
22863
22864     if(config && config.data){
22865         this.inlineData = config.data;
22866         delete config.data;
22867     }
22868
22869     Roo.apply(this, config);
22870     
22871     if(this.reader){ // reader passed
22872         this.reader = Roo.factory(this.reader, Roo.data);
22873         this.reader.xmodule = this.xmodule || false;
22874         if(!this.recordType){
22875             this.recordType = this.reader.recordType;
22876         }
22877         if(this.reader.onMetaChange){
22878             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
22879         }
22880     }
22881
22882     if(this.recordType){
22883         this.fields = this.recordType.prototype.fields;
22884     }
22885     this.modified = [];
22886
22887     this.addEvents({
22888         /**
22889          * @event datachanged
22890          * Fires when the data cache has changed, and a widget which is using this Store
22891          * as a Record cache should refresh its view.
22892          * @param {Store} this
22893          */
22894         datachanged : true,
22895         /**
22896          * @event metachange
22897          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
22898          * @param {Store} this
22899          * @param {Object} meta The JSON metadata
22900          */
22901         metachange : true,
22902         /**
22903          * @event add
22904          * Fires when Records have been added to the Store
22905          * @param {Store} this
22906          * @param {Roo.data.Record[]} records The array of Records added
22907          * @param {Number} index The index at which the record(s) were added
22908          */
22909         add : true,
22910         /**
22911          * @event remove
22912          * Fires when a Record has been removed from the Store
22913          * @param {Store} this
22914          * @param {Roo.data.Record} record The Record that was removed
22915          * @param {Number} index The index at which the record was removed
22916          */
22917         remove : true,
22918         /**
22919          * @event update
22920          * Fires when a Record has been updated
22921          * @param {Store} this
22922          * @param {Roo.data.Record} record The Record that was updated
22923          * @param {String} operation The update operation being performed.  Value may be one of:
22924          * <pre><code>
22925  Roo.data.Record.EDIT
22926  Roo.data.Record.REJECT
22927  Roo.data.Record.COMMIT
22928          * </code></pre>
22929          */
22930         update : true,
22931         /**
22932          * @event clear
22933          * Fires when the data cache has been cleared.
22934          * @param {Store} this
22935          */
22936         clear : true,
22937         /**
22938          * @event beforeload
22939          * Fires before a request is made for a new data object.  If the beforeload handler returns false
22940          * the load action will be canceled.
22941          * @param {Store} this
22942          * @param {Object} options The loading options that were specified (see {@link #load} for details)
22943          */
22944         beforeload : true,
22945         /**
22946          * @event beforeloadadd
22947          * Fires after a new set of Records has been loaded.
22948          * @param {Store} this
22949          * @param {Roo.data.Record[]} records The Records that were loaded
22950          * @param {Object} options The loading options that were specified (see {@link #load} for details)
22951          */
22952         beforeloadadd : true,
22953         /**
22954          * @event load
22955          * Fires after a new set of Records has been loaded, before they are added to the store.
22956          * @param {Store} this
22957          * @param {Roo.data.Record[]} records The Records that were loaded
22958          * @param {Object} options The loading options that were specified (see {@link #load} for details)
22959          * @params {Object} return from reader
22960          */
22961         load : true,
22962         /**
22963          * @event loadexception
22964          * Fires if an exception occurs in the Proxy during loading.
22965          * Called with the signature of the Proxy's "loadexception" event.
22966          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
22967          * 
22968          * @param {Proxy} 
22969          * @param {Object} return from JsonData.reader() - success, totalRecords, records
22970          * @param {Object} load options 
22971          * @param {Object} jsonData from your request (normally this contains the Exception)
22972          */
22973         loadexception : true
22974     });
22975     
22976     if(this.proxy){
22977         this.proxy = Roo.factory(this.proxy, Roo.data);
22978         this.proxy.xmodule = this.xmodule || false;
22979         this.relayEvents(this.proxy,  ["loadexception"]);
22980     }
22981     this.sortToggle = {};
22982     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
22983
22984     Roo.data.Store.superclass.constructor.call(this);
22985
22986     if(this.inlineData){
22987         this.loadData(this.inlineData);
22988         delete this.inlineData;
22989     }
22990 };
22991
22992 Roo.extend(Roo.data.Store, Roo.util.Observable, {
22993      /**
22994     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
22995     * without a remote query - used by combo/forms at present.
22996     */
22997     
22998     /**
22999     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
23000     */
23001     /**
23002     * @cfg {Array} data Inline data to be loaded when the store is initialized.
23003     */
23004     /**
23005     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
23006     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
23007     */
23008     /**
23009     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
23010     * on any HTTP request
23011     */
23012     /**
23013     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
23014     */
23015     /**
23016     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
23017     */
23018     multiSort: false,
23019     /**
23020     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
23021     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
23022     */
23023     remoteSort : false,
23024
23025     /**
23026     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
23027      * loaded or when a record is removed. (defaults to false).
23028     */
23029     pruneModifiedRecords : false,
23030
23031     // private
23032     lastOptions : null,
23033
23034     /**
23035      * Add Records to the Store and fires the add event.
23036      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
23037      */
23038     add : function(records){
23039         records = [].concat(records);
23040         for(var i = 0, len = records.length; i < len; i++){
23041             records[i].join(this);
23042         }
23043         var index = this.data.length;
23044         this.data.addAll(records);
23045         this.fireEvent("add", this, records, index);
23046     },
23047
23048     /**
23049      * Remove a Record from the Store and fires the remove event.
23050      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
23051      */
23052     remove : function(record){
23053         var index = this.data.indexOf(record);
23054         this.data.removeAt(index);
23055  
23056         if(this.pruneModifiedRecords){
23057             this.modified.remove(record);
23058         }
23059         this.fireEvent("remove", this, record, index);
23060     },
23061
23062     /**
23063      * Remove all Records from the Store and fires the clear event.
23064      */
23065     removeAll : function(){
23066         this.data.clear();
23067         if(this.pruneModifiedRecords){
23068             this.modified = [];
23069         }
23070         this.fireEvent("clear", this);
23071     },
23072
23073     /**
23074      * Inserts Records to the Store at the given index and fires the add event.
23075      * @param {Number} index The start index at which to insert the passed Records.
23076      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
23077      */
23078     insert : function(index, records){
23079         records = [].concat(records);
23080         for(var i = 0, len = records.length; i < len; i++){
23081             this.data.insert(index, records[i]);
23082             records[i].join(this);
23083         }
23084         this.fireEvent("add", this, records, index);
23085     },
23086
23087     /**
23088      * Get the index within the cache of the passed Record.
23089      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
23090      * @return {Number} The index of the passed Record. Returns -1 if not found.
23091      */
23092     indexOf : function(record){
23093         return this.data.indexOf(record);
23094     },
23095
23096     /**
23097      * Get the index within the cache of the Record with the passed id.
23098      * @param {String} id The id of the Record to find.
23099      * @return {Number} The index of the Record. Returns -1 if not found.
23100      */
23101     indexOfId : function(id){
23102         return this.data.indexOfKey(id);
23103     },
23104
23105     /**
23106      * Get the Record with the specified id.
23107      * @param {String} id The id of the Record to find.
23108      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
23109      */
23110     getById : function(id){
23111         return this.data.key(id);
23112     },
23113
23114     /**
23115      * Get the Record at the specified index.
23116      * @param {Number} index The index of the Record to find.
23117      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
23118      */
23119     getAt : function(index){
23120         return this.data.itemAt(index);
23121     },
23122
23123     /**
23124      * Returns a range of Records between specified indices.
23125      * @param {Number} startIndex (optional) The starting index (defaults to 0)
23126      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
23127      * @return {Roo.data.Record[]} An array of Records
23128      */
23129     getRange : function(start, end){
23130         return this.data.getRange(start, end);
23131     },
23132
23133     // private
23134     storeOptions : function(o){
23135         o = Roo.apply({}, o);
23136         delete o.callback;
23137         delete o.scope;
23138         this.lastOptions = o;
23139     },
23140
23141     /**
23142      * Loads the Record cache from the configured Proxy using the configured Reader.
23143      * <p>
23144      * If using remote paging, then the first load call must specify the <em>start</em>
23145      * and <em>limit</em> properties in the options.params property to establish the initial
23146      * position within the dataset, and the number of Records to cache on each read from the Proxy.
23147      * <p>
23148      * <strong>It is important to note that for remote data sources, loading is asynchronous,
23149      * and this call will return before the new data has been loaded. Perform any post-processing
23150      * in a callback function, or in a "load" event handler.</strong>
23151      * <p>
23152      * @param {Object} options An object containing properties which control loading options:<ul>
23153      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
23154      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
23155      * passed the following arguments:<ul>
23156      * <li>r : Roo.data.Record[]</li>
23157      * <li>options: Options object from the load call</li>
23158      * <li>success: Boolean success indicator</li></ul></li>
23159      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
23160      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
23161      * </ul>
23162      */
23163     load : function(options){
23164         options = options || {};
23165         if(this.fireEvent("beforeload", this, options) !== false){
23166             this.storeOptions(options);
23167             var p = Roo.apply(options.params || {}, this.baseParams);
23168             // if meta was not loaded from remote source.. try requesting it.
23169             if (!this.reader.metaFromRemote) {
23170                 p._requestMeta = 1;
23171             }
23172             if(this.sortInfo && this.remoteSort){
23173                 var pn = this.paramNames;
23174                 p[pn["sort"]] = this.sortInfo.field;
23175                 p[pn["dir"]] = this.sortInfo.direction;
23176             }
23177             if (this.multiSort) {
23178                 var pn = this.paramNames;
23179                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
23180             }
23181             
23182             this.proxy.load(p, this.reader, this.loadRecords, this, options);
23183         }
23184     },
23185
23186     /**
23187      * Reloads the Record cache from the configured Proxy using the configured Reader and
23188      * the options from the last load operation performed.
23189      * @param {Object} options (optional) An object containing properties which may override the options
23190      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
23191      * the most recently used options are reused).
23192      */
23193     reload : function(options){
23194         this.load(Roo.applyIf(options||{}, this.lastOptions));
23195     },
23196
23197     // private
23198     // Called as a callback by the Reader during a load operation.
23199     loadRecords : function(o, options, success){
23200         if(!o || success === false){
23201             if(success !== false){
23202                 this.fireEvent("load", this, [], options, o);
23203             }
23204             if(options.callback){
23205                 options.callback.call(options.scope || this, [], options, false);
23206             }
23207             return;
23208         }
23209         // if data returned failure - throw an exception.
23210         if (o.success === false) {
23211             // show a message if no listener is registered.
23212             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
23213                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
23214             }
23215             // loadmask wil be hooked into this..
23216             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
23217             return;
23218         }
23219         var r = o.records, t = o.totalRecords || r.length;
23220         
23221         this.fireEvent("beforeloadadd", this, r, options, o);
23222         
23223         if(!options || options.add !== true){
23224             if(this.pruneModifiedRecords){
23225                 this.modified = [];
23226             }
23227             for(var i = 0, len = r.length; i < len; i++){
23228                 r[i].join(this);
23229             }
23230             if(this.snapshot){
23231                 this.data = this.snapshot;
23232                 delete this.snapshot;
23233             }
23234             this.data.clear();
23235             this.data.addAll(r);
23236             this.totalLength = t;
23237             this.applySort();
23238             this.fireEvent("datachanged", this);
23239         }else{
23240             this.totalLength = Math.max(t, this.data.length+r.length);
23241             this.add(r);
23242         }
23243         
23244         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
23245                 
23246             var e = new Roo.data.Record({});
23247
23248             e.set(this.parent.displayField, this.parent.emptyTitle);
23249             e.set(this.parent.valueField, '');
23250
23251             this.insert(0, e);
23252         }
23253             
23254         this.fireEvent("load", this, r, options, o);
23255         if(options.callback){
23256             options.callback.call(options.scope || this, r, options, true);
23257         }
23258     },
23259
23260
23261     /**
23262      * Loads data from a passed data block. A Reader which understands the format of the data
23263      * must have been configured in the constructor.
23264      * @param {Object} data The data block from which to read the Records.  The format of the data expected
23265      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
23266      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
23267      */
23268     loadData : function(o, append){
23269         var r = this.reader.readRecords(o);
23270         this.loadRecords(r, {add: append}, true);
23271     },
23272     
23273      /**
23274      * using 'cn' the nested child reader read the child array into it's child stores.
23275      * @param {Object} rec The record with a 'children array
23276      */
23277     loadDataFromChildren : function(rec)
23278     {
23279         this.loadData(this.reader.toLoadData(rec));
23280     },
23281     
23282
23283     /**
23284      * Gets the number of cached records.
23285      * <p>
23286      * <em>If using paging, this may not be the total size of the dataset. If the data object
23287      * used by the Reader contains the dataset size, then the getTotalCount() function returns
23288      * the data set size</em>
23289      */
23290     getCount : function(){
23291         return this.data.length || 0;
23292     },
23293
23294     /**
23295      * Gets the total number of records in the dataset as returned by the server.
23296      * <p>
23297      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
23298      * the dataset size</em>
23299      */
23300     getTotalCount : function(){
23301         return this.totalLength || 0;
23302     },
23303
23304     /**
23305      * Returns the sort state of the Store as an object with two properties:
23306      * <pre><code>
23307  field {String} The name of the field by which the Records are sorted
23308  direction {String} The sort order, "ASC" or "DESC"
23309      * </code></pre>
23310      */
23311     getSortState : function(){
23312         return this.sortInfo;
23313     },
23314
23315     // private
23316     applySort : function(){
23317         if(this.sortInfo && !this.remoteSort){
23318             var s = this.sortInfo, f = s.field;
23319             var st = this.fields.get(f).sortType;
23320             var fn = function(r1, r2){
23321                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
23322                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
23323             };
23324             this.data.sort(s.direction, fn);
23325             if(this.snapshot && this.snapshot != this.data){
23326                 this.snapshot.sort(s.direction, fn);
23327             }
23328         }
23329     },
23330
23331     /**
23332      * Sets the default sort column and order to be used by the next load operation.
23333      * @param {String} fieldName The name of the field to sort by.
23334      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
23335      */
23336     setDefaultSort : function(field, dir){
23337         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
23338     },
23339
23340     /**
23341      * Sort the Records.
23342      * If remote sorting is used, the sort is performed on the server, and the cache is
23343      * reloaded. If local sorting is used, the cache is sorted internally.
23344      * @param {String} fieldName The name of the field to sort by.
23345      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
23346      */
23347     sort : function(fieldName, dir){
23348         var f = this.fields.get(fieldName);
23349         if(!dir){
23350             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
23351             
23352             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
23353                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
23354             }else{
23355                 dir = f.sortDir;
23356             }
23357         }
23358         this.sortToggle[f.name] = dir;
23359         this.sortInfo = {field: f.name, direction: dir};
23360         if(!this.remoteSort){
23361             this.applySort();
23362             this.fireEvent("datachanged", this);
23363         }else{
23364             this.load(this.lastOptions);
23365         }
23366     },
23367
23368     /**
23369      * Calls the specified function for each of the Records in the cache.
23370      * @param {Function} fn The function to call. The Record is passed as the first parameter.
23371      * Returning <em>false</em> aborts and exits the iteration.
23372      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
23373      */
23374     each : function(fn, scope){
23375         this.data.each(fn, scope);
23376     },
23377
23378     /**
23379      * Gets all records modified since the last commit.  Modified records are persisted across load operations
23380      * (e.g., during paging).
23381      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
23382      */
23383     getModifiedRecords : function(){
23384         return this.modified;
23385     },
23386
23387     // private
23388     createFilterFn : function(property, value, anyMatch){
23389         if(!value.exec){ // not a regex
23390             value = String(value);
23391             if(value.length == 0){
23392                 return false;
23393             }
23394             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
23395         }
23396         return function(r){
23397             return value.test(r.data[property]);
23398         };
23399     },
23400
23401     /**
23402      * Sums the value of <i>property</i> for each record between start and end and returns the result.
23403      * @param {String} property A field on your records
23404      * @param {Number} start The record index to start at (defaults to 0)
23405      * @param {Number} end The last record index to include (defaults to length - 1)
23406      * @return {Number} The sum
23407      */
23408     sum : function(property, start, end){
23409         var rs = this.data.items, v = 0;
23410         start = start || 0;
23411         end = (end || end === 0) ? end : rs.length-1;
23412
23413         for(var i = start; i <= end; i++){
23414             v += (rs[i].data[property] || 0);
23415         }
23416         return v;
23417     },
23418
23419     /**
23420      * Filter the records by a specified property.
23421      * @param {String} field A field on your records
23422      * @param {String/RegExp} value Either a string that the field
23423      * should start with or a RegExp to test against the field
23424      * @param {Boolean} anyMatch True to match any part not just the beginning
23425      */
23426     filter : function(property, value, anyMatch){
23427         var fn = this.createFilterFn(property, value, anyMatch);
23428         return fn ? this.filterBy(fn) : this.clearFilter();
23429     },
23430
23431     /**
23432      * Filter by a function. The specified function will be called with each
23433      * record in this data source. If the function returns true the record is included,
23434      * otherwise it is filtered.
23435      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
23436      * @param {Object} scope (optional) The scope of the function (defaults to this)
23437      */
23438     filterBy : function(fn, scope){
23439         this.snapshot = this.snapshot || this.data;
23440         this.data = this.queryBy(fn, scope||this);
23441         this.fireEvent("datachanged", this);
23442     },
23443
23444     /**
23445      * Query the records by a specified property.
23446      * @param {String} field A field on your records
23447      * @param {String/RegExp} value Either a string that the field
23448      * should start with or a RegExp to test against the field
23449      * @param {Boolean} anyMatch True to match any part not just the beginning
23450      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
23451      */
23452     query : function(property, value, anyMatch){
23453         var fn = this.createFilterFn(property, value, anyMatch);
23454         return fn ? this.queryBy(fn) : this.data.clone();
23455     },
23456
23457     /**
23458      * Query by a function. The specified function will be called with each
23459      * record in this data source. If the function returns true the record is included
23460      * in the results.
23461      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
23462      * @param {Object} scope (optional) The scope of the function (defaults to this)
23463       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
23464      **/
23465     queryBy : function(fn, scope){
23466         var data = this.snapshot || this.data;
23467         return data.filterBy(fn, scope||this);
23468     },
23469
23470     /**
23471      * Collects unique values for a particular dataIndex from this store.
23472      * @param {String} dataIndex The property to collect
23473      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
23474      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
23475      * @return {Array} An array of the unique values
23476      **/
23477     collect : function(dataIndex, allowNull, bypassFilter){
23478         var d = (bypassFilter === true && this.snapshot) ?
23479                 this.snapshot.items : this.data.items;
23480         var v, sv, r = [], l = {};
23481         for(var i = 0, len = d.length; i < len; i++){
23482             v = d[i].data[dataIndex];
23483             sv = String(v);
23484             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
23485                 l[sv] = true;
23486                 r[r.length] = v;
23487             }
23488         }
23489         return r;
23490     },
23491
23492     /**
23493      * Revert to a view of the Record cache with no filtering applied.
23494      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
23495      */
23496     clearFilter : function(suppressEvent){
23497         if(this.snapshot && this.snapshot != this.data){
23498             this.data = this.snapshot;
23499             delete this.snapshot;
23500             if(suppressEvent !== true){
23501                 this.fireEvent("datachanged", this);
23502             }
23503         }
23504     },
23505
23506     // private
23507     afterEdit : function(record){
23508         if(this.modified.indexOf(record) == -1){
23509             this.modified.push(record);
23510         }
23511         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
23512     },
23513     
23514     // private
23515     afterReject : function(record){
23516         this.modified.remove(record);
23517         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
23518     },
23519
23520     // private
23521     afterCommit : function(record){
23522         this.modified.remove(record);
23523         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
23524     },
23525
23526     /**
23527      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
23528      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
23529      */
23530     commitChanges : function(){
23531         var m = this.modified.slice(0);
23532         this.modified = [];
23533         for(var i = 0, len = m.length; i < len; i++){
23534             m[i].commit();
23535         }
23536     },
23537
23538     /**
23539      * Cancel outstanding changes on all changed records.
23540      */
23541     rejectChanges : function(){
23542         var m = this.modified.slice(0);
23543         this.modified = [];
23544         for(var i = 0, len = m.length; i < len; i++){
23545             m[i].reject();
23546         }
23547     },
23548
23549     onMetaChange : function(meta, rtype, o){
23550         this.recordType = rtype;
23551         this.fields = rtype.prototype.fields;
23552         delete this.snapshot;
23553         this.sortInfo = meta.sortInfo || this.sortInfo;
23554         this.modified = [];
23555         this.fireEvent('metachange', this, this.reader.meta);
23556     },
23557     
23558     moveIndex : function(data, type)
23559     {
23560         var index = this.indexOf(data);
23561         
23562         var newIndex = index + type;
23563         
23564         this.remove(data);
23565         
23566         this.insert(newIndex, data);
23567         
23568     }
23569 });/*
23570  * Based on:
23571  * Ext JS Library 1.1.1
23572  * Copyright(c) 2006-2007, Ext JS, LLC.
23573  *
23574  * Originally Released Under LGPL - original licence link has changed is not relivant.
23575  *
23576  * Fork - LGPL
23577  * <script type="text/javascript">
23578  */
23579
23580 /**
23581  * @class Roo.data.SimpleStore
23582  * @extends Roo.data.Store
23583  * Small helper class to make creating Stores from Array data easier.
23584  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
23585  * @cfg {Array} fields An array of field definition objects, or field name strings.
23586  * @cfg {Object} an existing reader (eg. copied from another store)
23587  * @cfg {Array} data The multi-dimensional array of data
23588  * @constructor
23589  * @param {Object} config
23590  */
23591 Roo.data.SimpleStore = function(config)
23592 {
23593     Roo.data.SimpleStore.superclass.constructor.call(this, {
23594         isLocal : true,
23595         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
23596                 id: config.id
23597             },
23598             Roo.data.Record.create(config.fields)
23599         ),
23600         proxy : new Roo.data.MemoryProxy(config.data)
23601     });
23602     this.load();
23603 };
23604 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
23605  * Based on:
23606  * Ext JS Library 1.1.1
23607  * Copyright(c) 2006-2007, Ext JS, LLC.
23608  *
23609  * Originally Released Under LGPL - original licence link has changed is not relivant.
23610  *
23611  * Fork - LGPL
23612  * <script type="text/javascript">
23613  */
23614
23615 /**
23616 /**
23617  * @extends Roo.data.Store
23618  * @class Roo.data.JsonStore
23619  * Small helper class to make creating Stores for JSON data easier. <br/>
23620 <pre><code>
23621 var store = new Roo.data.JsonStore({
23622     url: 'get-images.php',
23623     root: 'images',
23624     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
23625 });
23626 </code></pre>
23627  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
23628  * JsonReader and HttpProxy (unless inline data is provided).</b>
23629  * @cfg {Array} fields An array of field definition objects, or field name strings.
23630  * @constructor
23631  * @param {Object} config
23632  */
23633 Roo.data.JsonStore = function(c){
23634     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
23635         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
23636         reader: new Roo.data.JsonReader(c, c.fields)
23637     }));
23638 };
23639 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
23640  * Based on:
23641  * Ext JS Library 1.1.1
23642  * Copyright(c) 2006-2007, Ext JS, LLC.
23643  *
23644  * Originally Released Under LGPL - original licence link has changed is not relivant.
23645  *
23646  * Fork - LGPL
23647  * <script type="text/javascript">
23648  */
23649
23650  
23651 Roo.data.Field = function(config){
23652     if(typeof config == "string"){
23653         config = {name: config};
23654     }
23655     Roo.apply(this, config);
23656     
23657     if(!this.type){
23658         this.type = "auto";
23659     }
23660     
23661     var st = Roo.data.SortTypes;
23662     // named sortTypes are supported, here we look them up
23663     if(typeof this.sortType == "string"){
23664         this.sortType = st[this.sortType];
23665     }
23666     
23667     // set default sortType for strings and dates
23668     if(!this.sortType){
23669         switch(this.type){
23670             case "string":
23671                 this.sortType = st.asUCString;
23672                 break;
23673             case "date":
23674                 this.sortType = st.asDate;
23675                 break;
23676             default:
23677                 this.sortType = st.none;
23678         }
23679     }
23680
23681     // define once
23682     var stripRe = /[\$,%]/g;
23683
23684     // prebuilt conversion function for this field, instead of
23685     // switching every time we're reading a value
23686     if(!this.convert){
23687         var cv, dateFormat = this.dateFormat;
23688         switch(this.type){
23689             case "":
23690             case "auto":
23691             case undefined:
23692                 cv = function(v){ return v; };
23693                 break;
23694             case "string":
23695                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
23696                 break;
23697             case "int":
23698                 cv = function(v){
23699                     return v !== undefined && v !== null && v !== '' ?
23700                            parseInt(String(v).replace(stripRe, ""), 10) : '';
23701                     };
23702                 break;
23703             case "float":
23704                 cv = function(v){
23705                     return v !== undefined && v !== null && v !== '' ?
23706                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
23707                     };
23708                 break;
23709             case "bool":
23710             case "boolean":
23711                 cv = function(v){ return v === true || v === "true" || v == 1; };
23712                 break;
23713             case "date":
23714                 cv = function(v){
23715                     if(!v){
23716                         return '';
23717                     }
23718                     if(v instanceof Date){
23719                         return v;
23720                     }
23721                     if(dateFormat){
23722                         if(dateFormat == "timestamp"){
23723                             return new Date(v*1000);
23724                         }
23725                         return Date.parseDate(v, dateFormat);
23726                     }
23727                     var parsed = Date.parse(v);
23728                     return parsed ? new Date(parsed) : null;
23729                 };
23730              break;
23731             
23732         }
23733         this.convert = cv;
23734     }
23735 };
23736
23737 Roo.data.Field.prototype = {
23738     dateFormat: null,
23739     defaultValue: "",
23740     mapping: null,
23741     sortType : null,
23742     sortDir : "ASC"
23743 };/*
23744  * Based on:
23745  * Ext JS Library 1.1.1
23746  * Copyright(c) 2006-2007, Ext JS, LLC.
23747  *
23748  * Originally Released Under LGPL - original licence link has changed is not relivant.
23749  *
23750  * Fork - LGPL
23751  * <script type="text/javascript">
23752  */
23753  
23754 // Base class for reading structured data from a data source.  This class is intended to be
23755 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
23756
23757 /**
23758  * @class Roo.data.DataReader
23759  * Base class for reading structured data from a data source.  This class is intended to be
23760  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
23761  */
23762
23763 Roo.data.DataReader = function(meta, recordType){
23764     
23765     this.meta = meta;
23766     
23767     this.recordType = recordType instanceof Array ? 
23768         Roo.data.Record.create(recordType) : recordType;
23769 };
23770
23771 Roo.data.DataReader.prototype = {
23772     
23773     
23774     readerType : 'Data',
23775      /**
23776      * Create an empty record
23777      * @param {Object} data (optional) - overlay some values
23778      * @return {Roo.data.Record} record created.
23779      */
23780     newRow :  function(d) {
23781         var da =  {};
23782         this.recordType.prototype.fields.each(function(c) {
23783             switch( c.type) {
23784                 case 'int' : da[c.name] = 0; break;
23785                 case 'date' : da[c.name] = new Date(); break;
23786                 case 'float' : da[c.name] = 0.0; break;
23787                 case 'boolean' : da[c.name] = false; break;
23788                 default : da[c.name] = ""; break;
23789             }
23790             
23791         });
23792         return new this.recordType(Roo.apply(da, d));
23793     }
23794     
23795     
23796 };/*
23797  * Based on:
23798  * Ext JS Library 1.1.1
23799  * Copyright(c) 2006-2007, Ext JS, LLC.
23800  *
23801  * Originally Released Under LGPL - original licence link has changed is not relivant.
23802  *
23803  * Fork - LGPL
23804  * <script type="text/javascript">
23805  */
23806
23807 /**
23808  * @class Roo.data.DataProxy
23809  * @extends Roo.data.Observable
23810  * This class is an abstract base class for implementations which provide retrieval of
23811  * unformatted data objects.<br>
23812  * <p>
23813  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
23814  * (of the appropriate type which knows how to parse the data object) to provide a block of
23815  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
23816  * <p>
23817  * Custom implementations must implement the load method as described in
23818  * {@link Roo.data.HttpProxy#load}.
23819  */
23820 Roo.data.DataProxy = function(){
23821     this.addEvents({
23822         /**
23823          * @event beforeload
23824          * Fires before a network request is made to retrieve a data object.
23825          * @param {Object} This DataProxy object.
23826          * @param {Object} params The params parameter to the load function.
23827          */
23828         beforeload : true,
23829         /**
23830          * @event load
23831          * Fires before the load method's callback is called.
23832          * @param {Object} This DataProxy object.
23833          * @param {Object} o The data object.
23834          * @param {Object} arg The callback argument object passed to the load function.
23835          */
23836         load : true,
23837         /**
23838          * @event loadexception
23839          * Fires if an Exception occurs during data retrieval.
23840          * @param {Object} This DataProxy object.
23841          * @param {Object} o The data object.
23842          * @param {Object} arg The callback argument object passed to the load function.
23843          * @param {Object} e The Exception.
23844          */
23845         loadexception : true
23846     });
23847     Roo.data.DataProxy.superclass.constructor.call(this);
23848 };
23849
23850 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
23851
23852     /**
23853      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
23854      */
23855 /*
23856  * Based on:
23857  * Ext JS Library 1.1.1
23858  * Copyright(c) 2006-2007, Ext JS, LLC.
23859  *
23860  * Originally Released Under LGPL - original licence link has changed is not relivant.
23861  *
23862  * Fork - LGPL
23863  * <script type="text/javascript">
23864  */
23865 /**
23866  * @class Roo.data.MemoryProxy
23867  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
23868  * to the Reader when its load method is called.
23869  * @constructor
23870  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
23871  */
23872 Roo.data.MemoryProxy = function(data){
23873     if (data.data) {
23874         data = data.data;
23875     }
23876     Roo.data.MemoryProxy.superclass.constructor.call(this);
23877     this.data = data;
23878 };
23879
23880 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
23881     
23882     /**
23883      * Load data from the requested source (in this case an in-memory
23884      * data object passed to the constructor), read the data object into
23885      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
23886      * process that block using the passed callback.
23887      * @param {Object} params This parameter is not used by the MemoryProxy class.
23888      * @param {Roo.data.DataReader} reader The Reader object which converts the data
23889      * object into a block of Roo.data.Records.
23890      * @param {Function} callback The function into which to pass the block of Roo.data.records.
23891      * The function must be passed <ul>
23892      * <li>The Record block object</li>
23893      * <li>The "arg" argument from the load function</li>
23894      * <li>A boolean success indicator</li>
23895      * </ul>
23896      * @param {Object} scope The scope in which to call the callback
23897      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
23898      */
23899     load : function(params, reader, callback, scope, arg){
23900         params = params || {};
23901         var result;
23902         try {
23903             result = reader.readRecords(params.data ? params.data :this.data);
23904         }catch(e){
23905             this.fireEvent("loadexception", this, arg, null, e);
23906             callback.call(scope, null, arg, false);
23907             return;
23908         }
23909         callback.call(scope, result, arg, true);
23910     },
23911     
23912     // private
23913     update : function(params, records){
23914         
23915     }
23916 });/*
23917  * Based on:
23918  * Ext JS Library 1.1.1
23919  * Copyright(c) 2006-2007, Ext JS, LLC.
23920  *
23921  * Originally Released Under LGPL - original licence link has changed is not relivant.
23922  *
23923  * Fork - LGPL
23924  * <script type="text/javascript">
23925  */
23926 /**
23927  * @class Roo.data.HttpProxy
23928  * @extends Roo.data.DataProxy
23929  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
23930  * configured to reference a certain URL.<br><br>
23931  * <p>
23932  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
23933  * from which the running page was served.<br><br>
23934  * <p>
23935  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
23936  * <p>
23937  * Be aware that to enable the browser to parse an XML document, the server must set
23938  * the Content-Type header in the HTTP response to "text/xml".
23939  * @constructor
23940  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
23941  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
23942  * will be used to make the request.
23943  */
23944 Roo.data.HttpProxy = function(conn){
23945     Roo.data.HttpProxy.superclass.constructor.call(this);
23946     // is conn a conn config or a real conn?
23947     this.conn = conn;
23948     this.useAjax = !conn || !conn.events;
23949   
23950 };
23951
23952 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
23953     // thse are take from connection...
23954     
23955     /**
23956      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
23957      */
23958     /**
23959      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
23960      * extra parameters to each request made by this object. (defaults to undefined)
23961      */
23962     /**
23963      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
23964      *  to each request made by this object. (defaults to undefined)
23965      */
23966     /**
23967      * @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)
23968      */
23969     /**
23970      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
23971      */
23972      /**
23973      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
23974      * @type Boolean
23975      */
23976   
23977
23978     /**
23979      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
23980      * @type Boolean
23981      */
23982     /**
23983      * Return the {@link Roo.data.Connection} object being used by this Proxy.
23984      * @return {Connection} The Connection object. This object may be used to subscribe to events on
23985      * a finer-grained basis than the DataProxy events.
23986      */
23987     getConnection : function(){
23988         return this.useAjax ? Roo.Ajax : this.conn;
23989     },
23990
23991     /**
23992      * Load data from the configured {@link Roo.data.Connection}, read the data object into
23993      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
23994      * process that block using the passed callback.
23995      * @param {Object} params An object containing properties which are to be used as HTTP parameters
23996      * for the request to the remote server.
23997      * @param {Roo.data.DataReader} reader The Reader object which converts the data
23998      * object into a block of Roo.data.Records.
23999      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
24000      * The function must be passed <ul>
24001      * <li>The Record block object</li>
24002      * <li>The "arg" argument from the load function</li>
24003      * <li>A boolean success indicator</li>
24004      * </ul>
24005      * @param {Object} scope The scope in which to call the callback
24006      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
24007      */
24008     load : function(params, reader, callback, scope, arg){
24009         if(this.fireEvent("beforeload", this, params) !== false){
24010             var  o = {
24011                 params : params || {},
24012                 request: {
24013                     callback : callback,
24014                     scope : scope,
24015                     arg : arg
24016                 },
24017                 reader: reader,
24018                 callback : this.loadResponse,
24019                 scope: this
24020             };
24021             if(this.useAjax){
24022                 Roo.applyIf(o, this.conn);
24023                 if(this.activeRequest){
24024                     Roo.Ajax.abort(this.activeRequest);
24025                 }
24026                 this.activeRequest = Roo.Ajax.request(o);
24027             }else{
24028                 this.conn.request(o);
24029             }
24030         }else{
24031             callback.call(scope||this, null, arg, false);
24032         }
24033     },
24034
24035     // private
24036     loadResponse : function(o, success, response){
24037         delete this.activeRequest;
24038         if(!success){
24039             this.fireEvent("loadexception", this, o, response);
24040             o.request.callback.call(o.request.scope, null, o.request.arg, false);
24041             return;
24042         }
24043         var result;
24044         try {
24045             result = o.reader.read(response);
24046         }catch(e){
24047             this.fireEvent("loadexception", this, o, response, e);
24048             o.request.callback.call(o.request.scope, null, o.request.arg, false);
24049             return;
24050         }
24051         
24052         this.fireEvent("load", this, o, o.request.arg);
24053         o.request.callback.call(o.request.scope, result, o.request.arg, true);
24054     },
24055
24056     // private
24057     update : function(dataSet){
24058
24059     },
24060
24061     // private
24062     updateResponse : function(dataSet){
24063
24064     }
24065 });/*
24066  * Based on:
24067  * Ext JS Library 1.1.1
24068  * Copyright(c) 2006-2007, Ext JS, LLC.
24069  *
24070  * Originally Released Under LGPL - original licence link has changed is not relivant.
24071  *
24072  * Fork - LGPL
24073  * <script type="text/javascript">
24074  */
24075
24076 /**
24077  * @class Roo.data.ScriptTagProxy
24078  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
24079  * other than the originating domain of the running page.<br><br>
24080  * <p>
24081  * <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
24082  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
24083  * <p>
24084  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
24085  * source code that is used as the source inside a &lt;script> tag.<br><br>
24086  * <p>
24087  * In order for the browser to process the returned data, the server must wrap the data object
24088  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
24089  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
24090  * depending on whether the callback name was passed:
24091  * <p>
24092  * <pre><code>
24093 boolean scriptTag = false;
24094 String cb = request.getParameter("callback");
24095 if (cb != null) {
24096     scriptTag = true;
24097     response.setContentType("text/javascript");
24098 } else {
24099     response.setContentType("application/x-json");
24100 }
24101 Writer out = response.getWriter();
24102 if (scriptTag) {
24103     out.write(cb + "(");
24104 }
24105 out.print(dataBlock.toJsonString());
24106 if (scriptTag) {
24107     out.write(");");
24108 }
24109 </pre></code>
24110  *
24111  * @constructor
24112  * @param {Object} config A configuration object.
24113  */
24114 Roo.data.ScriptTagProxy = function(config){
24115     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
24116     Roo.apply(this, config);
24117     this.head = document.getElementsByTagName("head")[0];
24118 };
24119
24120 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
24121
24122 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
24123     /**
24124      * @cfg {String} url The URL from which to request the data object.
24125      */
24126     /**
24127      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
24128      */
24129     timeout : 30000,
24130     /**
24131      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
24132      * the server the name of the callback function set up by the load call to process the returned data object.
24133      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
24134      * javascript output which calls this named function passing the data object as its only parameter.
24135      */
24136     callbackParam : "callback",
24137     /**
24138      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
24139      * name to the request.
24140      */
24141     nocache : true,
24142
24143     /**
24144      * Load data from the configured URL, read the data object into
24145      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
24146      * process that block using the passed callback.
24147      * @param {Object} params An object containing properties which are to be used as HTTP parameters
24148      * for the request to the remote server.
24149      * @param {Roo.data.DataReader} reader The Reader object which converts the data
24150      * object into a block of Roo.data.Records.
24151      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
24152      * The function must be passed <ul>
24153      * <li>The Record block object</li>
24154      * <li>The "arg" argument from the load function</li>
24155      * <li>A boolean success indicator</li>
24156      * </ul>
24157      * @param {Object} scope The scope in which to call the callback
24158      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
24159      */
24160     load : function(params, reader, callback, scope, arg){
24161         if(this.fireEvent("beforeload", this, params) !== false){
24162
24163             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
24164
24165             var url = this.url;
24166             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
24167             if(this.nocache){
24168                 url += "&_dc=" + (new Date().getTime());
24169             }
24170             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
24171             var trans = {
24172                 id : transId,
24173                 cb : "stcCallback"+transId,
24174                 scriptId : "stcScript"+transId,
24175                 params : params,
24176                 arg : arg,
24177                 url : url,
24178                 callback : callback,
24179                 scope : scope,
24180                 reader : reader
24181             };
24182             var conn = this;
24183
24184             window[trans.cb] = function(o){
24185                 conn.handleResponse(o, trans);
24186             };
24187
24188             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
24189
24190             if(this.autoAbort !== false){
24191                 this.abort();
24192             }
24193
24194             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
24195
24196             var script = document.createElement("script");
24197             script.setAttribute("src", url);
24198             script.setAttribute("type", "text/javascript");
24199             script.setAttribute("id", trans.scriptId);
24200             this.head.appendChild(script);
24201
24202             this.trans = trans;
24203         }else{
24204             callback.call(scope||this, null, arg, false);
24205         }
24206     },
24207
24208     // private
24209     isLoading : function(){
24210         return this.trans ? true : false;
24211     },
24212
24213     /**
24214      * Abort the current server request.
24215      */
24216     abort : function(){
24217         if(this.isLoading()){
24218             this.destroyTrans(this.trans);
24219         }
24220     },
24221
24222     // private
24223     destroyTrans : function(trans, isLoaded){
24224         this.head.removeChild(document.getElementById(trans.scriptId));
24225         clearTimeout(trans.timeoutId);
24226         if(isLoaded){
24227             window[trans.cb] = undefined;
24228             try{
24229                 delete window[trans.cb];
24230             }catch(e){}
24231         }else{
24232             // if hasn't been loaded, wait for load to remove it to prevent script error
24233             window[trans.cb] = function(){
24234                 window[trans.cb] = undefined;
24235                 try{
24236                     delete window[trans.cb];
24237                 }catch(e){}
24238             };
24239         }
24240     },
24241
24242     // private
24243     handleResponse : function(o, trans){
24244         this.trans = false;
24245         this.destroyTrans(trans, true);
24246         var result;
24247         try {
24248             result = trans.reader.readRecords(o);
24249         }catch(e){
24250             this.fireEvent("loadexception", this, o, trans.arg, e);
24251             trans.callback.call(trans.scope||window, null, trans.arg, false);
24252             return;
24253         }
24254         this.fireEvent("load", this, o, trans.arg);
24255         trans.callback.call(trans.scope||window, result, trans.arg, true);
24256     },
24257
24258     // private
24259     handleFailure : function(trans){
24260         this.trans = false;
24261         this.destroyTrans(trans, false);
24262         this.fireEvent("loadexception", this, null, trans.arg);
24263         trans.callback.call(trans.scope||window, null, trans.arg, false);
24264     }
24265 });/*
24266  * Based on:
24267  * Ext JS Library 1.1.1
24268  * Copyright(c) 2006-2007, Ext JS, LLC.
24269  *
24270  * Originally Released Under LGPL - original licence link has changed is not relivant.
24271  *
24272  * Fork - LGPL
24273  * <script type="text/javascript">
24274  */
24275
24276 /**
24277  * @class Roo.data.JsonReader
24278  * @extends Roo.data.DataReader
24279  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
24280  * based on mappings in a provided Roo.data.Record constructor.
24281  * 
24282  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
24283  * in the reply previously. 
24284  * 
24285  * <p>
24286  * Example code:
24287  * <pre><code>
24288 var RecordDef = Roo.data.Record.create([
24289     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
24290     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
24291 ]);
24292 var myReader = new Roo.data.JsonReader({
24293     totalProperty: "results",    // The property which contains the total dataset size (optional)
24294     root: "rows",                // The property which contains an Array of row objects
24295     id: "id"                     // The property within each row object that provides an ID for the record (optional)
24296 }, RecordDef);
24297 </code></pre>
24298  * <p>
24299  * This would consume a JSON file like this:
24300  * <pre><code>
24301 { 'results': 2, 'rows': [
24302     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
24303     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
24304 }
24305 </code></pre>
24306  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
24307  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
24308  * paged from the remote server.
24309  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
24310  * @cfg {String} root name of the property which contains the Array of row objects.
24311  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
24312  * @cfg {Array} fields Array of field definition objects
24313  * @constructor
24314  * Create a new JsonReader
24315  * @param {Object} meta Metadata configuration options
24316  * @param {Object} recordType Either an Array of field definition objects,
24317  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
24318  */
24319 Roo.data.JsonReader = function(meta, recordType){
24320     
24321     meta = meta || {};
24322     // set some defaults:
24323     Roo.applyIf(meta, {
24324         totalProperty: 'total',
24325         successProperty : 'success',
24326         root : 'data',
24327         id : 'id'
24328     });
24329     
24330     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
24331 };
24332 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
24333     
24334     readerType : 'Json',
24335     
24336     /**
24337      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
24338      * Used by Store query builder to append _requestMeta to params.
24339      * 
24340      */
24341     metaFromRemote : false,
24342     /**
24343      * This method is only used by a DataProxy which has retrieved data from a remote server.
24344      * @param {Object} response The XHR object which contains the JSON data in its responseText.
24345      * @return {Object} data A data block which is used by an Roo.data.Store object as
24346      * a cache of Roo.data.Records.
24347      */
24348     read : function(response){
24349         var json = response.responseText;
24350        
24351         var o = /* eval:var:o */ eval("("+json+")");
24352         if(!o) {
24353             throw {message: "JsonReader.read: Json object not found"};
24354         }
24355         
24356         if(o.metaData){
24357             
24358             delete this.ef;
24359             this.metaFromRemote = true;
24360             this.meta = o.metaData;
24361             this.recordType = Roo.data.Record.create(o.metaData.fields);
24362             this.onMetaChange(this.meta, this.recordType, o);
24363         }
24364         return this.readRecords(o);
24365     },
24366
24367     // private function a store will implement
24368     onMetaChange : function(meta, recordType, o){
24369
24370     },
24371
24372     /**
24373          * @ignore
24374          */
24375     simpleAccess: function(obj, subsc) {
24376         return obj[subsc];
24377     },
24378
24379         /**
24380          * @ignore
24381          */
24382     getJsonAccessor: function(){
24383         var re = /[\[\.]/;
24384         return function(expr) {
24385             try {
24386                 return(re.test(expr))
24387                     ? new Function("obj", "return obj." + expr)
24388                     : function(obj){
24389                         return obj[expr];
24390                     };
24391             } catch(e){}
24392             return Roo.emptyFn;
24393         };
24394     }(),
24395
24396     /**
24397      * Create a data block containing Roo.data.Records from an XML document.
24398      * @param {Object} o An object which contains an Array of row objects in the property specified
24399      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
24400      * which contains the total size of the dataset.
24401      * @return {Object} data A data block which is used by an Roo.data.Store object as
24402      * a cache of Roo.data.Records.
24403      */
24404     readRecords : function(o){
24405         /**
24406          * After any data loads, the raw JSON data is available for further custom processing.
24407          * @type Object
24408          */
24409         this.o = o;
24410         var s = this.meta, Record = this.recordType,
24411             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
24412
24413 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
24414         if (!this.ef) {
24415             if(s.totalProperty) {
24416                     this.getTotal = this.getJsonAccessor(s.totalProperty);
24417                 }
24418                 if(s.successProperty) {
24419                     this.getSuccess = this.getJsonAccessor(s.successProperty);
24420                 }
24421                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
24422                 if (s.id) {
24423                         var g = this.getJsonAccessor(s.id);
24424                         this.getId = function(rec) {
24425                                 var r = g(rec);  
24426                                 return (r === undefined || r === "") ? null : r;
24427                         };
24428                 } else {
24429                         this.getId = function(){return null;};
24430                 }
24431             this.ef = [];
24432             for(var jj = 0; jj < fl; jj++){
24433                 f = fi[jj];
24434                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
24435                 this.ef[jj] = this.getJsonAccessor(map);
24436             }
24437         }
24438
24439         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
24440         if(s.totalProperty){
24441             var vt = parseInt(this.getTotal(o), 10);
24442             if(!isNaN(vt)){
24443                 totalRecords = vt;
24444             }
24445         }
24446         if(s.successProperty){
24447             var vs = this.getSuccess(o);
24448             if(vs === false || vs === 'false'){
24449                 success = false;
24450             }
24451         }
24452         var records = [];
24453         for(var i = 0; i < c; i++){
24454                 var n = root[i];
24455             var values = {};
24456             var id = this.getId(n);
24457             for(var j = 0; j < fl; j++){
24458                 f = fi[j];
24459             var v = this.ef[j](n);
24460             if (!f.convert) {
24461                 Roo.log('missing convert for ' + f.name);
24462                 Roo.log(f);
24463                 continue;
24464             }
24465             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
24466             }
24467             var record = new Record(values, id);
24468             record.json = n;
24469             records[i] = record;
24470         }
24471         return {
24472             raw : o,
24473             success : success,
24474             records : records,
24475             totalRecords : totalRecords
24476         };
24477     },
24478     // used when loading children.. @see loadDataFromChildren
24479     toLoadData: function(rec)
24480     {
24481         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
24482         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
24483         return { data : data, total : data.length };
24484         
24485     }
24486 });/*
24487  * Based on:
24488  * Ext JS Library 1.1.1
24489  * Copyright(c) 2006-2007, Ext JS, LLC.
24490  *
24491  * Originally Released Under LGPL - original licence link has changed is not relivant.
24492  *
24493  * Fork - LGPL
24494  * <script type="text/javascript">
24495  */
24496
24497 /**
24498  * @class Roo.data.XmlReader
24499  * @extends Roo.data.DataReader
24500  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
24501  * based on mappings in a provided Roo.data.Record constructor.<br><br>
24502  * <p>
24503  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
24504  * header in the HTTP response must be set to "text/xml".</em>
24505  * <p>
24506  * Example code:
24507  * <pre><code>
24508 var RecordDef = Roo.data.Record.create([
24509    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
24510    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
24511 ]);
24512 var myReader = new Roo.data.XmlReader({
24513    totalRecords: "results", // The element which contains the total dataset size (optional)
24514    record: "row",           // The repeated element which contains row information
24515    id: "id"                 // The element within the row that provides an ID for the record (optional)
24516 }, RecordDef);
24517 </code></pre>
24518  * <p>
24519  * This would consume an XML file like this:
24520  * <pre><code>
24521 &lt;?xml?>
24522 &lt;dataset>
24523  &lt;results>2&lt;/results>
24524  &lt;row>
24525    &lt;id>1&lt;/id>
24526    &lt;name>Bill&lt;/name>
24527    &lt;occupation>Gardener&lt;/occupation>
24528  &lt;/row>
24529  &lt;row>
24530    &lt;id>2&lt;/id>
24531    &lt;name>Ben&lt;/name>
24532    &lt;occupation>Horticulturalist&lt;/occupation>
24533  &lt;/row>
24534 &lt;/dataset>
24535 </code></pre>
24536  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
24537  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
24538  * paged from the remote server.
24539  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
24540  * @cfg {String} success The DomQuery path to the success attribute used by forms.
24541  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
24542  * a record identifier value.
24543  * @constructor
24544  * Create a new XmlReader
24545  * @param {Object} meta Metadata configuration options
24546  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
24547  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
24548  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
24549  */
24550 Roo.data.XmlReader = function(meta, recordType){
24551     meta = meta || {};
24552     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
24553 };
24554 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
24555     
24556     readerType : 'Xml',
24557     
24558     /**
24559      * This method is only used by a DataProxy which has retrieved data from a remote server.
24560          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
24561          * to contain a method called 'responseXML' that returns an XML document object.
24562      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
24563      * a cache of Roo.data.Records.
24564      */
24565     read : function(response){
24566         var doc = response.responseXML;
24567         if(!doc) {
24568             throw {message: "XmlReader.read: XML Document not available"};
24569         }
24570         return this.readRecords(doc);
24571     },
24572
24573     /**
24574      * Create a data block containing Roo.data.Records from an XML document.
24575          * @param {Object} doc A parsed XML document.
24576      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
24577      * a cache of Roo.data.Records.
24578      */
24579     readRecords : function(doc){
24580         /**
24581          * After any data loads/reads, the raw XML Document is available for further custom processing.
24582          * @type XMLDocument
24583          */
24584         this.xmlData = doc;
24585         var root = doc.documentElement || doc;
24586         var q = Roo.DomQuery;
24587         var recordType = this.recordType, fields = recordType.prototype.fields;
24588         var sid = this.meta.id;
24589         var totalRecords = 0, success = true;
24590         if(this.meta.totalRecords){
24591             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
24592         }
24593         
24594         if(this.meta.success){
24595             var sv = q.selectValue(this.meta.success, root, true);
24596             success = sv !== false && sv !== 'false';
24597         }
24598         var records = [];
24599         var ns = q.select(this.meta.record, root);
24600         for(var i = 0, len = ns.length; i < len; i++) {
24601                 var n = ns[i];
24602                 var values = {};
24603                 var id = sid ? q.selectValue(sid, n) : undefined;
24604                 for(var j = 0, jlen = fields.length; j < jlen; j++){
24605                     var f = fields.items[j];
24606                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
24607                     v = f.convert(v);
24608                     values[f.name] = v;
24609                 }
24610                 var record = new recordType(values, id);
24611                 record.node = n;
24612                 records[records.length] = record;
24613             }
24614
24615             return {
24616                 success : success,
24617                 records : records,
24618                 totalRecords : totalRecords || records.length
24619             };
24620     }
24621 });/*
24622  * Based on:
24623  * Ext JS Library 1.1.1
24624  * Copyright(c) 2006-2007, Ext JS, LLC.
24625  *
24626  * Originally Released Under LGPL - original licence link has changed is not relivant.
24627  *
24628  * Fork - LGPL
24629  * <script type="text/javascript">
24630  */
24631
24632 /**
24633  * @class Roo.data.ArrayReader
24634  * @extends Roo.data.DataReader
24635  * Data reader class to create an Array of Roo.data.Record objects from an Array.
24636  * Each element of that Array represents a row of data fields. The
24637  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
24638  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
24639  * <p>
24640  * Example code:.
24641  * <pre><code>
24642 var RecordDef = Roo.data.Record.create([
24643     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
24644     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
24645 ]);
24646 var myReader = new Roo.data.ArrayReader({
24647     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
24648 }, RecordDef);
24649 </code></pre>
24650  * <p>
24651  * This would consume an Array like this:
24652  * <pre><code>
24653 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
24654   </code></pre>
24655  
24656  * @constructor
24657  * Create a new JsonReader
24658  * @param {Object} meta Metadata configuration options.
24659  * @param {Object|Array} recordType Either an Array of field definition objects
24660  * 
24661  * @cfg {Array} fields Array of field definition objects
24662  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
24663  * as specified to {@link Roo.data.Record#create},
24664  * or an {@link Roo.data.Record} object
24665  *
24666  * 
24667  * created using {@link Roo.data.Record#create}.
24668  */
24669 Roo.data.ArrayReader = function(meta, recordType)
24670 {    
24671     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
24672 };
24673
24674 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
24675     
24676       /**
24677      * Create a data block containing Roo.data.Records from an XML document.
24678      * @param {Object} o An Array of row objects which represents the dataset.
24679      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
24680      * a cache of Roo.data.Records.
24681      */
24682     readRecords : function(o)
24683     {
24684         var sid = this.meta ? this.meta.id : null;
24685         var recordType = this.recordType, fields = recordType.prototype.fields;
24686         var records = [];
24687         var root = o;
24688         for(var i = 0; i < root.length; i++){
24689                 var n = root[i];
24690             var values = {};
24691             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
24692             for(var j = 0, jlen = fields.length; j < jlen; j++){
24693                 var f = fields.items[j];
24694                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
24695                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
24696                 v = f.convert(v);
24697                 values[f.name] = v;
24698             }
24699             var record = new recordType(values, id);
24700             record.json = n;
24701             records[records.length] = record;
24702         }
24703         return {
24704             records : records,
24705             totalRecords : records.length
24706         };
24707     },
24708     // used when loading children.. @see loadDataFromChildren
24709     toLoadData: function(rec)
24710     {
24711         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
24712         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
24713         
24714     }
24715     
24716     
24717 });/*
24718  * Based on:
24719  * Ext JS Library 1.1.1
24720  * Copyright(c) 2006-2007, Ext JS, LLC.
24721  *
24722  * Originally Released Under LGPL - original licence link has changed is not relivant.
24723  *
24724  * Fork - LGPL
24725  * <script type="text/javascript">
24726  */
24727
24728
24729 /**
24730  * @class Roo.data.Tree
24731  * @extends Roo.util.Observable
24732  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
24733  * in the tree have most standard DOM functionality.
24734  * @constructor
24735  * @param {Node} root (optional) The root node
24736  */
24737 Roo.data.Tree = function(root){
24738    this.nodeHash = {};
24739    /**
24740     * The root node for this tree
24741     * @type Node
24742     */
24743    this.root = null;
24744    if(root){
24745        this.setRootNode(root);
24746    }
24747    this.addEvents({
24748        /**
24749         * @event append
24750         * Fires when a new child node is appended to a node in this tree.
24751         * @param {Tree} tree The owner tree
24752         * @param {Node} parent The parent node
24753         * @param {Node} node The newly appended node
24754         * @param {Number} index The index of the newly appended node
24755         */
24756        "append" : true,
24757        /**
24758         * @event remove
24759         * Fires when a child node is removed from a node in this tree.
24760         * @param {Tree} tree The owner tree
24761         * @param {Node} parent The parent node
24762         * @param {Node} node The child node removed
24763         */
24764        "remove" : true,
24765        /**
24766         * @event move
24767         * Fires when a node is moved to a new location in the tree
24768         * @param {Tree} tree The owner tree
24769         * @param {Node} node The node moved
24770         * @param {Node} oldParent The old parent of this node
24771         * @param {Node} newParent The new parent of this node
24772         * @param {Number} index The index it was moved to
24773         */
24774        "move" : true,
24775        /**
24776         * @event insert
24777         * Fires when a new child node is inserted in a node in this tree.
24778         * @param {Tree} tree The owner tree
24779         * @param {Node} parent The parent node
24780         * @param {Node} node The child node inserted
24781         * @param {Node} refNode The child node the node was inserted before
24782         */
24783        "insert" : true,
24784        /**
24785         * @event beforeappend
24786         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
24787         * @param {Tree} tree The owner tree
24788         * @param {Node} parent The parent node
24789         * @param {Node} node The child node to be appended
24790         */
24791        "beforeappend" : true,
24792        /**
24793         * @event beforeremove
24794         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
24795         * @param {Tree} tree The owner tree
24796         * @param {Node} parent The parent node
24797         * @param {Node} node The child node to be removed
24798         */
24799        "beforeremove" : true,
24800        /**
24801         * @event beforemove
24802         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
24803         * @param {Tree} tree The owner tree
24804         * @param {Node} node The node being moved
24805         * @param {Node} oldParent The parent of the node
24806         * @param {Node} newParent The new parent the node is moving to
24807         * @param {Number} index The index it is being moved to
24808         */
24809        "beforemove" : true,
24810        /**
24811         * @event beforeinsert
24812         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
24813         * @param {Tree} tree The owner tree
24814         * @param {Node} parent The parent node
24815         * @param {Node} node The child node to be inserted
24816         * @param {Node} refNode The child node the node is being inserted before
24817         */
24818        "beforeinsert" : true
24819    });
24820
24821     Roo.data.Tree.superclass.constructor.call(this);
24822 };
24823
24824 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
24825     pathSeparator: "/",
24826
24827     proxyNodeEvent : function(){
24828         return this.fireEvent.apply(this, arguments);
24829     },
24830
24831     /**
24832      * Returns the root node for this tree.
24833      * @return {Node}
24834      */
24835     getRootNode : function(){
24836         return this.root;
24837     },
24838
24839     /**
24840      * Sets the root node for this tree.
24841      * @param {Node} node
24842      * @return {Node}
24843      */
24844     setRootNode : function(node){
24845         this.root = node;
24846         node.ownerTree = this;
24847         node.isRoot = true;
24848         this.registerNode(node);
24849         return node;
24850     },
24851
24852     /**
24853      * Gets a node in this tree by its id.
24854      * @param {String} id
24855      * @return {Node}
24856      */
24857     getNodeById : function(id){
24858         return this.nodeHash[id];
24859     },
24860
24861     registerNode : function(node){
24862         this.nodeHash[node.id] = node;
24863     },
24864
24865     unregisterNode : function(node){
24866         delete this.nodeHash[node.id];
24867     },
24868
24869     toString : function(){
24870         return "[Tree"+(this.id?" "+this.id:"")+"]";
24871     }
24872 });
24873
24874 /**
24875  * @class Roo.data.Node
24876  * @extends Roo.util.Observable
24877  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
24878  * @cfg {String} id The id for this node. If one is not specified, one is generated.
24879  * @constructor
24880  * @param {Object} attributes The attributes/config for the node
24881  */
24882 Roo.data.Node = function(attributes){
24883     /**
24884      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
24885      * @type {Object}
24886      */
24887     this.attributes = attributes || {};
24888     this.leaf = this.attributes.leaf;
24889     /**
24890      * The node id. @type String
24891      */
24892     this.id = this.attributes.id;
24893     if(!this.id){
24894         this.id = Roo.id(null, "ynode-");
24895         this.attributes.id = this.id;
24896     }
24897      
24898     
24899     /**
24900      * All child nodes of this node. @type Array
24901      */
24902     this.childNodes = [];
24903     if(!this.childNodes.indexOf){ // indexOf is a must
24904         this.childNodes.indexOf = function(o){
24905             for(var i = 0, len = this.length; i < len; i++){
24906                 if(this[i] == o) {
24907                     return i;
24908                 }
24909             }
24910             return -1;
24911         };
24912     }
24913     /**
24914      * The parent node for this node. @type Node
24915      */
24916     this.parentNode = null;
24917     /**
24918      * The first direct child node of this node, or null if this node has no child nodes. @type Node
24919      */
24920     this.firstChild = null;
24921     /**
24922      * The last direct child node of this node, or null if this node has no child nodes. @type Node
24923      */
24924     this.lastChild = null;
24925     /**
24926      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
24927      */
24928     this.previousSibling = null;
24929     /**
24930      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
24931      */
24932     this.nextSibling = null;
24933
24934     this.addEvents({
24935        /**
24936         * @event append
24937         * Fires when a new child node is appended
24938         * @param {Tree} tree The owner tree
24939         * @param {Node} this This node
24940         * @param {Node} node The newly appended node
24941         * @param {Number} index The index of the newly appended node
24942         */
24943        "append" : true,
24944        /**
24945         * @event remove
24946         * Fires when a child node is removed
24947         * @param {Tree} tree The owner tree
24948         * @param {Node} this This node
24949         * @param {Node} node The removed node
24950         */
24951        "remove" : true,
24952        /**
24953         * @event move
24954         * Fires when this node is moved to a new location in the tree
24955         * @param {Tree} tree The owner tree
24956         * @param {Node} this This node
24957         * @param {Node} oldParent The old parent of this node
24958         * @param {Node} newParent The new parent of this node
24959         * @param {Number} index The index it was moved to
24960         */
24961        "move" : true,
24962        /**
24963         * @event insert
24964         * Fires when a new child node is inserted.
24965         * @param {Tree} tree The owner tree
24966         * @param {Node} this This node
24967         * @param {Node} node The child node inserted
24968         * @param {Node} refNode The child node the node was inserted before
24969         */
24970        "insert" : true,
24971        /**
24972         * @event beforeappend
24973         * Fires before a new child is appended, return false to cancel the append.
24974         * @param {Tree} tree The owner tree
24975         * @param {Node} this This node
24976         * @param {Node} node The child node to be appended
24977         */
24978        "beforeappend" : true,
24979        /**
24980         * @event beforeremove
24981         * Fires before a child is removed, return false to cancel the remove.
24982         * @param {Tree} tree The owner tree
24983         * @param {Node} this This node
24984         * @param {Node} node The child node to be removed
24985         */
24986        "beforeremove" : true,
24987        /**
24988         * @event beforemove
24989         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
24990         * @param {Tree} tree The owner tree
24991         * @param {Node} this This node
24992         * @param {Node} oldParent The parent of this node
24993         * @param {Node} newParent The new parent this node is moving to
24994         * @param {Number} index The index it is being moved to
24995         */
24996        "beforemove" : true,
24997        /**
24998         * @event beforeinsert
24999         * Fires before a new child is inserted, return false to cancel the insert.
25000         * @param {Tree} tree The owner tree
25001         * @param {Node} this This node
25002         * @param {Node} node The child node to be inserted
25003         * @param {Node} refNode The child node the node is being inserted before
25004         */
25005        "beforeinsert" : true
25006    });
25007     this.listeners = this.attributes.listeners;
25008     Roo.data.Node.superclass.constructor.call(this);
25009 };
25010
25011 Roo.extend(Roo.data.Node, Roo.util.Observable, {
25012     fireEvent : function(evtName){
25013         // first do standard event for this node
25014         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
25015             return false;
25016         }
25017         // then bubble it up to the tree if the event wasn't cancelled
25018         var ot = this.getOwnerTree();
25019         if(ot){
25020             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
25021                 return false;
25022             }
25023         }
25024         return true;
25025     },
25026
25027     /**
25028      * Returns true if this node is a leaf
25029      * @return {Boolean}
25030      */
25031     isLeaf : function(){
25032         return this.leaf === true;
25033     },
25034
25035     // private
25036     setFirstChild : function(node){
25037         this.firstChild = node;
25038     },
25039
25040     //private
25041     setLastChild : function(node){
25042         this.lastChild = node;
25043     },
25044
25045
25046     /**
25047      * Returns true if this node is the last child of its parent
25048      * @return {Boolean}
25049      */
25050     isLast : function(){
25051        return (!this.parentNode ? true : this.parentNode.lastChild == this);
25052     },
25053
25054     /**
25055      * Returns true if this node is the first child of its parent
25056      * @return {Boolean}
25057      */
25058     isFirst : function(){
25059        return (!this.parentNode ? true : this.parentNode.firstChild == this);
25060     },
25061
25062     hasChildNodes : function(){
25063         return !this.isLeaf() && this.childNodes.length > 0;
25064     },
25065
25066     /**
25067      * Insert node(s) as the last child node of this node.
25068      * @param {Node/Array} node The node or Array of nodes to append
25069      * @return {Node} The appended node if single append, or null if an array was passed
25070      */
25071     appendChild : function(node){
25072         var multi = false;
25073         if(node instanceof Array){
25074             multi = node;
25075         }else if(arguments.length > 1){
25076             multi = arguments;
25077         }
25078         
25079         // if passed an array or multiple args do them one by one
25080         if(multi){
25081             for(var i = 0, len = multi.length; i < len; i++) {
25082                 this.appendChild(multi[i]);
25083             }
25084         }else{
25085             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
25086                 return false;
25087             }
25088             var index = this.childNodes.length;
25089             var oldParent = node.parentNode;
25090             // it's a move, make sure we move it cleanly
25091             if(oldParent){
25092                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
25093                     return false;
25094                 }
25095                 oldParent.removeChild(node);
25096             }
25097             
25098             index = this.childNodes.length;
25099             if(index == 0){
25100                 this.setFirstChild(node);
25101             }
25102             this.childNodes.push(node);
25103             node.parentNode = this;
25104             var ps = this.childNodes[index-1];
25105             if(ps){
25106                 node.previousSibling = ps;
25107                 ps.nextSibling = node;
25108             }else{
25109                 node.previousSibling = null;
25110             }
25111             node.nextSibling = null;
25112             this.setLastChild(node);
25113             node.setOwnerTree(this.getOwnerTree());
25114             this.fireEvent("append", this.ownerTree, this, node, index);
25115             if(this.ownerTree) {
25116                 this.ownerTree.fireEvent("appendnode", this, node, index);
25117             }
25118             if(oldParent){
25119                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
25120             }
25121             return node;
25122         }
25123     },
25124
25125     /**
25126      * Removes a child node from this node.
25127      * @param {Node} node The node to remove
25128      * @return {Node} The removed node
25129      */
25130     removeChild : function(node){
25131         var index = this.childNodes.indexOf(node);
25132         if(index == -1){
25133             return false;
25134         }
25135         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
25136             return false;
25137         }
25138
25139         // remove it from childNodes collection
25140         this.childNodes.splice(index, 1);
25141
25142         // update siblings
25143         if(node.previousSibling){
25144             node.previousSibling.nextSibling = node.nextSibling;
25145         }
25146         if(node.nextSibling){
25147             node.nextSibling.previousSibling = node.previousSibling;
25148         }
25149
25150         // update child refs
25151         if(this.firstChild == node){
25152             this.setFirstChild(node.nextSibling);
25153         }
25154         if(this.lastChild == node){
25155             this.setLastChild(node.previousSibling);
25156         }
25157
25158         node.setOwnerTree(null);
25159         // clear any references from the node
25160         node.parentNode = null;
25161         node.previousSibling = null;
25162         node.nextSibling = null;
25163         this.fireEvent("remove", this.ownerTree, this, node);
25164         return node;
25165     },
25166
25167     /**
25168      * Inserts the first node before the second node in this nodes childNodes collection.
25169      * @param {Node} node The node to insert
25170      * @param {Node} refNode The node to insert before (if null the node is appended)
25171      * @return {Node} The inserted node
25172      */
25173     insertBefore : function(node, refNode){
25174         if(!refNode){ // like standard Dom, refNode can be null for append
25175             return this.appendChild(node);
25176         }
25177         // nothing to do
25178         if(node == refNode){
25179             return false;
25180         }
25181
25182         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
25183             return false;
25184         }
25185         var index = this.childNodes.indexOf(refNode);
25186         var oldParent = node.parentNode;
25187         var refIndex = index;
25188
25189         // when moving internally, indexes will change after remove
25190         if(oldParent == this && this.childNodes.indexOf(node) < index){
25191             refIndex--;
25192         }
25193
25194         // it's a move, make sure we move it cleanly
25195         if(oldParent){
25196             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
25197                 return false;
25198             }
25199             oldParent.removeChild(node);
25200         }
25201         if(refIndex == 0){
25202             this.setFirstChild(node);
25203         }
25204         this.childNodes.splice(refIndex, 0, node);
25205         node.parentNode = this;
25206         var ps = this.childNodes[refIndex-1];
25207         if(ps){
25208             node.previousSibling = ps;
25209             ps.nextSibling = node;
25210         }else{
25211             node.previousSibling = null;
25212         }
25213         node.nextSibling = refNode;
25214         refNode.previousSibling = node;
25215         node.setOwnerTree(this.getOwnerTree());
25216         this.fireEvent("insert", this.ownerTree, this, node, refNode);
25217         if(oldParent){
25218             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
25219         }
25220         return node;
25221     },
25222
25223     /**
25224      * Returns the child node at the specified index.
25225      * @param {Number} index
25226      * @return {Node}
25227      */
25228     item : function(index){
25229         return this.childNodes[index];
25230     },
25231
25232     /**
25233      * Replaces one child node in this node with another.
25234      * @param {Node} newChild The replacement node
25235      * @param {Node} oldChild The node to replace
25236      * @return {Node} The replaced node
25237      */
25238     replaceChild : function(newChild, oldChild){
25239         this.insertBefore(newChild, oldChild);
25240         this.removeChild(oldChild);
25241         return oldChild;
25242     },
25243
25244     /**
25245      * Returns the index of a child node
25246      * @param {Node} node
25247      * @return {Number} The index of the node or -1 if it was not found
25248      */
25249     indexOf : function(child){
25250         return this.childNodes.indexOf(child);
25251     },
25252
25253     /**
25254      * Returns the tree this node is in.
25255      * @return {Tree}
25256      */
25257     getOwnerTree : function(){
25258         // if it doesn't have one, look for one
25259         if(!this.ownerTree){
25260             var p = this;
25261             while(p){
25262                 if(p.ownerTree){
25263                     this.ownerTree = p.ownerTree;
25264                     break;
25265                 }
25266                 p = p.parentNode;
25267             }
25268         }
25269         return this.ownerTree;
25270     },
25271
25272     /**
25273      * Returns depth of this node (the root node has a depth of 0)
25274      * @return {Number}
25275      */
25276     getDepth : function(){
25277         var depth = 0;
25278         var p = this;
25279         while(p.parentNode){
25280             ++depth;
25281             p = p.parentNode;
25282         }
25283         return depth;
25284     },
25285
25286     // private
25287     setOwnerTree : function(tree){
25288         // if it's move, we need to update everyone
25289         if(tree != this.ownerTree){
25290             if(this.ownerTree){
25291                 this.ownerTree.unregisterNode(this);
25292             }
25293             this.ownerTree = tree;
25294             var cs = this.childNodes;
25295             for(var i = 0, len = cs.length; i < len; i++) {
25296                 cs[i].setOwnerTree(tree);
25297             }
25298             if(tree){
25299                 tree.registerNode(this);
25300             }
25301         }
25302     },
25303
25304     /**
25305      * Returns the path for this node. The path can be used to expand or select this node programmatically.
25306      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
25307      * @return {String} The path
25308      */
25309     getPath : function(attr){
25310         attr = attr || "id";
25311         var p = this.parentNode;
25312         var b = [this.attributes[attr]];
25313         while(p){
25314             b.unshift(p.attributes[attr]);
25315             p = p.parentNode;
25316         }
25317         var sep = this.getOwnerTree().pathSeparator;
25318         return sep + b.join(sep);
25319     },
25320
25321     /**
25322      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
25323      * function call will be the scope provided or the current node. The arguments to the function
25324      * will be the args provided or the current node. If the function returns false at any point,
25325      * the bubble is stopped.
25326      * @param {Function} fn The function to call
25327      * @param {Object} scope (optional) The scope of the function (defaults to current node)
25328      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
25329      */
25330     bubble : function(fn, scope, args){
25331         var p = this;
25332         while(p){
25333             if(fn.call(scope || p, args || p) === false){
25334                 break;
25335             }
25336             p = p.parentNode;
25337         }
25338     },
25339
25340     /**
25341      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
25342      * function call will be the scope provided or the current node. The arguments to the function
25343      * will be the args provided or the current node. If the function returns false at any point,
25344      * the cascade is stopped on that branch.
25345      * @param {Function} fn The function to call
25346      * @param {Object} scope (optional) The scope of the function (defaults to current node)
25347      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
25348      */
25349     cascade : function(fn, scope, args){
25350         if(fn.call(scope || this, args || this) !== false){
25351             var cs = this.childNodes;
25352             for(var i = 0, len = cs.length; i < len; i++) {
25353                 cs[i].cascade(fn, scope, args);
25354             }
25355         }
25356     },
25357
25358     /**
25359      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
25360      * function call will be the scope provided or the current node. The arguments to the function
25361      * will be the args provided or the current node. If the function returns false at any point,
25362      * the iteration stops.
25363      * @param {Function} fn The function to call
25364      * @param {Object} scope (optional) The scope of the function (defaults to current node)
25365      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
25366      */
25367     eachChild : function(fn, scope, args){
25368         var cs = this.childNodes;
25369         for(var i = 0, len = cs.length; i < len; i++) {
25370                 if(fn.call(scope || this, args || cs[i]) === false){
25371                     break;
25372                 }
25373         }
25374     },
25375
25376     /**
25377      * Finds the first child that has the attribute with the specified value.
25378      * @param {String} attribute The attribute name
25379      * @param {Mixed} value The value to search for
25380      * @return {Node} The found child or null if none was found
25381      */
25382     findChild : function(attribute, value){
25383         var cs = this.childNodes;
25384         for(var i = 0, len = cs.length; i < len; i++) {
25385                 if(cs[i].attributes[attribute] == value){
25386                     return cs[i];
25387                 }
25388         }
25389         return null;
25390     },
25391
25392     /**
25393      * Finds the first child by a custom function. The child matches if the function passed
25394      * returns true.
25395      * @param {Function} fn
25396      * @param {Object} scope (optional)
25397      * @return {Node} The found child or null if none was found
25398      */
25399     findChildBy : function(fn, scope){
25400         var cs = this.childNodes;
25401         for(var i = 0, len = cs.length; i < len; i++) {
25402                 if(fn.call(scope||cs[i], cs[i]) === true){
25403                     return cs[i];
25404                 }
25405         }
25406         return null;
25407     },
25408
25409     /**
25410      * Sorts this nodes children using the supplied sort function
25411      * @param {Function} fn
25412      * @param {Object} scope (optional)
25413      */
25414     sort : function(fn, scope){
25415         var cs = this.childNodes;
25416         var len = cs.length;
25417         if(len > 0){
25418             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
25419             cs.sort(sortFn);
25420             for(var i = 0; i < len; i++){
25421                 var n = cs[i];
25422                 n.previousSibling = cs[i-1];
25423                 n.nextSibling = cs[i+1];
25424                 if(i == 0){
25425                     this.setFirstChild(n);
25426                 }
25427                 if(i == len-1){
25428                     this.setLastChild(n);
25429                 }
25430             }
25431         }
25432     },
25433
25434     /**
25435      * Returns true if this node is an ancestor (at any point) of the passed node.
25436      * @param {Node} node
25437      * @return {Boolean}
25438      */
25439     contains : function(node){
25440         return node.isAncestor(this);
25441     },
25442
25443     /**
25444      * Returns true if the passed node is an ancestor (at any point) of this node.
25445      * @param {Node} node
25446      * @return {Boolean}
25447      */
25448     isAncestor : function(node){
25449         var p = this.parentNode;
25450         while(p){
25451             if(p == node){
25452                 return true;
25453             }
25454             p = p.parentNode;
25455         }
25456         return false;
25457     },
25458
25459     toString : function(){
25460         return "[Node"+(this.id?" "+this.id:"")+"]";
25461     }
25462 });/*
25463  * Based on:
25464  * Ext JS Library 1.1.1
25465  * Copyright(c) 2006-2007, Ext JS, LLC.
25466  *
25467  * Originally Released Under LGPL - original licence link has changed is not relivant.
25468  *
25469  * Fork - LGPL
25470  * <script type="text/javascript">
25471  */
25472  (function(){ 
25473 /**
25474  * @class Roo.Layer
25475  * @extends Roo.Element
25476  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
25477  * automatic maintaining of shadow/shim positions.
25478  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
25479  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
25480  * you can pass a string with a CSS class name. False turns off the shadow.
25481  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
25482  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
25483  * @cfg {String} cls CSS class to add to the element
25484  * @cfg {Number} zindex Starting z-index (defaults to 11000)
25485  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
25486  * @constructor
25487  * @param {Object} config An object with config options.
25488  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
25489  */
25490
25491 Roo.Layer = function(config, existingEl){
25492     config = config || {};
25493     var dh = Roo.DomHelper;
25494     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
25495     if(existingEl){
25496         this.dom = Roo.getDom(existingEl);
25497     }
25498     if(!this.dom){
25499         var o = config.dh || {tag: "div", cls: "x-layer"};
25500         this.dom = dh.append(pel, o);
25501     }
25502     if(config.cls){
25503         this.addClass(config.cls);
25504     }
25505     this.constrain = config.constrain !== false;
25506     this.visibilityMode = Roo.Element.VISIBILITY;
25507     if(config.id){
25508         this.id = this.dom.id = config.id;
25509     }else{
25510         this.id = Roo.id(this.dom);
25511     }
25512     this.zindex = config.zindex || this.getZIndex();
25513     this.position("absolute", this.zindex);
25514     if(config.shadow){
25515         this.shadowOffset = config.shadowOffset || 4;
25516         this.shadow = new Roo.Shadow({
25517             offset : this.shadowOffset,
25518             mode : config.shadow
25519         });
25520     }else{
25521         this.shadowOffset = 0;
25522     }
25523     this.useShim = config.shim !== false && Roo.useShims;
25524     this.useDisplay = config.useDisplay;
25525     this.hide();
25526 };
25527
25528 var supr = Roo.Element.prototype;
25529
25530 // shims are shared among layer to keep from having 100 iframes
25531 var shims = [];
25532
25533 Roo.extend(Roo.Layer, Roo.Element, {
25534
25535     getZIndex : function(){
25536         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
25537     },
25538
25539     getShim : function(){
25540         if(!this.useShim){
25541             return null;
25542         }
25543         if(this.shim){
25544             return this.shim;
25545         }
25546         var shim = shims.shift();
25547         if(!shim){
25548             shim = this.createShim();
25549             shim.enableDisplayMode('block');
25550             shim.dom.style.display = 'none';
25551             shim.dom.style.visibility = 'visible';
25552         }
25553         var pn = this.dom.parentNode;
25554         if(shim.dom.parentNode != pn){
25555             pn.insertBefore(shim.dom, this.dom);
25556         }
25557         shim.setStyle('z-index', this.getZIndex()-2);
25558         this.shim = shim;
25559         return shim;
25560     },
25561
25562     hideShim : function(){
25563         if(this.shim){
25564             this.shim.setDisplayed(false);
25565             shims.push(this.shim);
25566             delete this.shim;
25567         }
25568     },
25569
25570     disableShadow : function(){
25571         if(this.shadow){
25572             this.shadowDisabled = true;
25573             this.shadow.hide();
25574             this.lastShadowOffset = this.shadowOffset;
25575             this.shadowOffset = 0;
25576         }
25577     },
25578
25579     enableShadow : function(show){
25580         if(this.shadow){
25581             this.shadowDisabled = false;
25582             this.shadowOffset = this.lastShadowOffset;
25583             delete this.lastShadowOffset;
25584             if(show){
25585                 this.sync(true);
25586             }
25587         }
25588     },
25589
25590     // private
25591     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
25592     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
25593     sync : function(doShow){
25594         var sw = this.shadow;
25595         if(!this.updating && this.isVisible() && (sw || this.useShim)){
25596             var sh = this.getShim();
25597
25598             var w = this.getWidth(),
25599                 h = this.getHeight();
25600
25601             var l = this.getLeft(true),
25602                 t = this.getTop(true);
25603
25604             if(sw && !this.shadowDisabled){
25605                 if(doShow && !sw.isVisible()){
25606                     sw.show(this);
25607                 }else{
25608                     sw.realign(l, t, w, h);
25609                 }
25610                 if(sh){
25611                     if(doShow){
25612                        sh.show();
25613                     }
25614                     // fit the shim behind the shadow, so it is shimmed too
25615                     var a = sw.adjusts, s = sh.dom.style;
25616                     s.left = (Math.min(l, l+a.l))+"px";
25617                     s.top = (Math.min(t, t+a.t))+"px";
25618                     s.width = (w+a.w)+"px";
25619                     s.height = (h+a.h)+"px";
25620                 }
25621             }else if(sh){
25622                 if(doShow){
25623                    sh.show();
25624                 }
25625                 sh.setSize(w, h);
25626                 sh.setLeftTop(l, t);
25627             }
25628             
25629         }
25630     },
25631
25632     // private
25633     destroy : function(){
25634         this.hideShim();
25635         if(this.shadow){
25636             this.shadow.hide();
25637         }
25638         this.removeAllListeners();
25639         var pn = this.dom.parentNode;
25640         if(pn){
25641             pn.removeChild(this.dom);
25642         }
25643         Roo.Element.uncache(this.id);
25644     },
25645
25646     remove : function(){
25647         this.destroy();
25648     },
25649
25650     // private
25651     beginUpdate : function(){
25652         this.updating = true;
25653     },
25654
25655     // private
25656     endUpdate : function(){
25657         this.updating = false;
25658         this.sync(true);
25659     },
25660
25661     // private
25662     hideUnders : function(negOffset){
25663         if(this.shadow){
25664             this.shadow.hide();
25665         }
25666         this.hideShim();
25667     },
25668
25669     // private
25670     constrainXY : function(){
25671         if(this.constrain){
25672             var vw = Roo.lib.Dom.getViewWidth(),
25673                 vh = Roo.lib.Dom.getViewHeight();
25674             var s = Roo.get(document).getScroll();
25675
25676             var xy = this.getXY();
25677             var x = xy[0], y = xy[1];   
25678             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
25679             // only move it if it needs it
25680             var moved = false;
25681             // first validate right/bottom
25682             if((x + w) > vw+s.left){
25683                 x = vw - w - this.shadowOffset;
25684                 moved = true;
25685             }
25686             if((y + h) > vh+s.top){
25687                 y = vh - h - this.shadowOffset;
25688                 moved = true;
25689             }
25690             // then make sure top/left isn't negative
25691             if(x < s.left){
25692                 x = s.left;
25693                 moved = true;
25694             }
25695             if(y < s.top){
25696                 y = s.top;
25697                 moved = true;
25698             }
25699             if(moved){
25700                 if(this.avoidY){
25701                     var ay = this.avoidY;
25702                     if(y <= ay && (y+h) >= ay){
25703                         y = ay-h-5;   
25704                     }
25705                 }
25706                 xy = [x, y];
25707                 this.storeXY(xy);
25708                 supr.setXY.call(this, xy);
25709                 this.sync();
25710             }
25711         }
25712     },
25713
25714     isVisible : function(){
25715         return this.visible;    
25716     },
25717
25718     // private
25719     showAction : function(){
25720         this.visible = true; // track visibility to prevent getStyle calls
25721         if(this.useDisplay === true){
25722             this.setDisplayed("");
25723         }else if(this.lastXY){
25724             supr.setXY.call(this, this.lastXY);
25725         }else if(this.lastLT){
25726             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
25727         }
25728     },
25729
25730     // private
25731     hideAction : function(){
25732         this.visible = false;
25733         if(this.useDisplay === true){
25734             this.setDisplayed(false);
25735         }else{
25736             this.setLeftTop(-10000,-10000);
25737         }
25738     },
25739
25740     // overridden Element method
25741     setVisible : function(v, a, d, c, e){
25742         if(v){
25743             this.showAction();
25744         }
25745         if(a && v){
25746             var cb = function(){
25747                 this.sync(true);
25748                 if(c){
25749                     c();
25750                 }
25751             }.createDelegate(this);
25752             supr.setVisible.call(this, true, true, d, cb, e);
25753         }else{
25754             if(!v){
25755                 this.hideUnders(true);
25756             }
25757             var cb = c;
25758             if(a){
25759                 cb = function(){
25760                     this.hideAction();
25761                     if(c){
25762                         c();
25763                     }
25764                 }.createDelegate(this);
25765             }
25766             supr.setVisible.call(this, v, a, d, cb, e);
25767             if(v){
25768                 this.sync(true);
25769             }else if(!a){
25770                 this.hideAction();
25771             }
25772         }
25773     },
25774
25775     storeXY : function(xy){
25776         delete this.lastLT;
25777         this.lastXY = xy;
25778     },
25779
25780     storeLeftTop : function(left, top){
25781         delete this.lastXY;
25782         this.lastLT = [left, top];
25783     },
25784
25785     // private
25786     beforeFx : function(){
25787         this.beforeAction();
25788         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
25789     },
25790
25791     // private
25792     afterFx : function(){
25793         Roo.Layer.superclass.afterFx.apply(this, arguments);
25794         this.sync(this.isVisible());
25795     },
25796
25797     // private
25798     beforeAction : function(){
25799         if(!this.updating && this.shadow){
25800             this.shadow.hide();
25801         }
25802     },
25803
25804     // overridden Element method
25805     setLeft : function(left){
25806         this.storeLeftTop(left, this.getTop(true));
25807         supr.setLeft.apply(this, arguments);
25808         this.sync();
25809     },
25810
25811     setTop : function(top){
25812         this.storeLeftTop(this.getLeft(true), top);
25813         supr.setTop.apply(this, arguments);
25814         this.sync();
25815     },
25816
25817     setLeftTop : function(left, top){
25818         this.storeLeftTop(left, top);
25819         supr.setLeftTop.apply(this, arguments);
25820         this.sync();
25821     },
25822
25823     setXY : function(xy, a, d, c, e){
25824         this.fixDisplay();
25825         this.beforeAction();
25826         this.storeXY(xy);
25827         var cb = this.createCB(c);
25828         supr.setXY.call(this, xy, a, d, cb, e);
25829         if(!a){
25830             cb();
25831         }
25832     },
25833
25834     // private
25835     createCB : function(c){
25836         var el = this;
25837         return function(){
25838             el.constrainXY();
25839             el.sync(true);
25840             if(c){
25841                 c();
25842             }
25843         };
25844     },
25845
25846     // overridden Element method
25847     setX : function(x, a, d, c, e){
25848         this.setXY([x, this.getY()], a, d, c, e);
25849     },
25850
25851     // overridden Element method
25852     setY : function(y, a, d, c, e){
25853         this.setXY([this.getX(), y], a, d, c, e);
25854     },
25855
25856     // overridden Element method
25857     setSize : function(w, h, a, d, c, e){
25858         this.beforeAction();
25859         var cb = this.createCB(c);
25860         supr.setSize.call(this, w, h, a, d, cb, e);
25861         if(!a){
25862             cb();
25863         }
25864     },
25865
25866     // overridden Element method
25867     setWidth : function(w, a, d, c, e){
25868         this.beforeAction();
25869         var cb = this.createCB(c);
25870         supr.setWidth.call(this, w, a, d, cb, e);
25871         if(!a){
25872             cb();
25873         }
25874     },
25875
25876     // overridden Element method
25877     setHeight : function(h, a, d, c, e){
25878         this.beforeAction();
25879         var cb = this.createCB(c);
25880         supr.setHeight.call(this, h, a, d, cb, e);
25881         if(!a){
25882             cb();
25883         }
25884     },
25885
25886     // overridden Element method
25887     setBounds : function(x, y, w, h, a, d, c, e){
25888         this.beforeAction();
25889         var cb = this.createCB(c);
25890         if(!a){
25891             this.storeXY([x, y]);
25892             supr.setXY.call(this, [x, y]);
25893             supr.setSize.call(this, w, h, a, d, cb, e);
25894             cb();
25895         }else{
25896             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
25897         }
25898         return this;
25899     },
25900     
25901     /**
25902      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
25903      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
25904      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
25905      * @param {Number} zindex The new z-index to set
25906      * @return {this} The Layer
25907      */
25908     setZIndex : function(zindex){
25909         this.zindex = zindex;
25910         this.setStyle("z-index", zindex + 2);
25911         if(this.shadow){
25912             this.shadow.setZIndex(zindex + 1);
25913         }
25914         if(this.shim){
25915             this.shim.setStyle("z-index", zindex);
25916         }
25917     }
25918 });
25919 })();/*
25920  * Based on:
25921  * Ext JS Library 1.1.1
25922  * Copyright(c) 2006-2007, Ext JS, LLC.
25923  *
25924  * Originally Released Under LGPL - original licence link has changed is not relivant.
25925  *
25926  * Fork - LGPL
25927  * <script type="text/javascript">
25928  */
25929
25930
25931 /**
25932  * @class Roo.Shadow
25933  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
25934  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
25935  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
25936  * @constructor
25937  * Create a new Shadow
25938  * @param {Object} config The config object
25939  */
25940 Roo.Shadow = function(config){
25941     Roo.apply(this, config);
25942     if(typeof this.mode != "string"){
25943         this.mode = this.defaultMode;
25944     }
25945     var o = this.offset, a = {h: 0};
25946     var rad = Math.floor(this.offset/2);
25947     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
25948         case "drop":
25949             a.w = 0;
25950             a.l = a.t = o;
25951             a.t -= 1;
25952             if(Roo.isIE){
25953                 a.l -= this.offset + rad;
25954                 a.t -= this.offset + rad;
25955                 a.w -= rad;
25956                 a.h -= rad;
25957                 a.t += 1;
25958             }
25959         break;
25960         case "sides":
25961             a.w = (o*2);
25962             a.l = -o;
25963             a.t = o-1;
25964             if(Roo.isIE){
25965                 a.l -= (this.offset - rad);
25966                 a.t -= this.offset + rad;
25967                 a.l += 1;
25968                 a.w -= (this.offset - rad)*2;
25969                 a.w -= rad + 1;
25970                 a.h -= 1;
25971             }
25972         break;
25973         case "frame":
25974             a.w = a.h = (o*2);
25975             a.l = a.t = -o;
25976             a.t += 1;
25977             a.h -= 2;
25978             if(Roo.isIE){
25979                 a.l -= (this.offset - rad);
25980                 a.t -= (this.offset - rad);
25981                 a.l += 1;
25982                 a.w -= (this.offset + rad + 1);
25983                 a.h -= (this.offset + rad);
25984                 a.h += 1;
25985             }
25986         break;
25987     };
25988
25989     this.adjusts = a;
25990 };
25991
25992 Roo.Shadow.prototype = {
25993     /**
25994      * @cfg {String} mode
25995      * The shadow display mode.  Supports the following options:<br />
25996      * sides: Shadow displays on both sides and bottom only<br />
25997      * frame: Shadow displays equally on all four sides<br />
25998      * drop: Traditional bottom-right drop shadow (default)
25999      */
26000     /**
26001      * @cfg {String} offset
26002      * The number of pixels to offset the shadow from the element (defaults to 4)
26003      */
26004     offset: 4,
26005
26006     // private
26007     defaultMode: "drop",
26008
26009     /**
26010      * Displays the shadow under the target element
26011      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
26012      */
26013     show : function(target){
26014         target = Roo.get(target);
26015         if(!this.el){
26016             this.el = Roo.Shadow.Pool.pull();
26017             if(this.el.dom.nextSibling != target.dom){
26018                 this.el.insertBefore(target);
26019             }
26020         }
26021         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
26022         if(Roo.isIE){
26023             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
26024         }
26025         this.realign(
26026             target.getLeft(true),
26027             target.getTop(true),
26028             target.getWidth(),
26029             target.getHeight()
26030         );
26031         this.el.dom.style.display = "block";
26032     },
26033
26034     /**
26035      * Returns true if the shadow is visible, else false
26036      */
26037     isVisible : function(){
26038         return this.el ? true : false;  
26039     },
26040
26041     /**
26042      * Direct alignment when values are already available. Show must be called at least once before
26043      * calling this method to ensure it is initialized.
26044      * @param {Number} left The target element left position
26045      * @param {Number} top The target element top position
26046      * @param {Number} width The target element width
26047      * @param {Number} height The target element height
26048      */
26049     realign : function(l, t, w, h){
26050         if(!this.el){
26051             return;
26052         }
26053         var a = this.adjusts, d = this.el.dom, s = d.style;
26054         var iea = 0;
26055         s.left = (l+a.l)+"px";
26056         s.top = (t+a.t)+"px";
26057         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
26058  
26059         if(s.width != sws || s.height != shs){
26060             s.width = sws;
26061             s.height = shs;
26062             if(!Roo.isIE){
26063                 var cn = d.childNodes;
26064                 var sww = Math.max(0, (sw-12))+"px";
26065                 cn[0].childNodes[1].style.width = sww;
26066                 cn[1].childNodes[1].style.width = sww;
26067                 cn[2].childNodes[1].style.width = sww;
26068                 cn[1].style.height = Math.max(0, (sh-12))+"px";
26069             }
26070         }
26071     },
26072
26073     /**
26074      * Hides this shadow
26075      */
26076     hide : function(){
26077         if(this.el){
26078             this.el.dom.style.display = "none";
26079             Roo.Shadow.Pool.push(this.el);
26080             delete this.el;
26081         }
26082     },
26083
26084     /**
26085      * Adjust the z-index of this shadow
26086      * @param {Number} zindex The new z-index
26087      */
26088     setZIndex : function(z){
26089         this.zIndex = z;
26090         if(this.el){
26091             this.el.setStyle("z-index", z);
26092         }
26093     }
26094 };
26095
26096 // Private utility class that manages the internal Shadow cache
26097 Roo.Shadow.Pool = function(){
26098     var p = [];
26099     var markup = Roo.isIE ?
26100                  '<div class="x-ie-shadow"></div>' :
26101                  '<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>';
26102     return {
26103         pull : function(){
26104             var sh = p.shift();
26105             if(!sh){
26106                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
26107                 sh.autoBoxAdjust = false;
26108             }
26109             return sh;
26110         },
26111
26112         push : function(sh){
26113             p.push(sh);
26114         }
26115     };
26116 }();/*
26117  * Based on:
26118  * Ext JS Library 1.1.1
26119  * Copyright(c) 2006-2007, Ext JS, LLC.
26120  *
26121  * Originally Released Under LGPL - original licence link has changed is not relivant.
26122  *
26123  * Fork - LGPL
26124  * <script type="text/javascript">
26125  */
26126
26127
26128 /**
26129  * @class Roo.SplitBar
26130  * @extends Roo.util.Observable
26131  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
26132  * <br><br>
26133  * Usage:
26134  * <pre><code>
26135 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
26136                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
26137 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
26138 split.minSize = 100;
26139 split.maxSize = 600;
26140 split.animate = true;
26141 split.on('moved', splitterMoved);
26142 </code></pre>
26143  * @constructor
26144  * Create a new SplitBar
26145  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
26146  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
26147  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
26148  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
26149                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
26150                         position of the SplitBar).
26151  */
26152 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
26153     
26154     /** @private */
26155     this.el = Roo.get(dragElement, true);
26156     this.el.dom.unselectable = "on";
26157     /** @private */
26158     this.resizingEl = Roo.get(resizingElement, true);
26159
26160     /**
26161      * @private
26162      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
26163      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
26164      * @type Number
26165      */
26166     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
26167     
26168     /**
26169      * The minimum size of the resizing element. (Defaults to 0)
26170      * @type Number
26171      */
26172     this.minSize = 0;
26173     
26174     /**
26175      * The maximum size of the resizing element. (Defaults to 2000)
26176      * @type Number
26177      */
26178     this.maxSize = 2000;
26179     
26180     /**
26181      * Whether to animate the transition to the new size
26182      * @type Boolean
26183      */
26184     this.animate = false;
26185     
26186     /**
26187      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
26188      * @type Boolean
26189      */
26190     this.useShim = false;
26191     
26192     /** @private */
26193     this.shim = null;
26194     
26195     if(!existingProxy){
26196         /** @private */
26197         this.proxy = Roo.SplitBar.createProxy(this.orientation);
26198     }else{
26199         this.proxy = Roo.get(existingProxy).dom;
26200     }
26201     /** @private */
26202     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
26203     
26204     /** @private */
26205     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
26206     
26207     /** @private */
26208     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
26209     
26210     /** @private */
26211     this.dragSpecs = {};
26212     
26213     /**
26214      * @private The adapter to use to positon and resize elements
26215      */
26216     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
26217     this.adapter.init(this);
26218     
26219     if(this.orientation == Roo.SplitBar.HORIZONTAL){
26220         /** @private */
26221         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
26222         this.el.addClass("x-splitbar-h");
26223     }else{
26224         /** @private */
26225         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
26226         this.el.addClass("x-splitbar-v");
26227     }
26228     
26229     this.addEvents({
26230         /**
26231          * @event resize
26232          * Fires when the splitter is moved (alias for {@link #event-moved})
26233          * @param {Roo.SplitBar} this
26234          * @param {Number} newSize the new width or height
26235          */
26236         "resize" : true,
26237         /**
26238          * @event moved
26239          * Fires when the splitter is moved
26240          * @param {Roo.SplitBar} this
26241          * @param {Number} newSize the new width or height
26242          */
26243         "moved" : true,
26244         /**
26245          * @event beforeresize
26246          * Fires before the splitter is dragged
26247          * @param {Roo.SplitBar} this
26248          */
26249         "beforeresize" : true,
26250
26251         "beforeapply" : true
26252     });
26253
26254     Roo.util.Observable.call(this);
26255 };
26256
26257 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
26258     onStartProxyDrag : function(x, y){
26259         this.fireEvent("beforeresize", this);
26260         if(!this.overlay){
26261             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
26262             o.unselectable();
26263             o.enableDisplayMode("block");
26264             // all splitbars share the same overlay
26265             Roo.SplitBar.prototype.overlay = o;
26266         }
26267         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
26268         this.overlay.show();
26269         Roo.get(this.proxy).setDisplayed("block");
26270         var size = this.adapter.getElementSize(this);
26271         this.activeMinSize = this.getMinimumSize();;
26272         this.activeMaxSize = this.getMaximumSize();;
26273         var c1 = size - this.activeMinSize;
26274         var c2 = Math.max(this.activeMaxSize - size, 0);
26275         if(this.orientation == Roo.SplitBar.HORIZONTAL){
26276             this.dd.resetConstraints();
26277             this.dd.setXConstraint(
26278                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
26279                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
26280             );
26281             this.dd.setYConstraint(0, 0);
26282         }else{
26283             this.dd.resetConstraints();
26284             this.dd.setXConstraint(0, 0);
26285             this.dd.setYConstraint(
26286                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
26287                 this.placement == Roo.SplitBar.TOP ? c2 : c1
26288             );
26289          }
26290         this.dragSpecs.startSize = size;
26291         this.dragSpecs.startPoint = [x, y];
26292         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
26293     },
26294     
26295     /** 
26296      * @private Called after the drag operation by the DDProxy
26297      */
26298     onEndProxyDrag : function(e){
26299         Roo.get(this.proxy).setDisplayed(false);
26300         var endPoint = Roo.lib.Event.getXY(e);
26301         if(this.overlay){
26302             this.overlay.hide();
26303         }
26304         var newSize;
26305         if(this.orientation == Roo.SplitBar.HORIZONTAL){
26306             newSize = this.dragSpecs.startSize + 
26307                 (this.placement == Roo.SplitBar.LEFT ?
26308                     endPoint[0] - this.dragSpecs.startPoint[0] :
26309                     this.dragSpecs.startPoint[0] - endPoint[0]
26310                 );
26311         }else{
26312             newSize = this.dragSpecs.startSize + 
26313                 (this.placement == Roo.SplitBar.TOP ?
26314                     endPoint[1] - this.dragSpecs.startPoint[1] :
26315                     this.dragSpecs.startPoint[1] - endPoint[1]
26316                 );
26317         }
26318         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
26319         if(newSize != this.dragSpecs.startSize){
26320             if(this.fireEvent('beforeapply', this, newSize) !== false){
26321                 this.adapter.setElementSize(this, newSize);
26322                 this.fireEvent("moved", this, newSize);
26323                 this.fireEvent("resize", this, newSize);
26324             }
26325         }
26326     },
26327     
26328     /**
26329      * Get the adapter this SplitBar uses
26330      * @return The adapter object
26331      */
26332     getAdapter : function(){
26333         return this.adapter;
26334     },
26335     
26336     /**
26337      * Set the adapter this SplitBar uses
26338      * @param {Object} adapter A SplitBar adapter object
26339      */
26340     setAdapter : function(adapter){
26341         this.adapter = adapter;
26342         this.adapter.init(this);
26343     },
26344     
26345     /**
26346      * Gets the minimum size for the resizing element
26347      * @return {Number} The minimum size
26348      */
26349     getMinimumSize : function(){
26350         return this.minSize;
26351     },
26352     
26353     /**
26354      * Sets the minimum size for the resizing element
26355      * @param {Number} minSize The minimum size
26356      */
26357     setMinimumSize : function(minSize){
26358         this.minSize = minSize;
26359     },
26360     
26361     /**
26362      * Gets the maximum size for the resizing element
26363      * @return {Number} The maximum size
26364      */
26365     getMaximumSize : function(){
26366         return this.maxSize;
26367     },
26368     
26369     /**
26370      * Sets the maximum size for the resizing element
26371      * @param {Number} maxSize The maximum size
26372      */
26373     setMaximumSize : function(maxSize){
26374         this.maxSize = maxSize;
26375     },
26376     
26377     /**
26378      * Sets the initialize size for the resizing element
26379      * @param {Number} size The initial size
26380      */
26381     setCurrentSize : function(size){
26382         var oldAnimate = this.animate;
26383         this.animate = false;
26384         this.adapter.setElementSize(this, size);
26385         this.animate = oldAnimate;
26386     },
26387     
26388     /**
26389      * Destroy this splitbar. 
26390      * @param {Boolean} removeEl True to remove the element
26391      */
26392     destroy : function(removeEl){
26393         if(this.shim){
26394             this.shim.remove();
26395         }
26396         this.dd.unreg();
26397         this.proxy.parentNode.removeChild(this.proxy);
26398         if(removeEl){
26399             this.el.remove();
26400         }
26401     }
26402 });
26403
26404 /**
26405  * @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.
26406  */
26407 Roo.SplitBar.createProxy = function(dir){
26408     var proxy = new Roo.Element(document.createElement("div"));
26409     proxy.unselectable();
26410     var cls = 'x-splitbar-proxy';
26411     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
26412     document.body.appendChild(proxy.dom);
26413     return proxy.dom;
26414 };
26415
26416 /** 
26417  * @class Roo.SplitBar.BasicLayoutAdapter
26418  * Default Adapter. It assumes the splitter and resizing element are not positioned
26419  * elements and only gets/sets the width of the element. Generally used for table based layouts.
26420  */
26421 Roo.SplitBar.BasicLayoutAdapter = function(){
26422 };
26423
26424 Roo.SplitBar.BasicLayoutAdapter.prototype = {
26425     // do nothing for now
26426     init : function(s){
26427     
26428     },
26429     /**
26430      * Called before drag operations to get the current size of the resizing element. 
26431      * @param {Roo.SplitBar} s The SplitBar using this adapter
26432      */
26433      getElementSize : function(s){
26434         if(s.orientation == Roo.SplitBar.HORIZONTAL){
26435             return s.resizingEl.getWidth();
26436         }else{
26437             return s.resizingEl.getHeight();
26438         }
26439     },
26440     
26441     /**
26442      * Called after drag operations to set the size of the resizing element.
26443      * @param {Roo.SplitBar} s The SplitBar using this adapter
26444      * @param {Number} newSize The new size to set
26445      * @param {Function} onComplete A function to be invoked when resizing is complete
26446      */
26447     setElementSize : function(s, newSize, onComplete){
26448         if(s.orientation == Roo.SplitBar.HORIZONTAL){
26449             if(!s.animate){
26450                 s.resizingEl.setWidth(newSize);
26451                 if(onComplete){
26452                     onComplete(s, newSize);
26453                 }
26454             }else{
26455                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
26456             }
26457         }else{
26458             
26459             if(!s.animate){
26460                 s.resizingEl.setHeight(newSize);
26461                 if(onComplete){
26462                     onComplete(s, newSize);
26463                 }
26464             }else{
26465                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
26466             }
26467         }
26468     }
26469 };
26470
26471 /** 
26472  *@class Roo.SplitBar.AbsoluteLayoutAdapter
26473  * @extends Roo.SplitBar.BasicLayoutAdapter
26474  * Adapter that  moves the splitter element to align with the resized sizing element. 
26475  * Used with an absolute positioned SplitBar.
26476  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
26477  * document.body, make sure you assign an id to the body element.
26478  */
26479 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
26480     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
26481     this.container = Roo.get(container);
26482 };
26483
26484 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
26485     init : function(s){
26486         this.basic.init(s);
26487     },
26488     
26489     getElementSize : function(s){
26490         return this.basic.getElementSize(s);
26491     },
26492     
26493     setElementSize : function(s, newSize, onComplete){
26494         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
26495     },
26496     
26497     moveSplitter : function(s){
26498         var yes = Roo.SplitBar;
26499         switch(s.placement){
26500             case yes.LEFT:
26501                 s.el.setX(s.resizingEl.getRight());
26502                 break;
26503             case yes.RIGHT:
26504                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
26505                 break;
26506             case yes.TOP:
26507                 s.el.setY(s.resizingEl.getBottom());
26508                 break;
26509             case yes.BOTTOM:
26510                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
26511                 break;
26512         }
26513     }
26514 };
26515
26516 /**
26517  * Orientation constant - Create a vertical SplitBar
26518  * @static
26519  * @type Number
26520  */
26521 Roo.SplitBar.VERTICAL = 1;
26522
26523 /**
26524  * Orientation constant - Create a horizontal SplitBar
26525  * @static
26526  * @type Number
26527  */
26528 Roo.SplitBar.HORIZONTAL = 2;
26529
26530 /**
26531  * Placement constant - The resizing element is to the left of the splitter element
26532  * @static
26533  * @type Number
26534  */
26535 Roo.SplitBar.LEFT = 1;
26536
26537 /**
26538  * Placement constant - The resizing element is to the right of the splitter element
26539  * @static
26540  * @type Number
26541  */
26542 Roo.SplitBar.RIGHT = 2;
26543
26544 /**
26545  * Placement constant - The resizing element is positioned above the splitter element
26546  * @static
26547  * @type Number
26548  */
26549 Roo.SplitBar.TOP = 3;
26550
26551 /**
26552  * Placement constant - The resizing element is positioned under splitter element
26553  * @static
26554  * @type Number
26555  */
26556 Roo.SplitBar.BOTTOM = 4;
26557 /*
26558  * Based on:
26559  * Ext JS Library 1.1.1
26560  * Copyright(c) 2006-2007, Ext JS, LLC.
26561  *
26562  * Originally Released Under LGPL - original licence link has changed is not relivant.
26563  *
26564  * Fork - LGPL
26565  * <script type="text/javascript">
26566  */
26567
26568 /**
26569  * @class Roo.View
26570  * @extends Roo.util.Observable
26571  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
26572  * This class also supports single and multi selection modes. <br>
26573  * Create a data model bound view:
26574  <pre><code>
26575  var store = new Roo.data.Store(...);
26576
26577  var view = new Roo.View({
26578     el : "my-element",
26579     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
26580  
26581     singleSelect: true,
26582     selectedClass: "ydataview-selected",
26583     store: store
26584  });
26585
26586  // listen for node click?
26587  view.on("click", function(vw, index, node, e){
26588  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
26589  });
26590
26591  // load XML data
26592  dataModel.load("foobar.xml");
26593  </code></pre>
26594  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
26595  * <br><br>
26596  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
26597  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
26598  * 
26599  * Note: old style constructor is still suported (container, template, config)
26600  * 
26601  * @constructor
26602  * Create a new View
26603  * @param {Object} config The config object
26604  * 
26605  */
26606 Roo.View = function(config, depreciated_tpl, depreciated_config){
26607     
26608     this.parent = false;
26609     
26610     if (typeof(depreciated_tpl) == 'undefined') {
26611         // new way.. - universal constructor.
26612         Roo.apply(this, config);
26613         this.el  = Roo.get(this.el);
26614     } else {
26615         // old format..
26616         this.el  = Roo.get(config);
26617         this.tpl = depreciated_tpl;
26618         Roo.apply(this, depreciated_config);
26619     }
26620     this.wrapEl  = this.el.wrap().wrap();
26621     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
26622     
26623     
26624     if(typeof(this.tpl) == "string"){
26625         this.tpl = new Roo.Template(this.tpl);
26626     } else {
26627         // support xtype ctors..
26628         this.tpl = new Roo.factory(this.tpl, Roo);
26629     }
26630     
26631     
26632     this.tpl.compile();
26633     
26634     /** @private */
26635     this.addEvents({
26636         /**
26637          * @event beforeclick
26638          * Fires before a click is processed. Returns false to cancel the default action.
26639          * @param {Roo.View} this
26640          * @param {Number} index The index of the target node
26641          * @param {HTMLElement} node The target node
26642          * @param {Roo.EventObject} e The raw event object
26643          */
26644             "beforeclick" : true,
26645         /**
26646          * @event click
26647          * Fires when a template node is clicked.
26648          * @param {Roo.View} this
26649          * @param {Number} index The index of the target node
26650          * @param {HTMLElement} node The target node
26651          * @param {Roo.EventObject} e The raw event object
26652          */
26653             "click" : true,
26654         /**
26655          * @event dblclick
26656          * Fires when a template node is double clicked.
26657          * @param {Roo.View} this
26658          * @param {Number} index The index of the target node
26659          * @param {HTMLElement} node The target node
26660          * @param {Roo.EventObject} e The raw event object
26661          */
26662             "dblclick" : true,
26663         /**
26664          * @event contextmenu
26665          * Fires when a template node is right clicked.
26666          * @param {Roo.View} this
26667          * @param {Number} index The index of the target node
26668          * @param {HTMLElement} node The target node
26669          * @param {Roo.EventObject} e The raw event object
26670          */
26671             "contextmenu" : true,
26672         /**
26673          * @event selectionchange
26674          * Fires when the selected nodes change.
26675          * @param {Roo.View} this
26676          * @param {Array} selections Array of the selected nodes
26677          */
26678             "selectionchange" : true,
26679     
26680         /**
26681          * @event beforeselect
26682          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
26683          * @param {Roo.View} this
26684          * @param {HTMLElement} node The node to be selected
26685          * @param {Array} selections Array of currently selected nodes
26686          */
26687             "beforeselect" : true,
26688         /**
26689          * @event preparedata
26690          * Fires on every row to render, to allow you to change the data.
26691          * @param {Roo.View} this
26692          * @param {Object} data to be rendered (change this)
26693          */
26694           "preparedata" : true
26695           
26696           
26697         });
26698
26699
26700
26701     this.el.on({
26702         "click": this.onClick,
26703         "dblclick": this.onDblClick,
26704         "contextmenu": this.onContextMenu,
26705         scope:this
26706     });
26707
26708     this.selections = [];
26709     this.nodes = [];
26710     this.cmp = new Roo.CompositeElementLite([]);
26711     if(this.store){
26712         this.store = Roo.factory(this.store, Roo.data);
26713         this.setStore(this.store, true);
26714     }
26715     
26716     if ( this.footer && this.footer.xtype) {
26717            
26718          var fctr = this.wrapEl.appendChild(document.createElement("div"));
26719         
26720         this.footer.dataSource = this.store;
26721         this.footer.container = fctr;
26722         this.footer = Roo.factory(this.footer, Roo);
26723         fctr.insertFirst(this.el);
26724         
26725         // this is a bit insane - as the paging toolbar seems to detach the el..
26726 //        dom.parentNode.parentNode.parentNode
26727          // they get detached?
26728     }
26729     
26730     
26731     Roo.View.superclass.constructor.call(this);
26732     
26733     
26734 };
26735
26736 Roo.extend(Roo.View, Roo.util.Observable, {
26737     
26738      /**
26739      * @cfg {Roo.data.Store} store Data store to load data from.
26740      */
26741     store : false,
26742     
26743     /**
26744      * @cfg {String|Roo.Element} el The container element.
26745      */
26746     el : '',
26747     
26748     /**
26749      * @cfg {String|Roo.Template} tpl The template used by this View 
26750      */
26751     tpl : false,
26752     /**
26753      * @cfg {String} dataName the named area of the template to use as the data area
26754      *                          Works with domtemplates roo-name="name"
26755      */
26756     dataName: false,
26757     /**
26758      * @cfg {String} selectedClass The css class to add to selected nodes
26759      */
26760     selectedClass : "x-view-selected",
26761      /**
26762      * @cfg {String} emptyText The empty text to show when nothing is loaded.
26763      */
26764     emptyText : "",
26765     
26766     /**
26767      * @cfg {String} text to display on mask (default Loading)
26768      */
26769     mask : false,
26770     /**
26771      * @cfg {Boolean} multiSelect Allow multiple selection
26772      */
26773     multiSelect : false,
26774     /**
26775      * @cfg {Boolean} singleSelect Allow single selection
26776      */
26777     singleSelect:  false,
26778     
26779     /**
26780      * @cfg {Boolean} toggleSelect - selecting 
26781      */
26782     toggleSelect : false,
26783     
26784     /**
26785      * @cfg {Boolean} tickable - selecting 
26786      */
26787     tickable : false,
26788     
26789     /**
26790      * Returns the element this view is bound to.
26791      * @return {Roo.Element}
26792      */
26793     getEl : function(){
26794         return this.wrapEl;
26795     },
26796     
26797     
26798
26799     /**
26800      * Refreshes the view. - called by datachanged on the store. - do not call directly.
26801      */
26802     refresh : function(){
26803         //Roo.log('refresh');
26804         var t = this.tpl;
26805         
26806         // if we are using something like 'domtemplate', then
26807         // the what gets used is:
26808         // t.applySubtemplate(NAME, data, wrapping data..)
26809         // the outer template then get' applied with
26810         //     the store 'extra data'
26811         // and the body get's added to the
26812         //      roo-name="data" node?
26813         //      <span class='roo-tpl-{name}'></span> ?????
26814         
26815         
26816         
26817         this.clearSelections();
26818         this.el.update("");
26819         var html = [];
26820         var records = this.store.getRange();
26821         if(records.length < 1) {
26822             
26823             // is this valid??  = should it render a template??
26824             
26825             this.el.update(this.emptyText);
26826             return;
26827         }
26828         var el = this.el;
26829         if (this.dataName) {
26830             this.el.update(t.apply(this.store.meta)); //????
26831             el = this.el.child('.roo-tpl-' + this.dataName);
26832         }
26833         
26834         for(var i = 0, len = records.length; i < len; i++){
26835             var data = this.prepareData(records[i].data, i, records[i]);
26836             this.fireEvent("preparedata", this, data, i, records[i]);
26837             
26838             var d = Roo.apply({}, data);
26839             
26840             if(this.tickable){
26841                 Roo.apply(d, {'roo-id' : Roo.id()});
26842                 
26843                 var _this = this;
26844             
26845                 Roo.each(this.parent.item, function(item){
26846                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
26847                         return;
26848                     }
26849                     Roo.apply(d, {'roo-data-checked' : 'checked'});
26850                 });
26851             }
26852             
26853             html[html.length] = Roo.util.Format.trim(
26854                 this.dataName ?
26855                     t.applySubtemplate(this.dataName, d, this.store.meta) :
26856                     t.apply(d)
26857             );
26858         }
26859         
26860         
26861         
26862         el.update(html.join(""));
26863         this.nodes = el.dom.childNodes;
26864         this.updateIndexes(0);
26865     },
26866     
26867
26868     /**
26869      * Function to override to reformat the data that is sent to
26870      * the template for each node.
26871      * DEPRICATED - use the preparedata event handler.
26872      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
26873      * a JSON object for an UpdateManager bound view).
26874      */
26875     prepareData : function(data, index, record)
26876     {
26877         this.fireEvent("preparedata", this, data, index, record);
26878         return data;
26879     },
26880
26881     onUpdate : function(ds, record){
26882         // Roo.log('on update');   
26883         this.clearSelections();
26884         var index = this.store.indexOf(record);
26885         var n = this.nodes[index];
26886         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
26887         n.parentNode.removeChild(n);
26888         this.updateIndexes(index, index);
26889     },
26890
26891     
26892     
26893 // --------- FIXME     
26894     onAdd : function(ds, records, index)
26895     {
26896         //Roo.log(['on Add', ds, records, index] );        
26897         this.clearSelections();
26898         if(this.nodes.length == 0){
26899             this.refresh();
26900             return;
26901         }
26902         var n = this.nodes[index];
26903         for(var i = 0, len = records.length; i < len; i++){
26904             var d = this.prepareData(records[i].data, i, records[i]);
26905             if(n){
26906                 this.tpl.insertBefore(n, d);
26907             }else{
26908                 
26909                 this.tpl.append(this.el, d);
26910             }
26911         }
26912         this.updateIndexes(index);
26913     },
26914
26915     onRemove : function(ds, record, index){
26916        // Roo.log('onRemove');
26917         this.clearSelections();
26918         var el = this.dataName  ?
26919             this.el.child('.roo-tpl-' + this.dataName) :
26920             this.el; 
26921         
26922         el.dom.removeChild(this.nodes[index]);
26923         this.updateIndexes(index);
26924     },
26925
26926     /**
26927      * Refresh an individual node.
26928      * @param {Number} index
26929      */
26930     refreshNode : function(index){
26931         this.onUpdate(this.store, this.store.getAt(index));
26932     },
26933
26934     updateIndexes : function(startIndex, endIndex){
26935         var ns = this.nodes;
26936         startIndex = startIndex || 0;
26937         endIndex = endIndex || ns.length - 1;
26938         for(var i = startIndex; i <= endIndex; i++){
26939             ns[i].nodeIndex = i;
26940         }
26941     },
26942
26943     /**
26944      * Changes the data store this view uses and refresh the view.
26945      * @param {Store} store
26946      */
26947     setStore : function(store, initial){
26948         if(!initial && this.store){
26949             this.store.un("datachanged", this.refresh);
26950             this.store.un("add", this.onAdd);
26951             this.store.un("remove", this.onRemove);
26952             this.store.un("update", this.onUpdate);
26953             this.store.un("clear", this.refresh);
26954             this.store.un("beforeload", this.onBeforeLoad);
26955             this.store.un("load", this.onLoad);
26956             this.store.un("loadexception", this.onLoad);
26957         }
26958         if(store){
26959           
26960             store.on("datachanged", this.refresh, this);
26961             store.on("add", this.onAdd, this);
26962             store.on("remove", this.onRemove, this);
26963             store.on("update", this.onUpdate, this);
26964             store.on("clear", this.refresh, this);
26965             store.on("beforeload", this.onBeforeLoad, this);
26966             store.on("load", this.onLoad, this);
26967             store.on("loadexception", this.onLoad, this);
26968         }
26969         
26970         if(store){
26971             this.refresh();
26972         }
26973     },
26974     /**
26975      * onbeforeLoad - masks the loading area.
26976      *
26977      */
26978     onBeforeLoad : function(store,opts)
26979     {
26980          //Roo.log('onBeforeLoad');   
26981         if (!opts.add) {
26982             this.el.update("");
26983         }
26984         this.el.mask(this.mask ? this.mask : "Loading" ); 
26985     },
26986     onLoad : function ()
26987     {
26988         this.el.unmask();
26989     },
26990     
26991
26992     /**
26993      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
26994      * @param {HTMLElement} node
26995      * @return {HTMLElement} The template node
26996      */
26997     findItemFromChild : function(node){
26998         var el = this.dataName  ?
26999             this.el.child('.roo-tpl-' + this.dataName,true) :
27000             this.el.dom; 
27001         
27002         if(!node || node.parentNode == el){
27003                     return node;
27004             }
27005             var p = node.parentNode;
27006             while(p && p != el){
27007             if(p.parentNode == el){
27008                 return p;
27009             }
27010             p = p.parentNode;
27011         }
27012             return null;
27013     },
27014
27015     /** @ignore */
27016     onClick : function(e){
27017         var item = this.findItemFromChild(e.getTarget());
27018         if(item){
27019             var index = this.indexOf(item);
27020             if(this.onItemClick(item, index, e) !== false){
27021                 this.fireEvent("click", this, index, item, e);
27022             }
27023         }else{
27024             this.clearSelections();
27025         }
27026     },
27027
27028     /** @ignore */
27029     onContextMenu : function(e){
27030         var item = this.findItemFromChild(e.getTarget());
27031         if(item){
27032             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
27033         }
27034     },
27035
27036     /** @ignore */
27037     onDblClick : function(e){
27038         var item = this.findItemFromChild(e.getTarget());
27039         if(item){
27040             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
27041         }
27042     },
27043
27044     onItemClick : function(item, index, e)
27045     {
27046         if(this.fireEvent("beforeclick", this, index, item, e) === false){
27047             return false;
27048         }
27049         if (this.toggleSelect) {
27050             var m = this.isSelected(item) ? 'unselect' : 'select';
27051             //Roo.log(m);
27052             var _t = this;
27053             _t[m](item, true, false);
27054             return true;
27055         }
27056         if(this.multiSelect || this.singleSelect){
27057             if(this.multiSelect && e.shiftKey && this.lastSelection){
27058                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
27059             }else{
27060                 this.select(item, this.multiSelect && e.ctrlKey);
27061                 this.lastSelection = item;
27062             }
27063             
27064             if(!this.tickable){
27065                 e.preventDefault();
27066             }
27067             
27068         }
27069         return true;
27070     },
27071
27072     /**
27073      * Get the number of selected nodes.
27074      * @return {Number}
27075      */
27076     getSelectionCount : function(){
27077         return this.selections.length;
27078     },
27079
27080     /**
27081      * Get the currently selected nodes.
27082      * @return {Array} An array of HTMLElements
27083      */
27084     getSelectedNodes : function(){
27085         return this.selections;
27086     },
27087
27088     /**
27089      * Get the indexes of the selected nodes.
27090      * @return {Array}
27091      */
27092     getSelectedIndexes : function(){
27093         var indexes = [], s = this.selections;
27094         for(var i = 0, len = s.length; i < len; i++){
27095             indexes.push(s[i].nodeIndex);
27096         }
27097         return indexes;
27098     },
27099
27100     /**
27101      * Clear all selections
27102      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
27103      */
27104     clearSelections : function(suppressEvent){
27105         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
27106             this.cmp.elements = this.selections;
27107             this.cmp.removeClass(this.selectedClass);
27108             this.selections = [];
27109             if(!suppressEvent){
27110                 this.fireEvent("selectionchange", this, this.selections);
27111             }
27112         }
27113     },
27114
27115     /**
27116      * Returns true if the passed node is selected
27117      * @param {HTMLElement/Number} node The node or node index
27118      * @return {Boolean}
27119      */
27120     isSelected : function(node){
27121         var s = this.selections;
27122         if(s.length < 1){
27123             return false;
27124         }
27125         node = this.getNode(node);
27126         return s.indexOf(node) !== -1;
27127     },
27128
27129     /**
27130      * Selects nodes.
27131      * @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
27132      * @param {Boolean} keepExisting (optional) true to keep existing selections
27133      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
27134      */
27135     select : function(nodeInfo, keepExisting, suppressEvent){
27136         if(nodeInfo instanceof Array){
27137             if(!keepExisting){
27138                 this.clearSelections(true);
27139             }
27140             for(var i = 0, len = nodeInfo.length; i < len; i++){
27141                 this.select(nodeInfo[i], true, true);
27142             }
27143             return;
27144         } 
27145         var node = this.getNode(nodeInfo);
27146         if(!node || this.isSelected(node)){
27147             return; // already selected.
27148         }
27149         if(!keepExisting){
27150             this.clearSelections(true);
27151         }
27152         
27153         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
27154             Roo.fly(node).addClass(this.selectedClass);
27155             this.selections.push(node);
27156             if(!suppressEvent){
27157                 this.fireEvent("selectionchange", this, this.selections);
27158             }
27159         }
27160         
27161         
27162     },
27163       /**
27164      * Unselects nodes.
27165      * @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
27166      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
27167      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
27168      */
27169     unselect : function(nodeInfo, keepExisting, suppressEvent)
27170     {
27171         if(nodeInfo instanceof Array){
27172             Roo.each(this.selections, function(s) {
27173                 this.unselect(s, nodeInfo);
27174             }, this);
27175             return;
27176         }
27177         var node = this.getNode(nodeInfo);
27178         if(!node || !this.isSelected(node)){
27179             //Roo.log("not selected");
27180             return; // not selected.
27181         }
27182         // fireevent???
27183         var ns = [];
27184         Roo.each(this.selections, function(s) {
27185             if (s == node ) {
27186                 Roo.fly(node).removeClass(this.selectedClass);
27187
27188                 return;
27189             }
27190             ns.push(s);
27191         },this);
27192         
27193         this.selections= ns;
27194         this.fireEvent("selectionchange", this, this.selections);
27195     },
27196
27197     /**
27198      * Gets a template node.
27199      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
27200      * @return {HTMLElement} The node or null if it wasn't found
27201      */
27202     getNode : function(nodeInfo){
27203         if(typeof nodeInfo == "string"){
27204             return document.getElementById(nodeInfo);
27205         }else if(typeof nodeInfo == "number"){
27206             return this.nodes[nodeInfo];
27207         }
27208         return nodeInfo;
27209     },
27210
27211     /**
27212      * Gets a range template nodes.
27213      * @param {Number} startIndex
27214      * @param {Number} endIndex
27215      * @return {Array} An array of nodes
27216      */
27217     getNodes : function(start, end){
27218         var ns = this.nodes;
27219         start = start || 0;
27220         end = typeof end == "undefined" ? ns.length - 1 : end;
27221         var nodes = [];
27222         if(start <= end){
27223             for(var i = start; i <= end; i++){
27224                 nodes.push(ns[i]);
27225             }
27226         } else{
27227             for(var i = start; i >= end; i--){
27228                 nodes.push(ns[i]);
27229             }
27230         }
27231         return nodes;
27232     },
27233
27234     /**
27235      * Finds the index of the passed node
27236      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
27237      * @return {Number} The index of the node or -1
27238      */
27239     indexOf : function(node){
27240         node = this.getNode(node);
27241         if(typeof node.nodeIndex == "number"){
27242             return node.nodeIndex;
27243         }
27244         var ns = this.nodes;
27245         for(var i = 0, len = ns.length; i < len; i++){
27246             if(ns[i] == node){
27247                 return i;
27248             }
27249         }
27250         return -1;
27251     }
27252 });
27253 /*
27254  * Based on:
27255  * Ext JS Library 1.1.1
27256  * Copyright(c) 2006-2007, Ext JS, LLC.
27257  *
27258  * Originally Released Under LGPL - original licence link has changed is not relivant.
27259  *
27260  * Fork - LGPL
27261  * <script type="text/javascript">
27262  */
27263
27264 /**
27265  * @class Roo.JsonView
27266  * @extends Roo.View
27267  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
27268 <pre><code>
27269 var view = new Roo.JsonView({
27270     container: "my-element",
27271     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
27272     multiSelect: true, 
27273     jsonRoot: "data" 
27274 });
27275
27276 // listen for node click?
27277 view.on("click", function(vw, index, node, e){
27278     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
27279 });
27280
27281 // direct load of JSON data
27282 view.load("foobar.php");
27283
27284 // Example from my blog list
27285 var tpl = new Roo.Template(
27286     '&lt;div class="entry"&gt;' +
27287     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
27288     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
27289     "&lt;/div&gt;&lt;hr /&gt;"
27290 );
27291
27292 var moreView = new Roo.JsonView({
27293     container :  "entry-list", 
27294     template : tpl,
27295     jsonRoot: "posts"
27296 });
27297 moreView.on("beforerender", this.sortEntries, this);
27298 moreView.load({
27299     url: "/blog/get-posts.php",
27300     params: "allposts=true",
27301     text: "Loading Blog Entries..."
27302 });
27303 </code></pre>
27304
27305 * Note: old code is supported with arguments : (container, template, config)
27306
27307
27308  * @constructor
27309  * Create a new JsonView
27310  * 
27311  * @param {Object} config The config object
27312  * 
27313  */
27314 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
27315     
27316     
27317     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
27318
27319     var um = this.el.getUpdateManager();
27320     um.setRenderer(this);
27321     um.on("update", this.onLoad, this);
27322     um.on("failure", this.onLoadException, this);
27323
27324     /**
27325      * @event beforerender
27326      * Fires before rendering of the downloaded JSON data.
27327      * @param {Roo.JsonView} this
27328      * @param {Object} data The JSON data loaded
27329      */
27330     /**
27331      * @event load
27332      * Fires when data is loaded.
27333      * @param {Roo.JsonView} this
27334      * @param {Object} data The JSON data loaded
27335      * @param {Object} response The raw Connect response object
27336      */
27337     /**
27338      * @event loadexception
27339      * Fires when loading fails.
27340      * @param {Roo.JsonView} this
27341      * @param {Object} response The raw Connect response object
27342      */
27343     this.addEvents({
27344         'beforerender' : true,
27345         'load' : true,
27346         'loadexception' : true
27347     });
27348 };
27349 Roo.extend(Roo.JsonView, Roo.View, {
27350     /**
27351      * @type {String} The root property in the loaded JSON object that contains the data
27352      */
27353     jsonRoot : "",
27354
27355     /**
27356      * Refreshes the view.
27357      */
27358     refresh : function(){
27359         this.clearSelections();
27360         this.el.update("");
27361         var html = [];
27362         var o = this.jsonData;
27363         if(o && o.length > 0){
27364             for(var i = 0, len = o.length; i < len; i++){
27365                 var data = this.prepareData(o[i], i, o);
27366                 html[html.length] = this.tpl.apply(data);
27367             }
27368         }else{
27369             html.push(this.emptyText);
27370         }
27371         this.el.update(html.join(""));
27372         this.nodes = this.el.dom.childNodes;
27373         this.updateIndexes(0);
27374     },
27375
27376     /**
27377      * 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.
27378      * @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:
27379      <pre><code>
27380      view.load({
27381          url: "your-url.php",
27382          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
27383          callback: yourFunction,
27384          scope: yourObject, //(optional scope)
27385          discardUrl: false,
27386          nocache: false,
27387          text: "Loading...",
27388          timeout: 30,
27389          scripts: false
27390      });
27391      </code></pre>
27392      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
27393      * 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.
27394      * @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}
27395      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
27396      * @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.
27397      */
27398     load : function(){
27399         var um = this.el.getUpdateManager();
27400         um.update.apply(um, arguments);
27401     },
27402
27403     // note - render is a standard framework call...
27404     // using it for the response is really flaky... - it's called by UpdateManager normally, except when called by the XComponent/addXtype.
27405     render : function(el, response){
27406         
27407         this.clearSelections();
27408         this.el.update("");
27409         var o;
27410         try{
27411             if (response != '') {
27412                 o = Roo.util.JSON.decode(response.responseText);
27413                 if(this.jsonRoot){
27414                     
27415                     o = o[this.jsonRoot];
27416                 }
27417             }
27418         } catch(e){
27419         }
27420         /**
27421          * The current JSON data or null
27422          */
27423         this.jsonData = o;
27424         this.beforeRender();
27425         this.refresh();
27426     },
27427
27428 /**
27429  * Get the number of records in the current JSON dataset
27430  * @return {Number}
27431  */
27432     getCount : function(){
27433         return this.jsonData ? this.jsonData.length : 0;
27434     },
27435
27436 /**
27437  * Returns the JSON object for the specified node(s)
27438  * @param {HTMLElement/Array} node The node or an array of nodes
27439  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
27440  * you get the JSON object for the node
27441  */
27442     getNodeData : function(node){
27443         if(node instanceof Array){
27444             var data = [];
27445             for(var i = 0, len = node.length; i < len; i++){
27446                 data.push(this.getNodeData(node[i]));
27447             }
27448             return data;
27449         }
27450         return this.jsonData[this.indexOf(node)] || null;
27451     },
27452
27453     beforeRender : function(){
27454         this.snapshot = this.jsonData;
27455         if(this.sortInfo){
27456             this.sort.apply(this, this.sortInfo);
27457         }
27458         this.fireEvent("beforerender", this, this.jsonData);
27459     },
27460
27461     onLoad : function(el, o){
27462         this.fireEvent("load", this, this.jsonData, o);
27463     },
27464
27465     onLoadException : function(el, o){
27466         this.fireEvent("loadexception", this, o);
27467     },
27468
27469 /**
27470  * Filter the data by a specific property.
27471  * @param {String} property A property on your JSON objects
27472  * @param {String/RegExp} value Either string that the property values
27473  * should start with, or a RegExp to test against the property
27474  */
27475     filter : function(property, value){
27476         if(this.jsonData){
27477             var data = [];
27478             var ss = this.snapshot;
27479             if(typeof value == "string"){
27480                 var vlen = value.length;
27481                 if(vlen == 0){
27482                     this.clearFilter();
27483                     return;
27484                 }
27485                 value = value.toLowerCase();
27486                 for(var i = 0, len = ss.length; i < len; i++){
27487                     var o = ss[i];
27488                     if(o[property].substr(0, vlen).toLowerCase() == value){
27489                         data.push(o);
27490                     }
27491                 }
27492             } else if(value.exec){ // regex?
27493                 for(var i = 0, len = ss.length; i < len; i++){
27494                     var o = ss[i];
27495                     if(value.test(o[property])){
27496                         data.push(o);
27497                     }
27498                 }
27499             } else{
27500                 return;
27501             }
27502             this.jsonData = data;
27503             this.refresh();
27504         }
27505     },
27506
27507 /**
27508  * Filter by a function. The passed function will be called with each
27509  * object in the current dataset. If the function returns true the value is kept,
27510  * otherwise it is filtered.
27511  * @param {Function} fn
27512  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
27513  */
27514     filterBy : function(fn, scope){
27515         if(this.jsonData){
27516             var data = [];
27517             var ss = this.snapshot;
27518             for(var i = 0, len = ss.length; i < len; i++){
27519                 var o = ss[i];
27520                 if(fn.call(scope || this, o)){
27521                     data.push(o);
27522                 }
27523             }
27524             this.jsonData = data;
27525             this.refresh();
27526         }
27527     },
27528
27529 /**
27530  * Clears the current filter.
27531  */
27532     clearFilter : function(){
27533         if(this.snapshot && this.jsonData != this.snapshot){
27534             this.jsonData = this.snapshot;
27535             this.refresh();
27536         }
27537     },
27538
27539
27540 /**
27541  * Sorts the data for this view and refreshes it.
27542  * @param {String} property A property on your JSON objects to sort on
27543  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
27544  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
27545  */
27546     sort : function(property, dir, sortType){
27547         this.sortInfo = Array.prototype.slice.call(arguments, 0);
27548         if(this.jsonData){
27549             var p = property;
27550             var dsc = dir && dir.toLowerCase() == "desc";
27551             var f = function(o1, o2){
27552                 var v1 = sortType ? sortType(o1[p]) : o1[p];
27553                 var v2 = sortType ? sortType(o2[p]) : o2[p];
27554                 ;
27555                 if(v1 < v2){
27556                     return dsc ? +1 : -1;
27557                 } else if(v1 > v2){
27558                     return dsc ? -1 : +1;
27559                 } else{
27560                     return 0;
27561                 }
27562             };
27563             this.jsonData.sort(f);
27564             this.refresh();
27565             if(this.jsonData != this.snapshot){
27566                 this.snapshot.sort(f);
27567             }
27568         }
27569     }
27570 });/*
27571  * Based on:
27572  * Ext JS Library 1.1.1
27573  * Copyright(c) 2006-2007, Ext JS, LLC.
27574  *
27575  * Originally Released Under LGPL - original licence link has changed is not relivant.
27576  *
27577  * Fork - LGPL
27578  * <script type="text/javascript">
27579  */
27580  
27581
27582 /**
27583  * @class Roo.ColorPalette
27584  * @extends Roo.Component
27585  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
27586  * Here's an example of typical usage:
27587  * <pre><code>
27588 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
27589 cp.render('my-div');
27590
27591 cp.on('select', function(palette, selColor){
27592     // do something with selColor
27593 });
27594 </code></pre>
27595  * @constructor
27596  * Create a new ColorPalette
27597  * @param {Object} config The config object
27598  */
27599 Roo.ColorPalette = function(config){
27600     Roo.ColorPalette.superclass.constructor.call(this, config);
27601     this.addEvents({
27602         /**
27603              * @event select
27604              * Fires when a color is selected
27605              * @param {ColorPalette} this
27606              * @param {String} color The 6-digit color hex code (without the # symbol)
27607              */
27608         select: true
27609     });
27610
27611     if(this.handler){
27612         this.on("select", this.handler, this.scope, true);
27613     }
27614 };
27615 Roo.extend(Roo.ColorPalette, Roo.Component, {
27616     /**
27617      * @cfg {String} itemCls
27618      * The CSS class to apply to the containing element (defaults to "x-color-palette")
27619      */
27620     itemCls : "x-color-palette",
27621     /**
27622      * @cfg {String} value
27623      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
27624      * the hex codes are case-sensitive.
27625      */
27626     value : null,
27627     clickEvent:'click',
27628     // private
27629     ctype: "Roo.ColorPalette",
27630
27631     /**
27632      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
27633      */
27634     allowReselect : false,
27635
27636     /**
27637      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
27638      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
27639      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
27640      * of colors with the width setting until the box is symmetrical.</p>
27641      * <p>You can override individual colors if needed:</p>
27642      * <pre><code>
27643 var cp = new Roo.ColorPalette();
27644 cp.colors[0] = "FF0000";  // change the first box to red
27645 </code></pre>
27646
27647 Or you can provide a custom array of your own for complete control:
27648 <pre><code>
27649 var cp = new Roo.ColorPalette();
27650 cp.colors = ["000000", "993300", "333300"];
27651 </code></pre>
27652      * @type Array
27653      */
27654     colors : [
27655         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
27656         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
27657         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
27658         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
27659         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
27660     ],
27661
27662     // private
27663     onRender : function(container, position){
27664         var t = new Roo.MasterTemplate(
27665             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
27666         );
27667         var c = this.colors;
27668         for(var i = 0, len = c.length; i < len; i++){
27669             t.add([c[i]]);
27670         }
27671         var el = document.createElement("div");
27672         el.className = this.itemCls;
27673         t.overwrite(el);
27674         container.dom.insertBefore(el, position);
27675         this.el = Roo.get(el);
27676         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
27677         if(this.clickEvent != 'click'){
27678             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
27679         }
27680     },
27681
27682     // private
27683     afterRender : function(){
27684         Roo.ColorPalette.superclass.afterRender.call(this);
27685         if(this.value){
27686             var s = this.value;
27687             this.value = null;
27688             this.select(s);
27689         }
27690     },
27691
27692     // private
27693     handleClick : function(e, t){
27694         e.preventDefault();
27695         if(!this.disabled){
27696             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
27697             this.select(c.toUpperCase());
27698         }
27699     },
27700
27701     /**
27702      * Selects the specified color in the palette (fires the select event)
27703      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
27704      */
27705     select : function(color){
27706         color = color.replace("#", "");
27707         if(color != this.value || this.allowReselect){
27708             var el = this.el;
27709             if(this.value){
27710                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
27711             }
27712             el.child("a.color-"+color).addClass("x-color-palette-sel");
27713             this.value = color;
27714             this.fireEvent("select", this, color);
27715         }
27716     }
27717 });/*
27718  * Based on:
27719  * Ext JS Library 1.1.1
27720  * Copyright(c) 2006-2007, Ext JS, LLC.
27721  *
27722  * Originally Released Under LGPL - original licence link has changed is not relivant.
27723  *
27724  * Fork - LGPL
27725  * <script type="text/javascript">
27726  */
27727  
27728 /**
27729  * @class Roo.DatePicker
27730  * @extends Roo.Component
27731  * Simple date picker class.
27732  * @constructor
27733  * Create a new DatePicker
27734  * @param {Object} config The config object
27735  */
27736 Roo.DatePicker = function(config){
27737     Roo.DatePicker.superclass.constructor.call(this, config);
27738
27739     this.value = config && config.value ?
27740                  config.value.clearTime() : new Date().clearTime();
27741
27742     this.addEvents({
27743         /**
27744              * @event select
27745              * Fires when a date is selected
27746              * @param {DatePicker} this
27747              * @param {Date} date The selected date
27748              */
27749         'select': true,
27750         /**
27751              * @event monthchange
27752              * Fires when the displayed month changes 
27753              * @param {DatePicker} this
27754              * @param {Date} date The selected month
27755              */
27756         'monthchange': true
27757     });
27758
27759     if(this.handler){
27760         this.on("select", this.handler,  this.scope || this);
27761     }
27762     // build the disabledDatesRE
27763     if(!this.disabledDatesRE && this.disabledDates){
27764         var dd = this.disabledDates;
27765         var re = "(?:";
27766         for(var i = 0; i < dd.length; i++){
27767             re += dd[i];
27768             if(i != dd.length-1) {
27769                 re += "|";
27770             }
27771         }
27772         this.disabledDatesRE = new RegExp(re + ")");
27773     }
27774 };
27775
27776 Roo.extend(Roo.DatePicker, Roo.Component, {
27777     /**
27778      * @cfg {String} todayText
27779      * The text to display on the button that selects the current date (defaults to "Today")
27780      */
27781     todayText : "Today",
27782     /**
27783      * @cfg {String} okText
27784      * The text to display on the ok button
27785      */
27786     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
27787     /**
27788      * @cfg {String} cancelText
27789      * The text to display on the cancel button
27790      */
27791     cancelText : "Cancel",
27792     /**
27793      * @cfg {String} todayTip
27794      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
27795      */
27796     todayTip : "{0} (Spacebar)",
27797     /**
27798      * @cfg {Date} minDate
27799      * Minimum allowable date (JavaScript date object, defaults to null)
27800      */
27801     minDate : null,
27802     /**
27803      * @cfg {Date} maxDate
27804      * Maximum allowable date (JavaScript date object, defaults to null)
27805      */
27806     maxDate : null,
27807     /**
27808      * @cfg {String} minText
27809      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
27810      */
27811     minText : "This date is before the minimum date",
27812     /**
27813      * @cfg {String} maxText
27814      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
27815      */
27816     maxText : "This date is after the maximum date",
27817     /**
27818      * @cfg {String} format
27819      * The default date format string which can be overriden for localization support.  The format must be
27820      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
27821      */
27822     format : "m/d/y",
27823     /**
27824      * @cfg {Array} disabledDays
27825      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
27826      */
27827     disabledDays : null,
27828     /**
27829      * @cfg {String} disabledDaysText
27830      * The tooltip to display when the date falls on a disabled day (defaults to "")
27831      */
27832     disabledDaysText : "",
27833     /**
27834      * @cfg {RegExp} disabledDatesRE
27835      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
27836      */
27837     disabledDatesRE : null,
27838     /**
27839      * @cfg {String} disabledDatesText
27840      * The tooltip text to display when the date falls on a disabled date (defaults to "")
27841      */
27842     disabledDatesText : "",
27843     /**
27844      * @cfg {Boolean} constrainToViewport
27845      * True to constrain the date picker to the viewport (defaults to true)
27846      */
27847     constrainToViewport : true,
27848     /**
27849      * @cfg {Array} monthNames
27850      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
27851      */
27852     monthNames : Date.monthNames,
27853     /**
27854      * @cfg {Array} dayNames
27855      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
27856      */
27857     dayNames : Date.dayNames,
27858     /**
27859      * @cfg {String} nextText
27860      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
27861      */
27862     nextText: 'Next Month (Control+Right)',
27863     /**
27864      * @cfg {String} prevText
27865      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
27866      */
27867     prevText: 'Previous Month (Control+Left)',
27868     /**
27869      * @cfg {String} monthYearText
27870      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
27871      */
27872     monthYearText: 'Choose a month (Control+Up/Down to move years)',
27873     /**
27874      * @cfg {Number} startDay
27875      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
27876      */
27877     startDay : 0,
27878     /**
27879      * @cfg {Bool} showClear
27880      * Show a clear button (usefull for date form elements that can be blank.)
27881      */
27882     
27883     showClear: false,
27884     
27885     /**
27886      * Sets the value of the date field
27887      * @param {Date} value The date to set
27888      */
27889     setValue : function(value){
27890         var old = this.value;
27891         
27892         if (typeof(value) == 'string') {
27893          
27894             value = Date.parseDate(value, this.format);
27895         }
27896         if (!value) {
27897             value = new Date();
27898         }
27899         
27900         this.value = value.clearTime(true);
27901         if(this.el){
27902             this.update(this.value);
27903         }
27904     },
27905
27906     /**
27907      * Gets the current selected value of the date field
27908      * @return {Date} The selected date
27909      */
27910     getValue : function(){
27911         return this.value;
27912     },
27913
27914     // private
27915     focus : function(){
27916         if(this.el){
27917             this.update(this.activeDate);
27918         }
27919     },
27920
27921     // privateval
27922     onRender : function(container, position){
27923         
27924         var m = [
27925              '<table cellspacing="0">',
27926                 '<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>',
27927                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
27928         var dn = this.dayNames;
27929         for(var i = 0; i < 7; i++){
27930             var d = this.startDay+i;
27931             if(d > 6){
27932                 d = d-7;
27933             }
27934             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
27935         }
27936         m[m.length] = "</tr></thead><tbody><tr>";
27937         for(var i = 0; i < 42; i++) {
27938             if(i % 7 == 0 && i != 0){
27939                 m[m.length] = "</tr><tr>";
27940             }
27941             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
27942         }
27943         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
27944             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
27945
27946         var el = document.createElement("div");
27947         el.className = "x-date-picker";
27948         el.innerHTML = m.join("");
27949
27950         container.dom.insertBefore(el, position);
27951
27952         this.el = Roo.get(el);
27953         this.eventEl = Roo.get(el.firstChild);
27954
27955         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
27956             handler: this.showPrevMonth,
27957             scope: this,
27958             preventDefault:true,
27959             stopDefault:true
27960         });
27961
27962         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
27963             handler: this.showNextMonth,
27964             scope: this,
27965             preventDefault:true,
27966             stopDefault:true
27967         });
27968
27969         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
27970
27971         this.monthPicker = this.el.down('div.x-date-mp');
27972         this.monthPicker.enableDisplayMode('block');
27973         
27974         var kn = new Roo.KeyNav(this.eventEl, {
27975             "left" : function(e){
27976                 e.ctrlKey ?
27977                     this.showPrevMonth() :
27978                     this.update(this.activeDate.add("d", -1));
27979             },
27980
27981             "right" : function(e){
27982                 e.ctrlKey ?
27983                     this.showNextMonth() :
27984                     this.update(this.activeDate.add("d", 1));
27985             },
27986
27987             "up" : function(e){
27988                 e.ctrlKey ?
27989                     this.showNextYear() :
27990                     this.update(this.activeDate.add("d", -7));
27991             },
27992
27993             "down" : function(e){
27994                 e.ctrlKey ?
27995                     this.showPrevYear() :
27996                     this.update(this.activeDate.add("d", 7));
27997             },
27998
27999             "pageUp" : function(e){
28000                 this.showNextMonth();
28001             },
28002
28003             "pageDown" : function(e){
28004                 this.showPrevMonth();
28005             },
28006
28007             "enter" : function(e){
28008                 e.stopPropagation();
28009                 return true;
28010             },
28011
28012             scope : this
28013         });
28014
28015         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
28016
28017         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
28018
28019         this.el.unselectable();
28020         
28021         this.cells = this.el.select("table.x-date-inner tbody td");
28022         this.textNodes = this.el.query("table.x-date-inner tbody span");
28023
28024         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
28025             text: "&#160;",
28026             tooltip: this.monthYearText
28027         });
28028
28029         this.mbtn.on('click', this.showMonthPicker, this);
28030         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
28031
28032
28033         var today = (new Date()).dateFormat(this.format);
28034         
28035         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
28036         if (this.showClear) {
28037             baseTb.add( new Roo.Toolbar.Fill());
28038         }
28039         baseTb.add({
28040             text: String.format(this.todayText, today),
28041             tooltip: String.format(this.todayTip, today),
28042             handler: this.selectToday,
28043             scope: this
28044         });
28045         
28046         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
28047             
28048         //});
28049         if (this.showClear) {
28050             
28051             baseTb.add( new Roo.Toolbar.Fill());
28052             baseTb.add({
28053                 text: '&#160;',
28054                 cls: 'x-btn-icon x-btn-clear',
28055                 handler: function() {
28056                     //this.value = '';
28057                     this.fireEvent("select", this, '');
28058                 },
28059                 scope: this
28060             });
28061         }
28062         
28063         
28064         if(Roo.isIE){
28065             this.el.repaint();
28066         }
28067         this.update(this.value);
28068     },
28069
28070     createMonthPicker : function(){
28071         if(!this.monthPicker.dom.firstChild){
28072             var buf = ['<table border="0" cellspacing="0">'];
28073             for(var i = 0; i < 6; i++){
28074                 buf.push(
28075                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
28076                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
28077                     i == 0 ?
28078                     '<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>' :
28079                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
28080                 );
28081             }
28082             buf.push(
28083                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
28084                     this.okText,
28085                     '</button><button type="button" class="x-date-mp-cancel">',
28086                     this.cancelText,
28087                     '</button></td></tr>',
28088                 '</table>'
28089             );
28090             this.monthPicker.update(buf.join(''));
28091             this.monthPicker.on('click', this.onMonthClick, this);
28092             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
28093
28094             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
28095             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
28096
28097             this.mpMonths.each(function(m, a, i){
28098                 i += 1;
28099                 if((i%2) == 0){
28100                     m.dom.xmonth = 5 + Math.round(i * .5);
28101                 }else{
28102                     m.dom.xmonth = Math.round((i-1) * .5);
28103                 }
28104             });
28105         }
28106     },
28107
28108     showMonthPicker : function(){
28109         this.createMonthPicker();
28110         var size = this.el.getSize();
28111         this.monthPicker.setSize(size);
28112         this.monthPicker.child('table').setSize(size);
28113
28114         this.mpSelMonth = (this.activeDate || this.value).getMonth();
28115         this.updateMPMonth(this.mpSelMonth);
28116         this.mpSelYear = (this.activeDate || this.value).getFullYear();
28117         this.updateMPYear(this.mpSelYear);
28118
28119         this.monthPicker.slideIn('t', {duration:.2});
28120     },
28121
28122     updateMPYear : function(y){
28123         this.mpyear = y;
28124         var ys = this.mpYears.elements;
28125         for(var i = 1; i <= 10; i++){
28126             var td = ys[i-1], y2;
28127             if((i%2) == 0){
28128                 y2 = y + Math.round(i * .5);
28129                 td.firstChild.innerHTML = y2;
28130                 td.xyear = y2;
28131             }else{
28132                 y2 = y - (5-Math.round(i * .5));
28133                 td.firstChild.innerHTML = y2;
28134                 td.xyear = y2;
28135             }
28136             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
28137         }
28138     },
28139
28140     updateMPMonth : function(sm){
28141         this.mpMonths.each(function(m, a, i){
28142             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
28143         });
28144     },
28145
28146     selectMPMonth: function(m){
28147         
28148     },
28149
28150     onMonthClick : function(e, t){
28151         e.stopEvent();
28152         var el = new Roo.Element(t), pn;
28153         if(el.is('button.x-date-mp-cancel')){
28154             this.hideMonthPicker();
28155         }
28156         else if(el.is('button.x-date-mp-ok')){
28157             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
28158             this.hideMonthPicker();
28159         }
28160         else if(pn = el.up('td.x-date-mp-month', 2)){
28161             this.mpMonths.removeClass('x-date-mp-sel');
28162             pn.addClass('x-date-mp-sel');
28163             this.mpSelMonth = pn.dom.xmonth;
28164         }
28165         else if(pn = el.up('td.x-date-mp-year', 2)){
28166             this.mpYears.removeClass('x-date-mp-sel');
28167             pn.addClass('x-date-mp-sel');
28168             this.mpSelYear = pn.dom.xyear;
28169         }
28170         else if(el.is('a.x-date-mp-prev')){
28171             this.updateMPYear(this.mpyear-10);
28172         }
28173         else if(el.is('a.x-date-mp-next')){
28174             this.updateMPYear(this.mpyear+10);
28175         }
28176     },
28177
28178     onMonthDblClick : function(e, t){
28179         e.stopEvent();
28180         var el = new Roo.Element(t), pn;
28181         if(pn = el.up('td.x-date-mp-month', 2)){
28182             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
28183             this.hideMonthPicker();
28184         }
28185         else if(pn = el.up('td.x-date-mp-year', 2)){
28186             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
28187             this.hideMonthPicker();
28188         }
28189     },
28190
28191     hideMonthPicker : function(disableAnim){
28192         if(this.monthPicker){
28193             if(disableAnim === true){
28194                 this.monthPicker.hide();
28195             }else{
28196                 this.monthPicker.slideOut('t', {duration:.2});
28197             }
28198         }
28199     },
28200
28201     // private
28202     showPrevMonth : function(e){
28203         this.update(this.activeDate.add("mo", -1));
28204     },
28205
28206     // private
28207     showNextMonth : function(e){
28208         this.update(this.activeDate.add("mo", 1));
28209     },
28210
28211     // private
28212     showPrevYear : function(){
28213         this.update(this.activeDate.add("y", -1));
28214     },
28215
28216     // private
28217     showNextYear : function(){
28218         this.update(this.activeDate.add("y", 1));
28219     },
28220
28221     // private
28222     handleMouseWheel : function(e){
28223         var delta = e.getWheelDelta();
28224         if(delta > 0){
28225             this.showPrevMonth();
28226             e.stopEvent();
28227         } else if(delta < 0){
28228             this.showNextMonth();
28229             e.stopEvent();
28230         }
28231     },
28232
28233     // private
28234     handleDateClick : function(e, t){
28235         e.stopEvent();
28236         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
28237             this.setValue(new Date(t.dateValue));
28238             this.fireEvent("select", this, this.value);
28239         }
28240     },
28241
28242     // private
28243     selectToday : function(){
28244         this.setValue(new Date().clearTime());
28245         this.fireEvent("select", this, this.value);
28246     },
28247
28248     // private
28249     update : function(date)
28250     {
28251         var vd = this.activeDate;
28252         this.activeDate = date;
28253         if(vd && this.el){
28254             var t = date.getTime();
28255             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
28256                 this.cells.removeClass("x-date-selected");
28257                 this.cells.each(function(c){
28258                    if(c.dom.firstChild.dateValue == t){
28259                        c.addClass("x-date-selected");
28260                        setTimeout(function(){
28261                             try{c.dom.firstChild.focus();}catch(e){}
28262                        }, 50);
28263                        return false;
28264                    }
28265                 });
28266                 return;
28267             }
28268         }
28269         
28270         var days = date.getDaysInMonth();
28271         var firstOfMonth = date.getFirstDateOfMonth();
28272         var startingPos = firstOfMonth.getDay()-this.startDay;
28273
28274         if(startingPos <= this.startDay){
28275             startingPos += 7;
28276         }
28277
28278         var pm = date.add("mo", -1);
28279         var prevStart = pm.getDaysInMonth()-startingPos;
28280
28281         var cells = this.cells.elements;
28282         var textEls = this.textNodes;
28283         days += startingPos;
28284
28285         // convert everything to numbers so it's fast
28286         var day = 86400000;
28287         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
28288         var today = new Date().clearTime().getTime();
28289         var sel = date.clearTime().getTime();
28290         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
28291         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
28292         var ddMatch = this.disabledDatesRE;
28293         var ddText = this.disabledDatesText;
28294         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
28295         var ddaysText = this.disabledDaysText;
28296         var format = this.format;
28297
28298         var setCellClass = function(cal, cell){
28299             cell.title = "";
28300             var t = d.getTime();
28301             cell.firstChild.dateValue = t;
28302             if(t == today){
28303                 cell.className += " x-date-today";
28304                 cell.title = cal.todayText;
28305             }
28306             if(t == sel){
28307                 cell.className += " x-date-selected";
28308                 setTimeout(function(){
28309                     try{cell.firstChild.focus();}catch(e){}
28310                 }, 50);
28311             }
28312             // disabling
28313             if(t < min) {
28314                 cell.className = " x-date-disabled";
28315                 cell.title = cal.minText;
28316                 return;
28317             }
28318             if(t > max) {
28319                 cell.className = " x-date-disabled";
28320                 cell.title = cal.maxText;
28321                 return;
28322             }
28323             if(ddays){
28324                 if(ddays.indexOf(d.getDay()) != -1){
28325                     cell.title = ddaysText;
28326                     cell.className = " x-date-disabled";
28327                 }
28328             }
28329             if(ddMatch && format){
28330                 var fvalue = d.dateFormat(format);
28331                 if(ddMatch.test(fvalue)){
28332                     cell.title = ddText.replace("%0", fvalue);
28333                     cell.className = " x-date-disabled";
28334                 }
28335             }
28336         };
28337
28338         var i = 0;
28339         for(; i < startingPos; i++) {
28340             textEls[i].innerHTML = (++prevStart);
28341             d.setDate(d.getDate()+1);
28342             cells[i].className = "x-date-prevday";
28343             setCellClass(this, cells[i]);
28344         }
28345         for(; i < days; i++){
28346             intDay = i - startingPos + 1;
28347             textEls[i].innerHTML = (intDay);
28348             d.setDate(d.getDate()+1);
28349             cells[i].className = "x-date-active";
28350             setCellClass(this, cells[i]);
28351         }
28352         var extraDays = 0;
28353         for(; i < 42; i++) {
28354              textEls[i].innerHTML = (++extraDays);
28355              d.setDate(d.getDate()+1);
28356              cells[i].className = "x-date-nextday";
28357              setCellClass(this, cells[i]);
28358         }
28359
28360         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
28361         this.fireEvent('monthchange', this, date);
28362         
28363         if(!this.internalRender){
28364             var main = this.el.dom.firstChild;
28365             var w = main.offsetWidth;
28366             this.el.setWidth(w + this.el.getBorderWidth("lr"));
28367             Roo.fly(main).setWidth(w);
28368             this.internalRender = true;
28369             // opera does not respect the auto grow header center column
28370             // then, after it gets a width opera refuses to recalculate
28371             // without a second pass
28372             if(Roo.isOpera && !this.secondPass){
28373                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
28374                 this.secondPass = true;
28375                 this.update.defer(10, this, [date]);
28376             }
28377         }
28378         
28379         
28380     }
28381 });        /*
28382  * Based on:
28383  * Ext JS Library 1.1.1
28384  * Copyright(c) 2006-2007, Ext JS, LLC.
28385  *
28386  * Originally Released Under LGPL - original licence link has changed is not relivant.
28387  *
28388  * Fork - LGPL
28389  * <script type="text/javascript">
28390  */
28391 /**
28392  * @class Roo.TabPanel
28393  * @extends Roo.util.Observable
28394  * A lightweight tab container.
28395  * <br><br>
28396  * Usage:
28397  * <pre><code>
28398 // basic tabs 1, built from existing content
28399 var tabs = new Roo.TabPanel("tabs1");
28400 tabs.addTab("script", "View Script");
28401 tabs.addTab("markup", "View Markup");
28402 tabs.activate("script");
28403
28404 // more advanced tabs, built from javascript
28405 var jtabs = new Roo.TabPanel("jtabs");
28406 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
28407
28408 // set up the UpdateManager
28409 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
28410 var updater = tab2.getUpdateManager();
28411 updater.setDefaultUrl("ajax1.htm");
28412 tab2.on('activate', updater.refresh, updater, true);
28413
28414 // Use setUrl for Ajax loading
28415 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
28416 tab3.setUrl("ajax2.htm", null, true);
28417
28418 // Disabled tab
28419 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
28420 tab4.disable();
28421
28422 jtabs.activate("jtabs-1");
28423  * </code></pre>
28424  * @constructor
28425  * Create a new TabPanel.
28426  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
28427  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
28428  */
28429 Roo.TabPanel = function(container, config){
28430     /**
28431     * The container element for this TabPanel.
28432     * @type Roo.Element
28433     */
28434     this.el = Roo.get(container, true);
28435     if(config){
28436         if(typeof config == "boolean"){
28437             this.tabPosition = config ? "bottom" : "top";
28438         }else{
28439             Roo.apply(this, config);
28440         }
28441     }
28442     if(this.tabPosition == "bottom"){
28443         this.bodyEl = Roo.get(this.createBody(this.el.dom));
28444         this.el.addClass("x-tabs-bottom");
28445     }
28446     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
28447     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
28448     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
28449     if(Roo.isIE){
28450         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
28451     }
28452     if(this.tabPosition != "bottom"){
28453         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
28454          * @type Roo.Element
28455          */
28456         this.bodyEl = Roo.get(this.createBody(this.el.dom));
28457         this.el.addClass("x-tabs-top");
28458     }
28459     this.items = [];
28460
28461     this.bodyEl.setStyle("position", "relative");
28462
28463     this.active = null;
28464     this.activateDelegate = this.activate.createDelegate(this);
28465
28466     this.addEvents({
28467         /**
28468          * @event tabchange
28469          * Fires when the active tab changes
28470          * @param {Roo.TabPanel} this
28471          * @param {Roo.TabPanelItem} activePanel The new active tab
28472          */
28473         "tabchange": true,
28474         /**
28475          * @event beforetabchange
28476          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
28477          * @param {Roo.TabPanel} this
28478          * @param {Object} e Set cancel to true on this object to cancel the tab change
28479          * @param {Roo.TabPanelItem} tab The tab being changed to
28480          */
28481         "beforetabchange" : true
28482     });
28483
28484     Roo.EventManager.onWindowResize(this.onResize, this);
28485     this.cpad = this.el.getPadding("lr");
28486     this.hiddenCount = 0;
28487
28488
28489     // toolbar on the tabbar support...
28490     if (this.toolbar) {
28491         var tcfg = this.toolbar;
28492         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
28493         this.toolbar = new Roo.Toolbar(tcfg);
28494         if (Roo.isSafari) {
28495             var tbl = tcfg.container.child('table', true);
28496             tbl.setAttribute('width', '100%');
28497         }
28498         
28499     }
28500    
28501
28502
28503     Roo.TabPanel.superclass.constructor.call(this);
28504 };
28505
28506 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
28507     /*
28508      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
28509      */
28510     tabPosition : "top",
28511     /*
28512      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
28513      */
28514     currentTabWidth : 0,
28515     /*
28516      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
28517      */
28518     minTabWidth : 40,
28519     /*
28520      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
28521      */
28522     maxTabWidth : 250,
28523     /*
28524      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
28525      */
28526     preferredTabWidth : 175,
28527     /*
28528      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
28529      */
28530     resizeTabs : false,
28531     /*
28532      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
28533      */
28534     monitorResize : true,
28535     /*
28536      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
28537      */
28538     toolbar : false,
28539
28540     /**
28541      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
28542      * @param {String} id The id of the div to use <b>or create</b>
28543      * @param {String} text The text for the tab
28544      * @param {String} content (optional) Content to put in the TabPanelItem body
28545      * @param {Boolean} closable (optional) True to create a close icon on the tab
28546      * @return {Roo.TabPanelItem} The created TabPanelItem
28547      */
28548     addTab : function(id, text, content, closable){
28549         var item = new Roo.TabPanelItem(this, id, text, closable);
28550         this.addTabItem(item);
28551         if(content){
28552             item.setContent(content);
28553         }
28554         return item;
28555     },
28556
28557     /**
28558      * Returns the {@link Roo.TabPanelItem} with the specified id/index
28559      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
28560      * @return {Roo.TabPanelItem}
28561      */
28562     getTab : function(id){
28563         return this.items[id];
28564     },
28565
28566     /**
28567      * Hides the {@link Roo.TabPanelItem} with the specified id/index
28568      * @param {String/Number} id The id or index of the TabPanelItem to hide.
28569      */
28570     hideTab : function(id){
28571         var t = this.items[id];
28572         if(!t.isHidden()){
28573            t.setHidden(true);
28574            this.hiddenCount++;
28575            this.autoSizeTabs();
28576         }
28577     },
28578
28579     /**
28580      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
28581      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
28582      */
28583     unhideTab : function(id){
28584         var t = this.items[id];
28585         if(t.isHidden()){
28586            t.setHidden(false);
28587            this.hiddenCount--;
28588            this.autoSizeTabs();
28589         }
28590     },
28591
28592     /**
28593      * Adds an existing {@link Roo.TabPanelItem}.
28594      * @param {Roo.TabPanelItem} item The TabPanelItem to add
28595      */
28596     addTabItem : function(item){
28597         this.items[item.id] = item;
28598         this.items.push(item);
28599         if(this.resizeTabs){
28600            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
28601            this.autoSizeTabs();
28602         }else{
28603             item.autoSize();
28604         }
28605     },
28606
28607     /**
28608      * Removes a {@link Roo.TabPanelItem}.
28609      * @param {String/Number} id The id or index of the TabPanelItem to remove.
28610      */
28611     removeTab : function(id){
28612         var items = this.items;
28613         var tab = items[id];
28614         if(!tab) { return; }
28615         var index = items.indexOf(tab);
28616         if(this.active == tab && items.length > 1){
28617             var newTab = this.getNextAvailable(index);
28618             if(newTab) {
28619                 newTab.activate();
28620             }
28621         }
28622         this.stripEl.dom.removeChild(tab.pnode.dom);
28623         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
28624             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
28625         }
28626         items.splice(index, 1);
28627         delete this.items[tab.id];
28628         tab.fireEvent("close", tab);
28629         tab.purgeListeners();
28630         this.autoSizeTabs();
28631     },
28632
28633     getNextAvailable : function(start){
28634         var items = this.items;
28635         var index = start;
28636         // look for a next tab that will slide over to
28637         // replace the one being removed
28638         while(index < items.length){
28639             var item = items[++index];
28640             if(item && !item.isHidden()){
28641                 return item;
28642             }
28643         }
28644         // if one isn't found select the previous tab (on the left)
28645         index = start;
28646         while(index >= 0){
28647             var item = items[--index];
28648             if(item && !item.isHidden()){
28649                 return item;
28650             }
28651         }
28652         return null;
28653     },
28654
28655     /**
28656      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
28657      * @param {String/Number} id The id or index of the TabPanelItem to disable.
28658      */
28659     disableTab : function(id){
28660         var tab = this.items[id];
28661         if(tab && this.active != tab){
28662             tab.disable();
28663         }
28664     },
28665
28666     /**
28667      * Enables a {@link Roo.TabPanelItem} that is disabled.
28668      * @param {String/Number} id The id or index of the TabPanelItem to enable.
28669      */
28670     enableTab : function(id){
28671         var tab = this.items[id];
28672         tab.enable();
28673     },
28674
28675     /**
28676      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
28677      * @param {String/Number} id The id or index of the TabPanelItem to activate.
28678      * @return {Roo.TabPanelItem} The TabPanelItem.
28679      */
28680     activate : function(id){
28681         var tab = this.items[id];
28682         if(!tab){
28683             return null;
28684         }
28685         if(tab == this.active || tab.disabled){
28686             return tab;
28687         }
28688         var e = {};
28689         this.fireEvent("beforetabchange", this, e, tab);
28690         if(e.cancel !== true && !tab.disabled){
28691             if(this.active){
28692                 this.active.hide();
28693             }
28694             this.active = this.items[id];
28695             this.active.show();
28696             this.fireEvent("tabchange", this, this.active);
28697         }
28698         return tab;
28699     },
28700
28701     /**
28702      * Gets the active {@link Roo.TabPanelItem}.
28703      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
28704      */
28705     getActiveTab : function(){
28706         return this.active;
28707     },
28708
28709     /**
28710      * Updates the tab body element to fit the height of the container element
28711      * for overflow scrolling
28712      * @param {Number} targetHeight (optional) Override the starting height from the elements height
28713      */
28714     syncHeight : function(targetHeight){
28715         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
28716         var bm = this.bodyEl.getMargins();
28717         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
28718         this.bodyEl.setHeight(newHeight);
28719         return newHeight;
28720     },
28721
28722     onResize : function(){
28723         if(this.monitorResize){
28724             this.autoSizeTabs();
28725         }
28726     },
28727
28728     /**
28729      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
28730      */
28731     beginUpdate : function(){
28732         this.updating = true;
28733     },
28734
28735     /**
28736      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
28737      */
28738     endUpdate : function(){
28739         this.updating = false;
28740         this.autoSizeTabs();
28741     },
28742
28743     /**
28744      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
28745      */
28746     autoSizeTabs : function(){
28747         var count = this.items.length;
28748         var vcount = count - this.hiddenCount;
28749         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
28750             return;
28751         }
28752         var w = Math.max(this.el.getWidth() - this.cpad, 10);
28753         var availWidth = Math.floor(w / vcount);
28754         var b = this.stripBody;
28755         if(b.getWidth() > w){
28756             var tabs = this.items;
28757             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
28758             if(availWidth < this.minTabWidth){
28759                 /*if(!this.sleft){    // incomplete scrolling code
28760                     this.createScrollButtons();
28761                 }
28762                 this.showScroll();
28763                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
28764             }
28765         }else{
28766             if(this.currentTabWidth < this.preferredTabWidth){
28767                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
28768             }
28769         }
28770     },
28771
28772     /**
28773      * Returns the number of tabs in this TabPanel.
28774      * @return {Number}
28775      */
28776      getCount : function(){
28777          return this.items.length;
28778      },
28779
28780     /**
28781      * Resizes all the tabs to the passed width
28782      * @param {Number} The new width
28783      */
28784     setTabWidth : function(width){
28785         this.currentTabWidth = width;
28786         for(var i = 0, len = this.items.length; i < len; i++) {
28787                 if(!this.items[i].isHidden()) {
28788                 this.items[i].setWidth(width);
28789             }
28790         }
28791     },
28792
28793     /**
28794      * Destroys this TabPanel
28795      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
28796      */
28797     destroy : function(removeEl){
28798         Roo.EventManager.removeResizeListener(this.onResize, this);
28799         for(var i = 0, len = this.items.length; i < len; i++){
28800             this.items[i].purgeListeners();
28801         }
28802         if(removeEl === true){
28803             this.el.update("");
28804             this.el.remove();
28805         }
28806     }
28807 });
28808
28809 /**
28810  * @class Roo.TabPanelItem
28811  * @extends Roo.util.Observable
28812  * Represents an individual item (tab plus body) in a TabPanel.
28813  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
28814  * @param {String} id The id of this TabPanelItem
28815  * @param {String} text The text for the tab of this TabPanelItem
28816  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
28817  */
28818 Roo.TabPanelItem = function(tabPanel, id, text, closable){
28819     /**
28820      * The {@link Roo.TabPanel} this TabPanelItem belongs to
28821      * @type Roo.TabPanel
28822      */
28823     this.tabPanel = tabPanel;
28824     /**
28825      * The id for this TabPanelItem
28826      * @type String
28827      */
28828     this.id = id;
28829     /** @private */
28830     this.disabled = false;
28831     /** @private */
28832     this.text = text;
28833     /** @private */
28834     this.loaded = false;
28835     this.closable = closable;
28836
28837     /**
28838      * The body element for this TabPanelItem.
28839      * @type Roo.Element
28840      */
28841     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
28842     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
28843     this.bodyEl.setStyle("display", "block");
28844     this.bodyEl.setStyle("zoom", "1");
28845     this.hideAction();
28846
28847     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
28848     /** @private */
28849     this.el = Roo.get(els.el, true);
28850     this.inner = Roo.get(els.inner, true);
28851     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
28852     this.pnode = Roo.get(els.el.parentNode, true);
28853     this.el.on("mousedown", this.onTabMouseDown, this);
28854     this.el.on("click", this.onTabClick, this);
28855     /** @private */
28856     if(closable){
28857         var c = Roo.get(els.close, true);
28858         c.dom.title = this.closeText;
28859         c.addClassOnOver("close-over");
28860         c.on("click", this.closeClick, this);
28861      }
28862
28863     this.addEvents({
28864          /**
28865          * @event activate
28866          * Fires when this tab becomes the active tab.
28867          * @param {Roo.TabPanel} tabPanel The parent TabPanel
28868          * @param {Roo.TabPanelItem} this
28869          */
28870         "activate": true,
28871         /**
28872          * @event beforeclose
28873          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
28874          * @param {Roo.TabPanelItem} this
28875          * @param {Object} e Set cancel to true on this object to cancel the close.
28876          */
28877         "beforeclose": true,
28878         /**
28879          * @event close
28880          * Fires when this tab is closed.
28881          * @param {Roo.TabPanelItem} this
28882          */
28883          "close": true,
28884         /**
28885          * @event deactivate
28886          * Fires when this tab is no longer the active tab.
28887          * @param {Roo.TabPanel} tabPanel The parent TabPanel
28888          * @param {Roo.TabPanelItem} this
28889          */
28890          "deactivate" : true
28891     });
28892     this.hidden = false;
28893
28894     Roo.TabPanelItem.superclass.constructor.call(this);
28895 };
28896
28897 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
28898     purgeListeners : function(){
28899        Roo.util.Observable.prototype.purgeListeners.call(this);
28900        this.el.removeAllListeners();
28901     },
28902     /**
28903      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
28904      */
28905     show : function(){
28906         this.pnode.addClass("on");
28907         this.showAction();
28908         if(Roo.isOpera){
28909             this.tabPanel.stripWrap.repaint();
28910         }
28911         this.fireEvent("activate", this.tabPanel, this);
28912     },
28913
28914     /**
28915      * Returns true if this tab is the active tab.
28916      * @return {Boolean}
28917      */
28918     isActive : function(){
28919         return this.tabPanel.getActiveTab() == this;
28920     },
28921
28922     /**
28923      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
28924      */
28925     hide : function(){
28926         this.pnode.removeClass("on");
28927         this.hideAction();
28928         this.fireEvent("deactivate", this.tabPanel, this);
28929     },
28930
28931     hideAction : function(){
28932         this.bodyEl.hide();
28933         this.bodyEl.setStyle("position", "absolute");
28934         this.bodyEl.setLeft("-20000px");
28935         this.bodyEl.setTop("-20000px");
28936     },
28937
28938     showAction : function(){
28939         this.bodyEl.setStyle("position", "relative");
28940         this.bodyEl.setTop("");
28941         this.bodyEl.setLeft("");
28942         this.bodyEl.show();
28943     },
28944
28945     /**
28946      * Set the tooltip for the tab.
28947      * @param {String} tooltip The tab's tooltip
28948      */
28949     setTooltip : function(text){
28950         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
28951             this.textEl.dom.qtip = text;
28952             this.textEl.dom.removeAttribute('title');
28953         }else{
28954             this.textEl.dom.title = text;
28955         }
28956     },
28957
28958     onTabClick : function(e){
28959         e.preventDefault();
28960         this.tabPanel.activate(this.id);
28961     },
28962
28963     onTabMouseDown : function(e){
28964         e.preventDefault();
28965         this.tabPanel.activate(this.id);
28966     },
28967
28968     getWidth : function(){
28969         return this.inner.getWidth();
28970     },
28971
28972     setWidth : function(width){
28973         var iwidth = width - this.pnode.getPadding("lr");
28974         this.inner.setWidth(iwidth);
28975         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
28976         this.pnode.setWidth(width);
28977     },
28978
28979     /**
28980      * Show or hide the tab
28981      * @param {Boolean} hidden True to hide or false to show.
28982      */
28983     setHidden : function(hidden){
28984         this.hidden = hidden;
28985         this.pnode.setStyle("display", hidden ? "none" : "");
28986     },
28987
28988     /**
28989      * Returns true if this tab is "hidden"
28990      * @return {Boolean}
28991      */
28992     isHidden : function(){
28993         return this.hidden;
28994     },
28995
28996     /**
28997      * Returns the text for this tab
28998      * @return {String}
28999      */
29000     getText : function(){
29001         return this.text;
29002     },
29003
29004     autoSize : function(){
29005         //this.el.beginMeasure();
29006         this.textEl.setWidth(1);
29007         /*
29008          *  #2804 [new] Tabs in Roojs
29009          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
29010          */
29011         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
29012         //this.el.endMeasure();
29013     },
29014
29015     /**
29016      * Sets the text for the tab (Note: this also sets the tooltip text)
29017      * @param {String} text The tab's text and tooltip
29018      */
29019     setText : function(text){
29020         this.text = text;
29021         this.textEl.update(text);
29022         this.setTooltip(text);
29023         if(!this.tabPanel.resizeTabs){
29024             this.autoSize();
29025         }
29026     },
29027     /**
29028      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
29029      */
29030     activate : function(){
29031         this.tabPanel.activate(this.id);
29032     },
29033
29034     /**
29035      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
29036      */
29037     disable : function(){
29038         if(this.tabPanel.active != this){
29039             this.disabled = true;
29040             this.pnode.addClass("disabled");
29041         }
29042     },
29043
29044     /**
29045      * Enables this TabPanelItem if it was previously disabled.
29046      */
29047     enable : function(){
29048         this.disabled = false;
29049         this.pnode.removeClass("disabled");
29050     },
29051
29052     /**
29053      * Sets the content for this TabPanelItem.
29054      * @param {String} content The content
29055      * @param {Boolean} loadScripts true to look for and load scripts
29056      */
29057     setContent : function(content, loadScripts){
29058         this.bodyEl.update(content, loadScripts);
29059     },
29060
29061     /**
29062      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
29063      * @return {Roo.UpdateManager} The UpdateManager
29064      */
29065     getUpdateManager : function(){
29066         return this.bodyEl.getUpdateManager();
29067     },
29068
29069     /**
29070      * Set a URL to be used to load the content for this TabPanelItem.
29071      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
29072      * @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)
29073      * @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)
29074      * @return {Roo.UpdateManager} The UpdateManager
29075      */
29076     setUrl : function(url, params, loadOnce){
29077         if(this.refreshDelegate){
29078             this.un('activate', this.refreshDelegate);
29079         }
29080         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
29081         this.on("activate", this.refreshDelegate);
29082         return this.bodyEl.getUpdateManager();
29083     },
29084
29085     /** @private */
29086     _handleRefresh : function(url, params, loadOnce){
29087         if(!loadOnce || !this.loaded){
29088             var updater = this.bodyEl.getUpdateManager();
29089             updater.update(url, params, this._setLoaded.createDelegate(this));
29090         }
29091     },
29092
29093     /**
29094      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
29095      *   Will fail silently if the setUrl method has not been called.
29096      *   This does not activate the panel, just updates its content.
29097      */
29098     refresh : function(){
29099         if(this.refreshDelegate){
29100            this.loaded = false;
29101            this.refreshDelegate();
29102         }
29103     },
29104
29105     /** @private */
29106     _setLoaded : function(){
29107         this.loaded = true;
29108     },
29109
29110     /** @private */
29111     closeClick : function(e){
29112         var o = {};
29113         e.stopEvent();
29114         this.fireEvent("beforeclose", this, o);
29115         if(o.cancel !== true){
29116             this.tabPanel.removeTab(this.id);
29117         }
29118     },
29119     /**
29120      * The text displayed in the tooltip for the close icon.
29121      * @type String
29122      */
29123     closeText : "Close this tab"
29124 });
29125
29126 /** @private */
29127 Roo.TabPanel.prototype.createStrip = function(container){
29128     var strip = document.createElement("div");
29129     strip.className = "x-tabs-wrap";
29130     container.appendChild(strip);
29131     return strip;
29132 };
29133 /** @private */
29134 Roo.TabPanel.prototype.createStripList = function(strip){
29135     // div wrapper for retard IE
29136     // returns the "tr" element.
29137     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
29138         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
29139         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
29140     return strip.firstChild.firstChild.firstChild.firstChild;
29141 };
29142 /** @private */
29143 Roo.TabPanel.prototype.createBody = function(container){
29144     var body = document.createElement("div");
29145     Roo.id(body, "tab-body");
29146     Roo.fly(body).addClass("x-tabs-body");
29147     container.appendChild(body);
29148     return body;
29149 };
29150 /** @private */
29151 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
29152     var body = Roo.getDom(id);
29153     if(!body){
29154         body = document.createElement("div");
29155         body.id = id;
29156     }
29157     Roo.fly(body).addClass("x-tabs-item-body");
29158     bodyEl.insertBefore(body, bodyEl.firstChild);
29159     return body;
29160 };
29161 /** @private */
29162 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
29163     var td = document.createElement("td");
29164     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
29165     //stripEl.appendChild(td);
29166     if(closable){
29167         td.className = "x-tabs-closable";
29168         if(!this.closeTpl){
29169             this.closeTpl = new Roo.Template(
29170                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
29171                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
29172                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
29173             );
29174         }
29175         var el = this.closeTpl.overwrite(td, {"text": text});
29176         var close = el.getElementsByTagName("div")[0];
29177         var inner = el.getElementsByTagName("em")[0];
29178         return {"el": el, "close": close, "inner": inner};
29179     } else {
29180         if(!this.tabTpl){
29181             this.tabTpl = new Roo.Template(
29182                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
29183                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
29184             );
29185         }
29186         var el = this.tabTpl.overwrite(td, {"text": text});
29187         var inner = el.getElementsByTagName("em")[0];
29188         return {"el": el, "inner": inner};
29189     }
29190 };/*
29191  * Based on:
29192  * Ext JS Library 1.1.1
29193  * Copyright(c) 2006-2007, Ext JS, LLC.
29194  *
29195  * Originally Released Under LGPL - original licence link has changed is not relivant.
29196  *
29197  * Fork - LGPL
29198  * <script type="text/javascript">
29199  */
29200
29201 /**
29202  * @class Roo.Button
29203  * @extends Roo.util.Observable
29204  * Simple Button class
29205  * @cfg {String} text The button text
29206  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
29207  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
29208  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
29209  * @cfg {Object} scope The scope of the handler
29210  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
29211  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
29212  * @cfg {Boolean} hidden True to start hidden (defaults to false)
29213  * @cfg {Boolean} disabled True to start disabled (defaults to false)
29214  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
29215  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
29216    applies if enableToggle = true)
29217  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
29218  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
29219   an {@link Roo.util.ClickRepeater} config object (defaults to false).
29220  * @constructor
29221  * Create a new button
29222  * @param {Object} config The config object
29223  */
29224 Roo.Button = function(renderTo, config)
29225 {
29226     if (!config) {
29227         config = renderTo;
29228         renderTo = config.renderTo || false;
29229     }
29230     
29231     Roo.apply(this, config);
29232     this.addEvents({
29233         /**
29234              * @event click
29235              * Fires when this button is clicked
29236              * @param {Button} this
29237              * @param {EventObject} e The click event
29238              */
29239             "click" : true,
29240         /**
29241              * @event toggle
29242              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
29243              * @param {Button} this
29244              * @param {Boolean} pressed
29245              */
29246             "toggle" : true,
29247         /**
29248              * @event mouseover
29249              * Fires when the mouse hovers over the button
29250              * @param {Button} this
29251              * @param {Event} e The event object
29252              */
29253         'mouseover' : true,
29254         /**
29255              * @event mouseout
29256              * Fires when the mouse exits the button
29257              * @param {Button} this
29258              * @param {Event} e The event object
29259              */
29260         'mouseout': true,
29261          /**
29262              * @event render
29263              * Fires when the button is rendered
29264              * @param {Button} this
29265              */
29266         'render': true
29267     });
29268     if(this.menu){
29269         this.menu = Roo.menu.MenuMgr.get(this.menu);
29270     }
29271     // register listeners first!!  - so render can be captured..
29272     Roo.util.Observable.call(this);
29273     if(renderTo){
29274         this.render(renderTo);
29275     }
29276     
29277   
29278 };
29279
29280 Roo.extend(Roo.Button, Roo.util.Observable, {
29281     /**
29282      * 
29283      */
29284     
29285     /**
29286      * Read-only. True if this button is hidden
29287      * @type Boolean
29288      */
29289     hidden : false,
29290     /**
29291      * Read-only. True if this button is disabled
29292      * @type Boolean
29293      */
29294     disabled : false,
29295     /**
29296      * Read-only. True if this button is pressed (only if enableToggle = true)
29297      * @type Boolean
29298      */
29299     pressed : false,
29300
29301     /**
29302      * @cfg {Number} tabIndex 
29303      * The DOM tabIndex for this button (defaults to undefined)
29304      */
29305     tabIndex : undefined,
29306
29307     /**
29308      * @cfg {Boolean} enableToggle
29309      * True to enable pressed/not pressed toggling (defaults to false)
29310      */
29311     enableToggle: false,
29312     /**
29313      * @cfg {Mixed} menu
29314      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
29315      */
29316     menu : undefined,
29317     /**
29318      * @cfg {String} menuAlign
29319      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
29320      */
29321     menuAlign : "tl-bl?",
29322
29323     /**
29324      * @cfg {String} iconCls
29325      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
29326      */
29327     iconCls : undefined,
29328     /**
29329      * @cfg {String} type
29330      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
29331      */
29332     type : 'button',
29333
29334     // private
29335     menuClassTarget: 'tr',
29336
29337     /**
29338      * @cfg {String} clickEvent
29339      * The type of event to map to the button's event handler (defaults to 'click')
29340      */
29341     clickEvent : 'click',
29342
29343     /**
29344      * @cfg {Boolean} handleMouseEvents
29345      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
29346      */
29347     handleMouseEvents : true,
29348
29349     /**
29350      * @cfg {String} tooltipType
29351      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
29352      */
29353     tooltipType : 'qtip',
29354
29355     /**
29356      * @cfg {String} cls
29357      * A CSS class to apply to the button's main element.
29358      */
29359     
29360     /**
29361      * @cfg {Roo.Template} template (Optional)
29362      * An {@link Roo.Template} with which to create the Button's main element. This Template must
29363      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
29364      * require code modifications if required elements (e.g. a button) aren't present.
29365      */
29366
29367     // private
29368     render : function(renderTo){
29369         var btn;
29370         if(this.hideParent){
29371             this.parentEl = Roo.get(renderTo);
29372         }
29373         if(!this.dhconfig){
29374             if(!this.template){
29375                 if(!Roo.Button.buttonTemplate){
29376                     // hideous table template
29377                     Roo.Button.buttonTemplate = new Roo.Template(
29378                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
29379                         '<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>',
29380                         "</tr></tbody></table>");
29381                 }
29382                 this.template = Roo.Button.buttonTemplate;
29383             }
29384             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
29385             var btnEl = btn.child("button:first");
29386             btnEl.on('focus', this.onFocus, this);
29387             btnEl.on('blur', this.onBlur, this);
29388             if(this.cls){
29389                 btn.addClass(this.cls);
29390             }
29391             if(this.icon){
29392                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
29393             }
29394             if(this.iconCls){
29395                 btnEl.addClass(this.iconCls);
29396                 if(!this.cls){
29397                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
29398                 }
29399             }
29400             if(this.tabIndex !== undefined){
29401                 btnEl.dom.tabIndex = this.tabIndex;
29402             }
29403             if(this.tooltip){
29404                 if(typeof this.tooltip == 'object'){
29405                     Roo.QuickTips.tips(Roo.apply({
29406                           target: btnEl.id
29407                     }, this.tooltip));
29408                 } else {
29409                     btnEl.dom[this.tooltipType] = this.tooltip;
29410                 }
29411             }
29412         }else{
29413             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
29414         }
29415         this.el = btn;
29416         if(this.id){
29417             this.el.dom.id = this.el.id = this.id;
29418         }
29419         if(this.menu){
29420             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
29421             this.menu.on("show", this.onMenuShow, this);
29422             this.menu.on("hide", this.onMenuHide, this);
29423         }
29424         btn.addClass("x-btn");
29425         if(Roo.isIE && !Roo.isIE7){
29426             this.autoWidth.defer(1, this);
29427         }else{
29428             this.autoWidth();
29429         }
29430         if(this.handleMouseEvents){
29431             btn.on("mouseover", this.onMouseOver, this);
29432             btn.on("mouseout", this.onMouseOut, this);
29433             btn.on("mousedown", this.onMouseDown, this);
29434         }
29435         btn.on(this.clickEvent, this.onClick, this);
29436         //btn.on("mouseup", this.onMouseUp, this);
29437         if(this.hidden){
29438             this.hide();
29439         }
29440         if(this.disabled){
29441             this.disable();
29442         }
29443         Roo.ButtonToggleMgr.register(this);
29444         if(this.pressed){
29445             this.el.addClass("x-btn-pressed");
29446         }
29447         if(this.repeat){
29448             var repeater = new Roo.util.ClickRepeater(btn,
29449                 typeof this.repeat == "object" ? this.repeat : {}
29450             );
29451             repeater.on("click", this.onClick,  this);
29452         }
29453         
29454         this.fireEvent('render', this);
29455         
29456     },
29457     /**
29458      * Returns the button's underlying element
29459      * @return {Roo.Element} The element
29460      */
29461     getEl : function(){
29462         return this.el;  
29463     },
29464     
29465     /**
29466      * Destroys this Button and removes any listeners.
29467      */
29468     destroy : function(){
29469         Roo.ButtonToggleMgr.unregister(this);
29470         this.el.removeAllListeners();
29471         this.purgeListeners();
29472         this.el.remove();
29473     },
29474
29475     // private
29476     autoWidth : function(){
29477         if(this.el){
29478             this.el.setWidth("auto");
29479             if(Roo.isIE7 && Roo.isStrict){
29480                 var ib = this.el.child('button');
29481                 if(ib && ib.getWidth() > 20){
29482                     ib.clip();
29483                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
29484                 }
29485             }
29486             if(this.minWidth){
29487                 if(this.hidden){
29488                     this.el.beginMeasure();
29489                 }
29490                 if(this.el.getWidth() < this.minWidth){
29491                     this.el.setWidth(this.minWidth);
29492                 }
29493                 if(this.hidden){
29494                     this.el.endMeasure();
29495                 }
29496             }
29497         }
29498     },
29499
29500     /**
29501      * Assigns this button's click handler
29502      * @param {Function} handler The function to call when the button is clicked
29503      * @param {Object} scope (optional) Scope for the function passed in
29504      */
29505     setHandler : function(handler, scope){
29506         this.handler = handler;
29507         this.scope = scope;  
29508     },
29509     
29510     /**
29511      * Sets this button's text
29512      * @param {String} text The button text
29513      */
29514     setText : function(text){
29515         this.text = text;
29516         if(this.el){
29517             this.el.child("td.x-btn-center button.x-btn-text").update(text);
29518         }
29519         this.autoWidth();
29520     },
29521     
29522     /**
29523      * Gets the text for this button
29524      * @return {String} The button text
29525      */
29526     getText : function(){
29527         return this.text;  
29528     },
29529     
29530     /**
29531      * Show this button
29532      */
29533     show: function(){
29534         this.hidden = false;
29535         if(this.el){
29536             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
29537         }
29538     },
29539     
29540     /**
29541      * Hide this button
29542      */
29543     hide: function(){
29544         this.hidden = true;
29545         if(this.el){
29546             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
29547         }
29548     },
29549     
29550     /**
29551      * Convenience function for boolean show/hide
29552      * @param {Boolean} visible True to show, false to hide
29553      */
29554     setVisible: function(visible){
29555         if(visible) {
29556             this.show();
29557         }else{
29558             this.hide();
29559         }
29560     },
29561     
29562     /**
29563      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
29564      * @param {Boolean} state (optional) Force a particular state
29565      */
29566     toggle : function(state){
29567         state = state === undefined ? !this.pressed : state;
29568         if(state != this.pressed){
29569             if(state){
29570                 this.el.addClass("x-btn-pressed");
29571                 this.pressed = true;
29572                 this.fireEvent("toggle", this, true);
29573             }else{
29574                 this.el.removeClass("x-btn-pressed");
29575                 this.pressed = false;
29576                 this.fireEvent("toggle", this, false);
29577             }
29578             if(this.toggleHandler){
29579                 this.toggleHandler.call(this.scope || this, this, state);
29580             }
29581         }
29582     },
29583     
29584     /**
29585      * Focus the button
29586      */
29587     focus : function(){
29588         this.el.child('button:first').focus();
29589     },
29590     
29591     /**
29592      * Disable this button
29593      */
29594     disable : function(){
29595         if(this.el){
29596             this.el.addClass("x-btn-disabled");
29597         }
29598         this.disabled = true;
29599     },
29600     
29601     /**
29602      * Enable this button
29603      */
29604     enable : function(){
29605         if(this.el){
29606             this.el.removeClass("x-btn-disabled");
29607         }
29608         this.disabled = false;
29609     },
29610
29611     /**
29612      * Convenience function for boolean enable/disable
29613      * @param {Boolean} enabled True to enable, false to disable
29614      */
29615     setDisabled : function(v){
29616         this[v !== true ? "enable" : "disable"]();
29617     },
29618
29619     // private
29620     onClick : function(e)
29621     {
29622         if(e){
29623             e.preventDefault();
29624         }
29625         if(e.button != 0){
29626             return;
29627         }
29628         if(!this.disabled){
29629             if(this.enableToggle){
29630                 this.toggle();
29631             }
29632             if(this.menu && !this.menu.isVisible()){
29633                 this.menu.show(this.el, this.menuAlign);
29634             }
29635             this.fireEvent("click", this, e);
29636             if(this.handler){
29637                 this.el.removeClass("x-btn-over");
29638                 this.handler.call(this.scope || this, this, e);
29639             }
29640         }
29641     },
29642     // private
29643     onMouseOver : function(e){
29644         if(!this.disabled){
29645             this.el.addClass("x-btn-over");
29646             this.fireEvent('mouseover', this, e);
29647         }
29648     },
29649     // private
29650     onMouseOut : function(e){
29651         if(!e.within(this.el,  true)){
29652             this.el.removeClass("x-btn-over");
29653             this.fireEvent('mouseout', this, e);
29654         }
29655     },
29656     // private
29657     onFocus : function(e){
29658         if(!this.disabled){
29659             this.el.addClass("x-btn-focus");
29660         }
29661     },
29662     // private
29663     onBlur : function(e){
29664         this.el.removeClass("x-btn-focus");
29665     },
29666     // private
29667     onMouseDown : function(e){
29668         if(!this.disabled && e.button == 0){
29669             this.el.addClass("x-btn-click");
29670             Roo.get(document).on('mouseup', this.onMouseUp, this);
29671         }
29672     },
29673     // private
29674     onMouseUp : function(e){
29675         if(e.button == 0){
29676             this.el.removeClass("x-btn-click");
29677             Roo.get(document).un('mouseup', this.onMouseUp, this);
29678         }
29679     },
29680     // private
29681     onMenuShow : function(e){
29682         this.el.addClass("x-btn-menu-active");
29683     },
29684     // private
29685     onMenuHide : function(e){
29686         this.el.removeClass("x-btn-menu-active");
29687     }   
29688 });
29689
29690 // Private utility class used by Button
29691 Roo.ButtonToggleMgr = function(){
29692    var groups = {};
29693    
29694    function toggleGroup(btn, state){
29695        if(state){
29696            var g = groups[btn.toggleGroup];
29697            for(var i = 0, l = g.length; i < l; i++){
29698                if(g[i] != btn){
29699                    g[i].toggle(false);
29700                }
29701            }
29702        }
29703    }
29704    
29705    return {
29706        register : function(btn){
29707            if(!btn.toggleGroup){
29708                return;
29709            }
29710            var g = groups[btn.toggleGroup];
29711            if(!g){
29712                g = groups[btn.toggleGroup] = [];
29713            }
29714            g.push(btn);
29715            btn.on("toggle", toggleGroup);
29716        },
29717        
29718        unregister : function(btn){
29719            if(!btn.toggleGroup){
29720                return;
29721            }
29722            var g = groups[btn.toggleGroup];
29723            if(g){
29724                g.remove(btn);
29725                btn.un("toggle", toggleGroup);
29726            }
29727        }
29728    };
29729 }();/*
29730  * Based on:
29731  * Ext JS Library 1.1.1
29732  * Copyright(c) 2006-2007, Ext JS, LLC.
29733  *
29734  * Originally Released Under LGPL - original licence link has changed is not relivant.
29735  *
29736  * Fork - LGPL
29737  * <script type="text/javascript">
29738  */
29739  
29740 /**
29741  * @class Roo.SplitButton
29742  * @extends Roo.Button
29743  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
29744  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
29745  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
29746  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
29747  * @cfg {String} arrowTooltip The title attribute of the arrow
29748  * @constructor
29749  * Create a new menu button
29750  * @param {String/HTMLElement/Element} renderTo The element to append the button to
29751  * @param {Object} config The config object
29752  */
29753 Roo.SplitButton = function(renderTo, config){
29754     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
29755     /**
29756      * @event arrowclick
29757      * Fires when this button's arrow is clicked
29758      * @param {SplitButton} this
29759      * @param {EventObject} e The click event
29760      */
29761     this.addEvents({"arrowclick":true});
29762 };
29763
29764 Roo.extend(Roo.SplitButton, Roo.Button, {
29765     render : function(renderTo){
29766         // this is one sweet looking template!
29767         var tpl = new Roo.Template(
29768             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
29769             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
29770             '<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>',
29771             "</tbody></table></td><td>",
29772             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
29773             '<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>',
29774             "</tbody></table></td></tr></table>"
29775         );
29776         var btn = tpl.append(renderTo, [this.text, this.type], true);
29777         var btnEl = btn.child("button");
29778         if(this.cls){
29779             btn.addClass(this.cls);
29780         }
29781         if(this.icon){
29782             btnEl.setStyle('background-image', 'url(' +this.icon +')');
29783         }
29784         if(this.iconCls){
29785             btnEl.addClass(this.iconCls);
29786             if(!this.cls){
29787                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
29788             }
29789         }
29790         this.el = btn;
29791         if(this.handleMouseEvents){
29792             btn.on("mouseover", this.onMouseOver, this);
29793             btn.on("mouseout", this.onMouseOut, this);
29794             btn.on("mousedown", this.onMouseDown, this);
29795             btn.on("mouseup", this.onMouseUp, this);
29796         }
29797         btn.on(this.clickEvent, this.onClick, this);
29798         if(this.tooltip){
29799             if(typeof this.tooltip == 'object'){
29800                 Roo.QuickTips.tips(Roo.apply({
29801                       target: btnEl.id
29802                 }, this.tooltip));
29803             } else {
29804                 btnEl.dom[this.tooltipType] = this.tooltip;
29805             }
29806         }
29807         if(this.arrowTooltip){
29808             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
29809         }
29810         if(this.hidden){
29811             this.hide();
29812         }
29813         if(this.disabled){
29814             this.disable();
29815         }
29816         if(this.pressed){
29817             this.el.addClass("x-btn-pressed");
29818         }
29819         if(Roo.isIE && !Roo.isIE7){
29820             this.autoWidth.defer(1, this);
29821         }else{
29822             this.autoWidth();
29823         }
29824         if(this.menu){
29825             this.menu.on("show", this.onMenuShow, this);
29826             this.menu.on("hide", this.onMenuHide, this);
29827         }
29828         this.fireEvent('render', this);
29829     },
29830
29831     // private
29832     autoWidth : function(){
29833         if(this.el){
29834             var tbl = this.el.child("table:first");
29835             var tbl2 = this.el.child("table:last");
29836             this.el.setWidth("auto");
29837             tbl.setWidth("auto");
29838             if(Roo.isIE7 && Roo.isStrict){
29839                 var ib = this.el.child('button:first');
29840                 if(ib && ib.getWidth() > 20){
29841                     ib.clip();
29842                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
29843                 }
29844             }
29845             if(this.minWidth){
29846                 if(this.hidden){
29847                     this.el.beginMeasure();
29848                 }
29849                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
29850                     tbl.setWidth(this.minWidth-tbl2.getWidth());
29851                 }
29852                 if(this.hidden){
29853                     this.el.endMeasure();
29854                 }
29855             }
29856             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
29857         } 
29858     },
29859     /**
29860      * Sets this button's click handler
29861      * @param {Function} handler The function to call when the button is clicked
29862      * @param {Object} scope (optional) Scope for the function passed above
29863      */
29864     setHandler : function(handler, scope){
29865         this.handler = handler;
29866         this.scope = scope;  
29867     },
29868     
29869     /**
29870      * Sets this button's arrow click handler
29871      * @param {Function} handler The function to call when the arrow is clicked
29872      * @param {Object} scope (optional) Scope for the function passed above
29873      */
29874     setArrowHandler : function(handler, scope){
29875         this.arrowHandler = handler;
29876         this.scope = scope;  
29877     },
29878     
29879     /**
29880      * Focus the button
29881      */
29882     focus : function(){
29883         if(this.el){
29884             this.el.child("button:first").focus();
29885         }
29886     },
29887
29888     // private
29889     onClick : function(e){
29890         e.preventDefault();
29891         if(!this.disabled){
29892             if(e.getTarget(".x-btn-menu-arrow-wrap")){
29893                 if(this.menu && !this.menu.isVisible()){
29894                     this.menu.show(this.el, this.menuAlign);
29895                 }
29896                 this.fireEvent("arrowclick", this, e);
29897                 if(this.arrowHandler){
29898                     this.arrowHandler.call(this.scope || this, this, e);
29899                 }
29900             }else{
29901                 this.fireEvent("click", this, e);
29902                 if(this.handler){
29903                     this.handler.call(this.scope || this, this, e);
29904                 }
29905             }
29906         }
29907     },
29908     // private
29909     onMouseDown : function(e){
29910         if(!this.disabled){
29911             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
29912         }
29913     },
29914     // private
29915     onMouseUp : function(e){
29916         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
29917     }   
29918 });
29919
29920
29921 // backwards compat
29922 Roo.MenuButton = Roo.SplitButton;/*
29923  * Based on:
29924  * Ext JS Library 1.1.1
29925  * Copyright(c) 2006-2007, Ext JS, LLC.
29926  *
29927  * Originally Released Under LGPL - original licence link has changed is not relivant.
29928  *
29929  * Fork - LGPL
29930  * <script type="text/javascript">
29931  */
29932
29933 /**
29934  * @class Roo.Toolbar
29935  * Basic Toolbar class.
29936  * @constructor
29937  * Creates a new Toolbar
29938  * @param {Object} container The config object
29939  */ 
29940 Roo.Toolbar = function(container, buttons, config)
29941 {
29942     /// old consturctor format still supported..
29943     if(container instanceof Array){ // omit the container for later rendering
29944         buttons = container;
29945         config = buttons;
29946         container = null;
29947     }
29948     if (typeof(container) == 'object' && container.xtype) {
29949         config = container;
29950         container = config.container;
29951         buttons = config.buttons || []; // not really - use items!!
29952     }
29953     var xitems = [];
29954     if (config && config.items) {
29955         xitems = config.items;
29956         delete config.items;
29957     }
29958     Roo.apply(this, config);
29959     this.buttons = buttons;
29960     
29961     if(container){
29962         this.render(container);
29963     }
29964     this.xitems = xitems;
29965     Roo.each(xitems, function(b) {
29966         this.add(b);
29967     }, this);
29968     
29969 };
29970
29971 Roo.Toolbar.prototype = {
29972     /**
29973      * @cfg {Array} items
29974      * array of button configs or elements to add (will be converted to a MixedCollection)
29975      */
29976     
29977     /**
29978      * @cfg {String/HTMLElement/Element} container
29979      * The id or element that will contain the toolbar
29980      */
29981     // private
29982     render : function(ct){
29983         this.el = Roo.get(ct);
29984         if(this.cls){
29985             this.el.addClass(this.cls);
29986         }
29987         // using a table allows for vertical alignment
29988         // 100% width is needed by Safari...
29989         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
29990         this.tr = this.el.child("tr", true);
29991         var autoId = 0;
29992         this.items = new Roo.util.MixedCollection(false, function(o){
29993             return o.id || ("item" + (++autoId));
29994         });
29995         if(this.buttons){
29996             this.add.apply(this, this.buttons);
29997             delete this.buttons;
29998         }
29999     },
30000
30001     /**
30002      * Adds element(s) to the toolbar -- this function takes a variable number of 
30003      * arguments of mixed type and adds them to the toolbar.
30004      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
30005      * <ul>
30006      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
30007      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
30008      * <li>Field: Any form field (equivalent to {@link #addField})</li>
30009      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
30010      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
30011      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
30012      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
30013      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
30014      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
30015      * </ul>
30016      * @param {Mixed} arg2
30017      * @param {Mixed} etc.
30018      */
30019     add : function(){
30020         var a = arguments, l = a.length;
30021         for(var i = 0; i < l; i++){
30022             this._add(a[i]);
30023         }
30024     },
30025     // private..
30026     _add : function(el) {
30027         
30028         if (el.xtype) {
30029             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
30030         }
30031         
30032         if (el.applyTo){ // some kind of form field
30033             return this.addField(el);
30034         } 
30035         if (el.render){ // some kind of Toolbar.Item
30036             return this.addItem(el);
30037         }
30038         if (typeof el == "string"){ // string
30039             if(el == "separator" || el == "-"){
30040                 return this.addSeparator();
30041             }
30042             if (el == " "){
30043                 return this.addSpacer();
30044             }
30045             if(el == "->"){
30046                 return this.addFill();
30047             }
30048             return this.addText(el);
30049             
30050         }
30051         if(el.tagName){ // element
30052             return this.addElement(el);
30053         }
30054         if(typeof el == "object"){ // must be button config?
30055             return this.addButton(el);
30056         }
30057         // and now what?!?!
30058         return false;
30059         
30060     },
30061     
30062     /**
30063      * Add an Xtype element
30064      * @param {Object} xtype Xtype Object
30065      * @return {Object} created Object
30066      */
30067     addxtype : function(e){
30068         return this.add(e);  
30069     },
30070     
30071     /**
30072      * Returns the Element for this toolbar.
30073      * @return {Roo.Element}
30074      */
30075     getEl : function(){
30076         return this.el;  
30077     },
30078     
30079     /**
30080      * Adds a separator
30081      * @return {Roo.Toolbar.Item} The separator item
30082      */
30083     addSeparator : function(){
30084         return this.addItem(new Roo.Toolbar.Separator());
30085     },
30086
30087     /**
30088      * Adds a spacer element
30089      * @return {Roo.Toolbar.Spacer} The spacer item
30090      */
30091     addSpacer : function(){
30092         return this.addItem(new Roo.Toolbar.Spacer());
30093     },
30094
30095     /**
30096      * Adds a fill element that forces subsequent additions to the right side of the toolbar
30097      * @return {Roo.Toolbar.Fill} The fill item
30098      */
30099     addFill : function(){
30100         return this.addItem(new Roo.Toolbar.Fill());
30101     },
30102
30103     /**
30104      * Adds any standard HTML element to the toolbar
30105      * @param {String/HTMLElement/Element} el The element or id of the element to add
30106      * @return {Roo.Toolbar.Item} The element's item
30107      */
30108     addElement : function(el){
30109         return this.addItem(new Roo.Toolbar.Item(el));
30110     },
30111     /**
30112      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
30113      * @type Roo.util.MixedCollection  
30114      */
30115     items : false,
30116      
30117     /**
30118      * Adds any Toolbar.Item or subclass
30119      * @param {Roo.Toolbar.Item} item
30120      * @return {Roo.Toolbar.Item} The item
30121      */
30122     addItem : function(item){
30123         var td = this.nextBlock();
30124         item.render(td);
30125         this.items.add(item);
30126         return item;
30127     },
30128     
30129     /**
30130      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
30131      * @param {Object/Array} config A button config or array of configs
30132      * @return {Roo.Toolbar.Button/Array}
30133      */
30134     addButton : function(config){
30135         if(config instanceof Array){
30136             var buttons = [];
30137             for(var i = 0, len = config.length; i < len; i++) {
30138                 buttons.push(this.addButton(config[i]));
30139             }
30140             return buttons;
30141         }
30142         var b = config;
30143         if(!(config instanceof Roo.Toolbar.Button)){
30144             b = config.split ?
30145                 new Roo.Toolbar.SplitButton(config) :
30146                 new Roo.Toolbar.Button(config);
30147         }
30148         var td = this.nextBlock();
30149         b.render(td);
30150         this.items.add(b);
30151         return b;
30152     },
30153     
30154     /**
30155      * Adds text to the toolbar
30156      * @param {String} text The text to add
30157      * @return {Roo.Toolbar.Item} The element's item
30158      */
30159     addText : function(text){
30160         return this.addItem(new Roo.Toolbar.TextItem(text));
30161     },
30162     
30163     /**
30164      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
30165      * @param {Number} index The index where the item is to be inserted
30166      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
30167      * @return {Roo.Toolbar.Button/Item}
30168      */
30169     insertButton : function(index, item){
30170         if(item instanceof Array){
30171             var buttons = [];
30172             for(var i = 0, len = item.length; i < len; i++) {
30173                buttons.push(this.insertButton(index + i, item[i]));
30174             }
30175             return buttons;
30176         }
30177         if (!(item instanceof Roo.Toolbar.Button)){
30178            item = new Roo.Toolbar.Button(item);
30179         }
30180         var td = document.createElement("td");
30181         this.tr.insertBefore(td, this.tr.childNodes[index]);
30182         item.render(td);
30183         this.items.insert(index, item);
30184         return item;
30185     },
30186     
30187     /**
30188      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
30189      * @param {Object} config
30190      * @return {Roo.Toolbar.Item} The element's item
30191      */
30192     addDom : function(config, returnEl){
30193         var td = this.nextBlock();
30194         Roo.DomHelper.overwrite(td, config);
30195         var ti = new Roo.Toolbar.Item(td.firstChild);
30196         ti.render(td);
30197         this.items.add(ti);
30198         return ti;
30199     },
30200
30201     /**
30202      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
30203      * @type Roo.util.MixedCollection  
30204      */
30205     fields : false,
30206     
30207     /**
30208      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
30209      * Note: the field should not have been rendered yet. For a field that has already been
30210      * rendered, use {@link #addElement}.
30211      * @param {Roo.form.Field} field
30212      * @return {Roo.ToolbarItem}
30213      */
30214      
30215       
30216     addField : function(field) {
30217         if (!this.fields) {
30218             var autoId = 0;
30219             this.fields = new Roo.util.MixedCollection(false, function(o){
30220                 return o.id || ("item" + (++autoId));
30221             });
30222
30223         }
30224         
30225         var td = this.nextBlock();
30226         field.render(td);
30227         var ti = new Roo.Toolbar.Item(td.firstChild);
30228         ti.render(td);
30229         this.items.add(ti);
30230         this.fields.add(field);
30231         return ti;
30232     },
30233     /**
30234      * Hide the toolbar
30235      * @method hide
30236      */
30237      
30238       
30239     hide : function()
30240     {
30241         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
30242         this.el.child('div').hide();
30243     },
30244     /**
30245      * Show the toolbar
30246      * @method show
30247      */
30248     show : function()
30249     {
30250         this.el.child('div').show();
30251     },
30252       
30253     // private
30254     nextBlock : function(){
30255         var td = document.createElement("td");
30256         this.tr.appendChild(td);
30257         return td;
30258     },
30259
30260     // private
30261     destroy : function(){
30262         if(this.items){ // rendered?
30263             Roo.destroy.apply(Roo, this.items.items);
30264         }
30265         if(this.fields){ // rendered?
30266             Roo.destroy.apply(Roo, this.fields.items);
30267         }
30268         Roo.Element.uncache(this.el, this.tr);
30269     }
30270 };
30271
30272 /**
30273  * @class Roo.Toolbar.Item
30274  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
30275  * @constructor
30276  * Creates a new Item
30277  * @param {HTMLElement} el 
30278  */
30279 Roo.Toolbar.Item = function(el){
30280     var cfg = {};
30281     if (typeof (el.xtype) != 'undefined') {
30282         cfg = el;
30283         el = cfg.el;
30284     }
30285     
30286     this.el = Roo.getDom(el);
30287     this.id = Roo.id(this.el);
30288     this.hidden = false;
30289     
30290     this.addEvents({
30291          /**
30292              * @event render
30293              * Fires when the button is rendered
30294              * @param {Button} this
30295              */
30296         'render': true
30297     });
30298     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
30299 };
30300 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
30301 //Roo.Toolbar.Item.prototype = {
30302     
30303     /**
30304      * Get this item's HTML Element
30305      * @return {HTMLElement}
30306      */
30307     getEl : function(){
30308        return this.el;  
30309     },
30310
30311     // private
30312     render : function(td){
30313         
30314          this.td = td;
30315         td.appendChild(this.el);
30316         
30317         this.fireEvent('render', this);
30318     },
30319     
30320     /**
30321      * Removes and destroys this item.
30322      */
30323     destroy : function(){
30324         this.td.parentNode.removeChild(this.td);
30325     },
30326     
30327     /**
30328      * Shows this item.
30329      */
30330     show: function(){
30331         this.hidden = false;
30332         this.td.style.display = "";
30333     },
30334     
30335     /**
30336      * Hides this item.
30337      */
30338     hide: function(){
30339         this.hidden = true;
30340         this.td.style.display = "none";
30341     },
30342     
30343     /**
30344      * Convenience function for boolean show/hide.
30345      * @param {Boolean} visible true to show/false to hide
30346      */
30347     setVisible: function(visible){
30348         if(visible) {
30349             this.show();
30350         }else{
30351             this.hide();
30352         }
30353     },
30354     
30355     /**
30356      * Try to focus this item.
30357      */
30358     focus : function(){
30359         Roo.fly(this.el).focus();
30360     },
30361     
30362     /**
30363      * Disables this item.
30364      */
30365     disable : function(){
30366         Roo.fly(this.td).addClass("x-item-disabled");
30367         this.disabled = true;
30368         this.el.disabled = true;
30369     },
30370     
30371     /**
30372      * Enables this item.
30373      */
30374     enable : function(){
30375         Roo.fly(this.td).removeClass("x-item-disabled");
30376         this.disabled = false;
30377         this.el.disabled = false;
30378     }
30379 });
30380
30381
30382 /**
30383  * @class Roo.Toolbar.Separator
30384  * @extends Roo.Toolbar.Item
30385  * A simple toolbar separator class
30386  * @constructor
30387  * Creates a new Separator
30388  */
30389 Roo.Toolbar.Separator = function(cfg){
30390     
30391     var s = document.createElement("span");
30392     s.className = "ytb-sep";
30393     if (cfg) {
30394         cfg.el = s;
30395     }
30396     
30397     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
30398 };
30399 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
30400     enable:Roo.emptyFn,
30401     disable:Roo.emptyFn,
30402     focus:Roo.emptyFn
30403 });
30404
30405 /**
30406  * @class Roo.Toolbar.Spacer
30407  * @extends Roo.Toolbar.Item
30408  * A simple element that adds extra horizontal space to a toolbar.
30409  * @constructor
30410  * Creates a new Spacer
30411  */
30412 Roo.Toolbar.Spacer = function(cfg){
30413     var s = document.createElement("div");
30414     s.className = "ytb-spacer";
30415     if (cfg) {
30416         cfg.el = s;
30417     }
30418     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
30419 };
30420 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
30421     enable:Roo.emptyFn,
30422     disable:Roo.emptyFn,
30423     focus:Roo.emptyFn
30424 });
30425
30426 /**
30427  * @class Roo.Toolbar.Fill
30428  * @extends Roo.Toolbar.Spacer
30429  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
30430  * @constructor
30431  * Creates a new Spacer
30432  */
30433 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
30434     // private
30435     render : function(td){
30436         td.style.width = '100%';
30437         Roo.Toolbar.Fill.superclass.render.call(this, td);
30438     }
30439 });
30440
30441 /**
30442  * @class Roo.Toolbar.TextItem
30443  * @extends Roo.Toolbar.Item
30444  * A simple class that renders text directly into a toolbar.
30445  * @constructor
30446  * Creates a new TextItem
30447  * @param {String} text
30448  */
30449 Roo.Toolbar.TextItem = function(cfg){
30450     var  text = cfg || "";
30451     if (typeof(cfg) == 'object') {
30452         text = cfg.text || "";
30453     }  else {
30454         cfg = null;
30455     }
30456     var s = document.createElement("span");
30457     s.className = "ytb-text";
30458     s.innerHTML = text;
30459     if (cfg) {
30460         cfg.el  = s;
30461     }
30462     
30463     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
30464 };
30465 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
30466     
30467      
30468     enable:Roo.emptyFn,
30469     disable:Roo.emptyFn,
30470     focus:Roo.emptyFn
30471 });
30472
30473 /**
30474  * @class Roo.Toolbar.Button
30475  * @extends Roo.Button
30476  * A button that renders into a toolbar.
30477  * @constructor
30478  * Creates a new Button
30479  * @param {Object} config A standard {@link Roo.Button} config object
30480  */
30481 Roo.Toolbar.Button = function(config){
30482     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
30483 };
30484 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
30485     render : function(td){
30486         this.td = td;
30487         Roo.Toolbar.Button.superclass.render.call(this, td);
30488     },
30489     
30490     /**
30491      * Removes and destroys this button
30492      */
30493     destroy : function(){
30494         Roo.Toolbar.Button.superclass.destroy.call(this);
30495         this.td.parentNode.removeChild(this.td);
30496     },
30497     
30498     /**
30499      * Shows this button
30500      */
30501     show: function(){
30502         this.hidden = false;
30503         this.td.style.display = "";
30504     },
30505     
30506     /**
30507      * Hides this button
30508      */
30509     hide: function(){
30510         this.hidden = true;
30511         this.td.style.display = "none";
30512     },
30513
30514     /**
30515      * Disables this item
30516      */
30517     disable : function(){
30518         Roo.fly(this.td).addClass("x-item-disabled");
30519         this.disabled = true;
30520     },
30521
30522     /**
30523      * Enables this item
30524      */
30525     enable : function(){
30526         Roo.fly(this.td).removeClass("x-item-disabled");
30527         this.disabled = false;
30528     }
30529 });
30530 // backwards compat
30531 Roo.ToolbarButton = Roo.Toolbar.Button;
30532
30533 /**
30534  * @class Roo.Toolbar.SplitButton
30535  * @extends Roo.SplitButton
30536  * A menu button that renders into a toolbar.
30537  * @constructor
30538  * Creates a new SplitButton
30539  * @param {Object} config A standard {@link Roo.SplitButton} config object
30540  */
30541 Roo.Toolbar.SplitButton = function(config){
30542     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
30543 };
30544 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
30545     render : function(td){
30546         this.td = td;
30547         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
30548     },
30549     
30550     /**
30551      * Removes and destroys this button
30552      */
30553     destroy : function(){
30554         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
30555         this.td.parentNode.removeChild(this.td);
30556     },
30557     
30558     /**
30559      * Shows this button
30560      */
30561     show: function(){
30562         this.hidden = false;
30563         this.td.style.display = "";
30564     },
30565     
30566     /**
30567      * Hides this button
30568      */
30569     hide: function(){
30570         this.hidden = true;
30571         this.td.style.display = "none";
30572     }
30573 });
30574
30575 // backwards compat
30576 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
30577  * Based on:
30578  * Ext JS Library 1.1.1
30579  * Copyright(c) 2006-2007, Ext JS, LLC.
30580  *
30581  * Originally Released Under LGPL - original licence link has changed is not relivant.
30582  *
30583  * Fork - LGPL
30584  * <script type="text/javascript">
30585  */
30586  
30587 /**
30588  * @class Roo.PagingToolbar
30589  * @extends Roo.Toolbar
30590  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
30591  * @constructor
30592  * Create a new PagingToolbar
30593  * @param {Object} config The config object
30594  */
30595 Roo.PagingToolbar = function(el, ds, config)
30596 {
30597     // old args format still supported... - xtype is prefered..
30598     if (typeof(el) == 'object' && el.xtype) {
30599         // created from xtype...
30600         config = el;
30601         ds = el.dataSource;
30602         el = config.container;
30603     }
30604     var items = [];
30605     if (config.items) {
30606         items = config.items;
30607         config.items = [];
30608     }
30609     
30610     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
30611     this.ds = ds;
30612     this.cursor = 0;
30613     this.renderButtons(this.el);
30614     this.bind(ds);
30615     
30616     // supprot items array.
30617    
30618     Roo.each(items, function(e) {
30619         this.add(Roo.factory(e));
30620     },this);
30621     
30622 };
30623
30624 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
30625     /**
30626      * @cfg {Roo.data.Store} dataSource
30627      * The underlying data store providing the paged data
30628      */
30629     /**
30630      * @cfg {String/HTMLElement/Element} container
30631      * container The id or element that will contain the toolbar
30632      */
30633     /**
30634      * @cfg {Boolean} displayInfo
30635      * True to display the displayMsg (defaults to false)
30636      */
30637     /**
30638      * @cfg {Number} pageSize
30639      * The number of records to display per page (defaults to 20)
30640      */
30641     pageSize: 20,
30642     /**
30643      * @cfg {String} displayMsg
30644      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
30645      */
30646     displayMsg : 'Displaying {0} - {1} of {2}',
30647     /**
30648      * @cfg {String} emptyMsg
30649      * The message to display when no records are found (defaults to "No data to display")
30650      */
30651     emptyMsg : 'No data to display',
30652     /**
30653      * Customizable piece of the default paging text (defaults to "Page")
30654      * @type String
30655      */
30656     beforePageText : "Page",
30657     /**
30658      * Customizable piece of the default paging text (defaults to "of %0")
30659      * @type String
30660      */
30661     afterPageText : "of {0}",
30662     /**
30663      * Customizable piece of the default paging text (defaults to "First Page")
30664      * @type String
30665      */
30666     firstText : "First Page",
30667     /**
30668      * Customizable piece of the default paging text (defaults to "Previous Page")
30669      * @type String
30670      */
30671     prevText : "Previous Page",
30672     /**
30673      * Customizable piece of the default paging text (defaults to "Next Page")
30674      * @type String
30675      */
30676     nextText : "Next Page",
30677     /**
30678      * Customizable piece of the default paging text (defaults to "Last Page")
30679      * @type String
30680      */
30681     lastText : "Last Page",
30682     /**
30683      * Customizable piece of the default paging text (defaults to "Refresh")
30684      * @type String
30685      */
30686     refreshText : "Refresh",
30687
30688     // private
30689     renderButtons : function(el){
30690         Roo.PagingToolbar.superclass.render.call(this, el);
30691         this.first = this.addButton({
30692             tooltip: this.firstText,
30693             cls: "x-btn-icon x-grid-page-first",
30694             disabled: true,
30695             handler: this.onClick.createDelegate(this, ["first"])
30696         });
30697         this.prev = this.addButton({
30698             tooltip: this.prevText,
30699             cls: "x-btn-icon x-grid-page-prev",
30700             disabled: true,
30701             handler: this.onClick.createDelegate(this, ["prev"])
30702         });
30703         //this.addSeparator();
30704         this.add(this.beforePageText);
30705         this.field = Roo.get(this.addDom({
30706            tag: "input",
30707            type: "text",
30708            size: "3",
30709            value: "1",
30710            cls: "x-grid-page-number"
30711         }).el);
30712         this.field.on("keydown", this.onPagingKeydown, this);
30713         this.field.on("focus", function(){this.dom.select();});
30714         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
30715         this.field.setHeight(18);
30716         //this.addSeparator();
30717         this.next = this.addButton({
30718             tooltip: this.nextText,
30719             cls: "x-btn-icon x-grid-page-next",
30720             disabled: true,
30721             handler: this.onClick.createDelegate(this, ["next"])
30722         });
30723         this.last = this.addButton({
30724             tooltip: this.lastText,
30725             cls: "x-btn-icon x-grid-page-last",
30726             disabled: true,
30727             handler: this.onClick.createDelegate(this, ["last"])
30728         });
30729         //this.addSeparator();
30730         this.loading = this.addButton({
30731             tooltip: this.refreshText,
30732             cls: "x-btn-icon x-grid-loading",
30733             handler: this.onClick.createDelegate(this, ["refresh"])
30734         });
30735
30736         if(this.displayInfo){
30737             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
30738         }
30739     },
30740
30741     // private
30742     updateInfo : function(){
30743         if(this.displayEl){
30744             var count = this.ds.getCount();
30745             var msg = count == 0 ?
30746                 this.emptyMsg :
30747                 String.format(
30748                     this.displayMsg,
30749                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
30750                 );
30751             this.displayEl.update(msg);
30752         }
30753     },
30754
30755     // private
30756     onLoad : function(ds, r, o){
30757        this.cursor = o.params ? o.params.start : 0;
30758        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
30759
30760        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
30761        this.field.dom.value = ap;
30762        this.first.setDisabled(ap == 1);
30763        this.prev.setDisabled(ap == 1);
30764        this.next.setDisabled(ap == ps);
30765        this.last.setDisabled(ap == ps);
30766        this.loading.enable();
30767        this.updateInfo();
30768     },
30769
30770     // private
30771     getPageData : function(){
30772         var total = this.ds.getTotalCount();
30773         return {
30774             total : total,
30775             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
30776             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
30777         };
30778     },
30779
30780     // private
30781     onLoadError : function(){
30782         this.loading.enable();
30783     },
30784
30785     // private
30786     onPagingKeydown : function(e){
30787         var k = e.getKey();
30788         var d = this.getPageData();
30789         if(k == e.RETURN){
30790             var v = this.field.dom.value, pageNum;
30791             if(!v || isNaN(pageNum = parseInt(v, 10))){
30792                 this.field.dom.value = d.activePage;
30793                 return;
30794             }
30795             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
30796             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
30797             e.stopEvent();
30798         }
30799         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))
30800         {
30801           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
30802           this.field.dom.value = pageNum;
30803           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
30804           e.stopEvent();
30805         }
30806         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
30807         {
30808           var v = this.field.dom.value, pageNum; 
30809           var increment = (e.shiftKey) ? 10 : 1;
30810           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
30811             increment *= -1;
30812           }
30813           if(!v || isNaN(pageNum = parseInt(v, 10))) {
30814             this.field.dom.value = d.activePage;
30815             return;
30816           }
30817           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
30818           {
30819             this.field.dom.value = parseInt(v, 10) + increment;
30820             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
30821             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
30822           }
30823           e.stopEvent();
30824         }
30825     },
30826
30827     // private
30828     beforeLoad : function(){
30829         if(this.loading){
30830             this.loading.disable();
30831         }
30832     },
30833
30834     // private
30835     onClick : function(which){
30836         var ds = this.ds;
30837         switch(which){
30838             case "first":
30839                 ds.load({params:{start: 0, limit: this.pageSize}});
30840             break;
30841             case "prev":
30842                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
30843             break;
30844             case "next":
30845                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
30846             break;
30847             case "last":
30848                 var total = ds.getTotalCount();
30849                 var extra = total % this.pageSize;
30850                 var lastStart = extra ? (total - extra) : total-this.pageSize;
30851                 ds.load({params:{start: lastStart, limit: this.pageSize}});
30852             break;
30853             case "refresh":
30854                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
30855             break;
30856         }
30857     },
30858
30859     /**
30860      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
30861      * @param {Roo.data.Store} store The data store to unbind
30862      */
30863     unbind : function(ds){
30864         ds.un("beforeload", this.beforeLoad, this);
30865         ds.un("load", this.onLoad, this);
30866         ds.un("loadexception", this.onLoadError, this);
30867         ds.un("remove", this.updateInfo, this);
30868         ds.un("add", this.updateInfo, this);
30869         this.ds = undefined;
30870     },
30871
30872     /**
30873      * Binds the paging toolbar to the specified {@link Roo.data.Store}
30874      * @param {Roo.data.Store} store The data store to bind
30875      */
30876     bind : function(ds){
30877         ds.on("beforeload", this.beforeLoad, this);
30878         ds.on("load", this.onLoad, this);
30879         ds.on("loadexception", this.onLoadError, this);
30880         ds.on("remove", this.updateInfo, this);
30881         ds.on("add", this.updateInfo, this);
30882         this.ds = ds;
30883     }
30884 });/*
30885  * Based on:
30886  * Ext JS Library 1.1.1
30887  * Copyright(c) 2006-2007, Ext JS, LLC.
30888  *
30889  * Originally Released Under LGPL - original licence link has changed is not relivant.
30890  *
30891  * Fork - LGPL
30892  * <script type="text/javascript">
30893  */
30894
30895 /**
30896  * @class Roo.Resizable
30897  * @extends Roo.util.Observable
30898  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
30899  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
30900  * 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
30901  * the element will be wrapped for you automatically.</p>
30902  * <p>Here is the list of valid resize handles:</p>
30903  * <pre>
30904 Value   Description
30905 ------  -------------------
30906  'n'     north
30907  's'     south
30908  'e'     east
30909  'w'     west
30910  'nw'    northwest
30911  'sw'    southwest
30912  'se'    southeast
30913  'ne'    northeast
30914  'hd'    horizontal drag
30915  'all'   all
30916 </pre>
30917  * <p>Here's an example showing the creation of a typical Resizable:</p>
30918  * <pre><code>
30919 var resizer = new Roo.Resizable("element-id", {
30920     handles: 'all',
30921     minWidth: 200,
30922     minHeight: 100,
30923     maxWidth: 500,
30924     maxHeight: 400,
30925     pinned: true
30926 });
30927 resizer.on("resize", myHandler);
30928 </code></pre>
30929  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
30930  * resizer.east.setDisplayed(false);</p>
30931  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
30932  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
30933  * resize operation's new size (defaults to [0, 0])
30934  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
30935  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
30936  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
30937  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
30938  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
30939  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
30940  * @cfg {Number} width The width of the element in pixels (defaults to null)
30941  * @cfg {Number} height The height of the element in pixels (defaults to null)
30942  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
30943  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
30944  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
30945  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
30946  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
30947  * in favor of the handles config option (defaults to false)
30948  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
30949  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
30950  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
30951  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
30952  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
30953  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
30954  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
30955  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
30956  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
30957  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
30958  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
30959  * @constructor
30960  * Create a new resizable component
30961  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
30962  * @param {Object} config configuration options
30963   */
30964 Roo.Resizable = function(el, config)
30965 {
30966     this.el = Roo.get(el);
30967
30968     if(config && config.wrap){
30969         config.resizeChild = this.el;
30970         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
30971         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
30972         this.el.setStyle("overflow", "hidden");
30973         this.el.setPositioning(config.resizeChild.getPositioning());
30974         config.resizeChild.clearPositioning();
30975         if(!config.width || !config.height){
30976             var csize = config.resizeChild.getSize();
30977             this.el.setSize(csize.width, csize.height);
30978         }
30979         if(config.pinned && !config.adjustments){
30980             config.adjustments = "auto";
30981         }
30982     }
30983
30984     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
30985     this.proxy.unselectable();
30986     this.proxy.enableDisplayMode('block');
30987
30988     Roo.apply(this, config);
30989
30990     if(this.pinned){
30991         this.disableTrackOver = true;
30992         this.el.addClass("x-resizable-pinned");
30993     }
30994     // if the element isn't positioned, make it relative
30995     var position = this.el.getStyle("position");
30996     if(position != "absolute" && position != "fixed"){
30997         this.el.setStyle("position", "relative");
30998     }
30999     if(!this.handles){ // no handles passed, must be legacy style
31000         this.handles = 's,e,se';
31001         if(this.multiDirectional){
31002             this.handles += ',n,w';
31003         }
31004     }
31005     if(this.handles == "all"){
31006         this.handles = "n s e w ne nw se sw";
31007     }
31008     var hs = this.handles.split(/\s*?[,;]\s*?| /);
31009     var ps = Roo.Resizable.positions;
31010     for(var i = 0, len = hs.length; i < len; i++){
31011         if(hs[i] && ps[hs[i]]){
31012             var pos = ps[hs[i]];
31013             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
31014         }
31015     }
31016     // legacy
31017     this.corner = this.southeast;
31018     
31019     // updateBox = the box can move..
31020     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
31021         this.updateBox = true;
31022     }
31023
31024     this.activeHandle = null;
31025
31026     if(this.resizeChild){
31027         if(typeof this.resizeChild == "boolean"){
31028             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
31029         }else{
31030             this.resizeChild = Roo.get(this.resizeChild, true);
31031         }
31032     }
31033     
31034     if(this.adjustments == "auto"){
31035         var rc = this.resizeChild;
31036         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
31037         if(rc && (hw || hn)){
31038             rc.position("relative");
31039             rc.setLeft(hw ? hw.el.getWidth() : 0);
31040             rc.setTop(hn ? hn.el.getHeight() : 0);
31041         }
31042         this.adjustments = [
31043             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
31044             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
31045         ];
31046     }
31047
31048     if(this.draggable){
31049         this.dd = this.dynamic ?
31050             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
31051         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
31052     }
31053
31054     // public events
31055     this.addEvents({
31056         /**
31057          * @event beforeresize
31058          * Fired before resize is allowed. Set enabled to false to cancel resize.
31059          * @param {Roo.Resizable} this
31060          * @param {Roo.EventObject} e The mousedown event
31061          */
31062         "beforeresize" : true,
31063         /**
31064          * @event resizing
31065          * Fired a resizing.
31066          * @param {Roo.Resizable} this
31067          * @param {Number} x The new x position
31068          * @param {Number} y The new y position
31069          * @param {Number} w The new w width
31070          * @param {Number} h The new h hight
31071          * @param {Roo.EventObject} e The mouseup event
31072          */
31073         "resizing" : true,
31074         /**
31075          * @event resize
31076          * Fired after a resize.
31077          * @param {Roo.Resizable} this
31078          * @param {Number} width The new width
31079          * @param {Number} height The new height
31080          * @param {Roo.EventObject} e The mouseup event
31081          */
31082         "resize" : true
31083     });
31084
31085     if(this.width !== null && this.height !== null){
31086         this.resizeTo(this.width, this.height);
31087     }else{
31088         this.updateChildSize();
31089     }
31090     if(Roo.isIE){
31091         this.el.dom.style.zoom = 1;
31092     }
31093     Roo.Resizable.superclass.constructor.call(this);
31094 };
31095
31096 Roo.extend(Roo.Resizable, Roo.util.Observable, {
31097         resizeChild : false,
31098         adjustments : [0, 0],
31099         minWidth : 5,
31100         minHeight : 5,
31101         maxWidth : 10000,
31102         maxHeight : 10000,
31103         enabled : true,
31104         animate : false,
31105         duration : .35,
31106         dynamic : false,
31107         handles : false,
31108         multiDirectional : false,
31109         disableTrackOver : false,
31110         easing : 'easeOutStrong',
31111         widthIncrement : 0,
31112         heightIncrement : 0,
31113         pinned : false,
31114         width : null,
31115         height : null,
31116         preserveRatio : false,
31117         transparent: false,
31118         minX: 0,
31119         minY: 0,
31120         draggable: false,
31121
31122         /**
31123          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
31124          */
31125         constrainTo: undefined,
31126         /**
31127          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
31128          */
31129         resizeRegion: undefined,
31130
31131
31132     /**
31133      * Perform a manual resize
31134      * @param {Number} width
31135      * @param {Number} height
31136      */
31137     resizeTo : function(width, height){
31138         this.el.setSize(width, height);
31139         this.updateChildSize();
31140         this.fireEvent("resize", this, width, height, null);
31141     },
31142
31143     // private
31144     startSizing : function(e, handle){
31145         this.fireEvent("beforeresize", this, e);
31146         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
31147
31148             if(!this.overlay){
31149                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
31150                 this.overlay.unselectable();
31151                 this.overlay.enableDisplayMode("block");
31152                 this.overlay.on("mousemove", this.onMouseMove, this);
31153                 this.overlay.on("mouseup", this.onMouseUp, this);
31154             }
31155             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
31156
31157             this.resizing = true;
31158             this.startBox = this.el.getBox();
31159             this.startPoint = e.getXY();
31160             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
31161                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
31162
31163             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
31164             this.overlay.show();
31165
31166             if(this.constrainTo) {
31167                 var ct = Roo.get(this.constrainTo);
31168                 this.resizeRegion = ct.getRegion().adjust(
31169                     ct.getFrameWidth('t'),
31170                     ct.getFrameWidth('l'),
31171                     -ct.getFrameWidth('b'),
31172                     -ct.getFrameWidth('r')
31173                 );
31174             }
31175
31176             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
31177             this.proxy.show();
31178             this.proxy.setBox(this.startBox);
31179             if(!this.dynamic){
31180                 this.proxy.setStyle('visibility', 'visible');
31181             }
31182         }
31183     },
31184
31185     // private
31186     onMouseDown : function(handle, e){
31187         if(this.enabled){
31188             e.stopEvent();
31189             this.activeHandle = handle;
31190             this.startSizing(e, handle);
31191         }
31192     },
31193
31194     // private
31195     onMouseUp : function(e){
31196         var size = this.resizeElement();
31197         this.resizing = false;
31198         this.handleOut();
31199         this.overlay.hide();
31200         this.proxy.hide();
31201         this.fireEvent("resize", this, size.width, size.height, e);
31202     },
31203
31204     // private
31205     updateChildSize : function(){
31206         
31207         if(this.resizeChild){
31208             var el = this.el;
31209             var child = this.resizeChild;
31210             var adj = this.adjustments;
31211             if(el.dom.offsetWidth){
31212                 var b = el.getSize(true);
31213                 child.setSize(b.width+adj[0], b.height+adj[1]);
31214             }
31215             // Second call here for IE
31216             // The first call enables instant resizing and
31217             // the second call corrects scroll bars if they
31218             // exist
31219             if(Roo.isIE){
31220                 setTimeout(function(){
31221                     if(el.dom.offsetWidth){
31222                         var b = el.getSize(true);
31223                         child.setSize(b.width+adj[0], b.height+adj[1]);
31224                     }
31225                 }, 10);
31226             }
31227         }
31228     },
31229
31230     // private
31231     snap : function(value, inc, min){
31232         if(!inc || !value) {
31233             return value;
31234         }
31235         var newValue = value;
31236         var m = value % inc;
31237         if(m > 0){
31238             if(m > (inc/2)){
31239                 newValue = value + (inc-m);
31240             }else{
31241                 newValue = value - m;
31242             }
31243         }
31244         return Math.max(min, newValue);
31245     },
31246
31247     // private
31248     resizeElement : function(){
31249         var box = this.proxy.getBox();
31250         if(this.updateBox){
31251             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
31252         }else{
31253             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
31254         }
31255         this.updateChildSize();
31256         if(!this.dynamic){
31257             this.proxy.hide();
31258         }
31259         return box;
31260     },
31261
31262     // private
31263     constrain : function(v, diff, m, mx){
31264         if(v - diff < m){
31265             diff = v - m;
31266         }else if(v - diff > mx){
31267             diff = mx - v;
31268         }
31269         return diff;
31270     },
31271
31272     // private
31273     onMouseMove : function(e){
31274         
31275         if(this.enabled){
31276             try{// try catch so if something goes wrong the user doesn't get hung
31277
31278             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
31279                 return;
31280             }
31281
31282             //var curXY = this.startPoint;
31283             var curSize = this.curSize || this.startBox;
31284             var x = this.startBox.x, y = this.startBox.y;
31285             var ox = x, oy = y;
31286             var w = curSize.width, h = curSize.height;
31287             var ow = w, oh = h;
31288             var mw = this.minWidth, mh = this.minHeight;
31289             var mxw = this.maxWidth, mxh = this.maxHeight;
31290             var wi = this.widthIncrement;
31291             var hi = this.heightIncrement;
31292
31293             var eventXY = e.getXY();
31294             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
31295             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
31296
31297             var pos = this.activeHandle.position;
31298
31299             switch(pos){
31300                 case "east":
31301                     w += diffX;
31302                     w = Math.min(Math.max(mw, w), mxw);
31303                     break;
31304              
31305                 case "south":
31306                     h += diffY;
31307                     h = Math.min(Math.max(mh, h), mxh);
31308                     break;
31309                 case "southeast":
31310                     w += diffX;
31311                     h += diffY;
31312                     w = Math.min(Math.max(mw, w), mxw);
31313                     h = Math.min(Math.max(mh, h), mxh);
31314                     break;
31315                 case "north":
31316                     diffY = this.constrain(h, diffY, mh, mxh);
31317                     y += diffY;
31318                     h -= diffY;
31319                     break;
31320                 case "hdrag":
31321                     
31322                     if (wi) {
31323                         var adiffX = Math.abs(diffX);
31324                         var sub = (adiffX % wi); // how much 
31325                         if (sub > (wi/2)) { // far enough to snap
31326                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
31327                         } else {
31328                             // remove difference.. 
31329                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
31330                         }
31331                     }
31332                     x += diffX;
31333                     x = Math.max(this.minX, x);
31334                     break;
31335                 case "west":
31336                     diffX = this.constrain(w, diffX, mw, mxw);
31337                     x += diffX;
31338                     w -= diffX;
31339                     break;
31340                 case "northeast":
31341                     w += diffX;
31342                     w = Math.min(Math.max(mw, w), mxw);
31343                     diffY = this.constrain(h, diffY, mh, mxh);
31344                     y += diffY;
31345                     h -= diffY;
31346                     break;
31347                 case "northwest":
31348                     diffX = this.constrain(w, diffX, mw, mxw);
31349                     diffY = this.constrain(h, diffY, mh, mxh);
31350                     y += diffY;
31351                     h -= diffY;
31352                     x += diffX;
31353                     w -= diffX;
31354                     break;
31355                case "southwest":
31356                     diffX = this.constrain(w, diffX, mw, mxw);
31357                     h += diffY;
31358                     h = Math.min(Math.max(mh, h), mxh);
31359                     x += diffX;
31360                     w -= diffX;
31361                     break;
31362             }
31363
31364             var sw = this.snap(w, wi, mw);
31365             var sh = this.snap(h, hi, mh);
31366             if(sw != w || sh != h){
31367                 switch(pos){
31368                     case "northeast":
31369                         y -= sh - h;
31370                     break;
31371                     case "north":
31372                         y -= sh - h;
31373                         break;
31374                     case "southwest":
31375                         x -= sw - w;
31376                     break;
31377                     case "west":
31378                         x -= sw - w;
31379                         break;
31380                     case "northwest":
31381                         x -= sw - w;
31382                         y -= sh - h;
31383                     break;
31384                 }
31385                 w = sw;
31386                 h = sh;
31387             }
31388
31389             if(this.preserveRatio){
31390                 switch(pos){
31391                     case "southeast":
31392                     case "east":
31393                         h = oh * (w/ow);
31394                         h = Math.min(Math.max(mh, h), mxh);
31395                         w = ow * (h/oh);
31396                        break;
31397                     case "south":
31398                         w = ow * (h/oh);
31399                         w = Math.min(Math.max(mw, w), mxw);
31400                         h = oh * (w/ow);
31401                         break;
31402                     case "northeast":
31403                         w = ow * (h/oh);
31404                         w = Math.min(Math.max(mw, w), mxw);
31405                         h = oh * (w/ow);
31406                     break;
31407                     case "north":
31408                         var tw = w;
31409                         w = ow * (h/oh);
31410                         w = Math.min(Math.max(mw, w), mxw);
31411                         h = oh * (w/ow);
31412                         x += (tw - w) / 2;
31413                         break;
31414                     case "southwest":
31415                         h = oh * (w/ow);
31416                         h = Math.min(Math.max(mh, h), mxh);
31417                         var tw = w;
31418                         w = ow * (h/oh);
31419                         x += tw - w;
31420                         break;
31421                     case "west":
31422                         var th = h;
31423                         h = oh * (w/ow);
31424                         h = Math.min(Math.max(mh, h), mxh);
31425                         y += (th - h) / 2;
31426                         var tw = w;
31427                         w = ow * (h/oh);
31428                         x += tw - w;
31429                        break;
31430                     case "northwest":
31431                         var tw = w;
31432                         var th = h;
31433                         h = oh * (w/ow);
31434                         h = Math.min(Math.max(mh, h), mxh);
31435                         w = ow * (h/oh);
31436                         y += th - h;
31437                         x += tw - w;
31438                        break;
31439
31440                 }
31441             }
31442             if (pos == 'hdrag') {
31443                 w = ow;
31444             }
31445             this.proxy.setBounds(x, y, w, h);
31446             if(this.dynamic){
31447                 this.resizeElement();
31448             }
31449             }catch(e){}
31450         }
31451         this.fireEvent("resizing", this, x, y, w, h, e);
31452     },
31453
31454     // private
31455     handleOver : function(){
31456         if(this.enabled){
31457             this.el.addClass("x-resizable-over");
31458         }
31459     },
31460
31461     // private
31462     handleOut : function(){
31463         if(!this.resizing){
31464             this.el.removeClass("x-resizable-over");
31465         }
31466     },
31467
31468     /**
31469      * Returns the element this component is bound to.
31470      * @return {Roo.Element}
31471      */
31472     getEl : function(){
31473         return this.el;
31474     },
31475
31476     /**
31477      * Returns the resizeChild element (or null).
31478      * @return {Roo.Element}
31479      */
31480     getResizeChild : function(){
31481         return this.resizeChild;
31482     },
31483     groupHandler : function()
31484     {
31485         
31486     },
31487     /**
31488      * Destroys this resizable. If the element was wrapped and
31489      * removeEl is not true then the element remains.
31490      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
31491      */
31492     destroy : function(removeEl){
31493         this.proxy.remove();
31494         if(this.overlay){
31495             this.overlay.removeAllListeners();
31496             this.overlay.remove();
31497         }
31498         var ps = Roo.Resizable.positions;
31499         for(var k in ps){
31500             if(typeof ps[k] != "function" && this[ps[k]]){
31501                 var h = this[ps[k]];
31502                 h.el.removeAllListeners();
31503                 h.el.remove();
31504             }
31505         }
31506         if(removeEl){
31507             this.el.update("");
31508             this.el.remove();
31509         }
31510     }
31511 });
31512
31513 // private
31514 // hash to map config positions to true positions
31515 Roo.Resizable.positions = {
31516     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
31517     hd: "hdrag"
31518 };
31519
31520 // private
31521 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
31522     if(!this.tpl){
31523         // only initialize the template if resizable is used
31524         var tpl = Roo.DomHelper.createTemplate(
31525             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
31526         );
31527         tpl.compile();
31528         Roo.Resizable.Handle.prototype.tpl = tpl;
31529     }
31530     this.position = pos;
31531     this.rz = rz;
31532     // show north drag fro topdra
31533     var handlepos = pos == 'hdrag' ? 'north' : pos;
31534     
31535     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
31536     if (pos == 'hdrag') {
31537         this.el.setStyle('cursor', 'pointer');
31538     }
31539     this.el.unselectable();
31540     if(transparent){
31541         this.el.setOpacity(0);
31542     }
31543     this.el.on("mousedown", this.onMouseDown, this);
31544     if(!disableTrackOver){
31545         this.el.on("mouseover", this.onMouseOver, this);
31546         this.el.on("mouseout", this.onMouseOut, this);
31547     }
31548 };
31549
31550 // private
31551 Roo.Resizable.Handle.prototype = {
31552     afterResize : function(rz){
31553         Roo.log('after?');
31554         // do nothing
31555     },
31556     // private
31557     onMouseDown : function(e){
31558         this.rz.onMouseDown(this, e);
31559     },
31560     // private
31561     onMouseOver : function(e){
31562         this.rz.handleOver(this, e);
31563     },
31564     // private
31565     onMouseOut : function(e){
31566         this.rz.handleOut(this, e);
31567     }
31568 };/*
31569  * Based on:
31570  * Ext JS Library 1.1.1
31571  * Copyright(c) 2006-2007, Ext JS, LLC.
31572  *
31573  * Originally Released Under LGPL - original licence link has changed is not relivant.
31574  *
31575  * Fork - LGPL
31576  * <script type="text/javascript">
31577  */
31578
31579 /**
31580  * @class Roo.Editor
31581  * @extends Roo.Component
31582  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
31583  * @constructor
31584  * Create a new Editor
31585  * @param {Roo.form.Field} field The Field object (or descendant)
31586  * @param {Object} config The config object
31587  */
31588 Roo.Editor = function(field, config){
31589     Roo.Editor.superclass.constructor.call(this, config);
31590     this.field = field;
31591     this.addEvents({
31592         /**
31593              * @event beforestartedit
31594              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
31595              * false from the handler of this event.
31596              * @param {Editor} this
31597              * @param {Roo.Element} boundEl The underlying element bound to this editor
31598              * @param {Mixed} value The field value being set
31599              */
31600         "beforestartedit" : true,
31601         /**
31602              * @event startedit
31603              * Fires when this editor is displayed
31604              * @param {Roo.Element} boundEl The underlying element bound to this editor
31605              * @param {Mixed} value The starting field value
31606              */
31607         "startedit" : true,
31608         /**
31609              * @event beforecomplete
31610              * Fires after a change has been made to the field, but before the change is reflected in the underlying
31611              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
31612              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
31613              * event will not fire since no edit actually occurred.
31614              * @param {Editor} this
31615              * @param {Mixed} value The current field value
31616              * @param {Mixed} startValue The original field value
31617              */
31618         "beforecomplete" : true,
31619         /**
31620              * @event complete
31621              * Fires after editing is complete and any changed value has been written to the underlying field.
31622              * @param {Editor} this
31623              * @param {Mixed} value The current field value
31624              * @param {Mixed} startValue The original field value
31625              */
31626         "complete" : true,
31627         /**
31628          * @event specialkey
31629          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
31630          * {@link Roo.EventObject#getKey} to determine which key was pressed.
31631          * @param {Roo.form.Field} this
31632          * @param {Roo.EventObject} e The event object
31633          */
31634         "specialkey" : true
31635     });
31636 };
31637
31638 Roo.extend(Roo.Editor, Roo.Component, {
31639     /**
31640      * @cfg {Boolean/String} autosize
31641      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
31642      * or "height" to adopt the height only (defaults to false)
31643      */
31644     /**
31645      * @cfg {Boolean} revertInvalid
31646      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
31647      * validation fails (defaults to true)
31648      */
31649     /**
31650      * @cfg {Boolean} ignoreNoChange
31651      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
31652      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
31653      * will never be ignored.
31654      */
31655     /**
31656      * @cfg {Boolean} hideEl
31657      * False to keep the bound element visible while the editor is displayed (defaults to true)
31658      */
31659     /**
31660      * @cfg {Mixed} value
31661      * The data value of the underlying field (defaults to "")
31662      */
31663     value : "",
31664     /**
31665      * @cfg {String} alignment
31666      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
31667      */
31668     alignment: "c-c?",
31669     /**
31670      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
31671      * for bottom-right shadow (defaults to "frame")
31672      */
31673     shadow : "frame",
31674     /**
31675      * @cfg {Boolean} constrain True to constrain the editor to the viewport
31676      */
31677     constrain : false,
31678     /**
31679      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
31680      */
31681     completeOnEnter : false,
31682     /**
31683      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
31684      */
31685     cancelOnEsc : false,
31686     /**
31687      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
31688      */
31689     updateEl : false,
31690
31691     // private
31692     onRender : function(ct, position){
31693         this.el = new Roo.Layer({
31694             shadow: this.shadow,
31695             cls: "x-editor",
31696             parentEl : ct,
31697             shim : this.shim,
31698             shadowOffset:4,
31699             id: this.id,
31700             constrain: this.constrain
31701         });
31702         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
31703         if(this.field.msgTarget != 'title'){
31704             this.field.msgTarget = 'qtip';
31705         }
31706         this.field.render(this.el);
31707         if(Roo.isGecko){
31708             this.field.el.dom.setAttribute('autocomplete', 'off');
31709         }
31710         this.field.on("specialkey", this.onSpecialKey, this);
31711         if(this.swallowKeys){
31712             this.field.el.swallowEvent(['keydown','keypress']);
31713         }
31714         this.field.show();
31715         this.field.on("blur", this.onBlur, this);
31716         if(this.field.grow){
31717             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
31718         }
31719     },
31720
31721     onSpecialKey : function(field, e)
31722     {
31723         //Roo.log('editor onSpecialKey');
31724         if(this.completeOnEnter && e.getKey() == e.ENTER){
31725             e.stopEvent();
31726             this.completeEdit();
31727             return;
31728         }
31729         // do not fire special key otherwise it might hide close the editor...
31730         if(e.getKey() == e.ENTER){    
31731             return;
31732         }
31733         if(this.cancelOnEsc && e.getKey() == e.ESC){
31734             this.cancelEdit();
31735             return;
31736         } 
31737         this.fireEvent('specialkey', field, e);
31738     
31739     },
31740
31741     /**
31742      * Starts the editing process and shows the editor.
31743      * @param {String/HTMLElement/Element} el The element to edit
31744      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
31745       * to the innerHTML of el.
31746      */
31747     startEdit : function(el, value){
31748         if(this.editing){
31749             this.completeEdit();
31750         }
31751         this.boundEl = Roo.get(el);
31752         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
31753         if(!this.rendered){
31754             this.render(this.parentEl || document.body);
31755         }
31756         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
31757             return;
31758         }
31759         this.startValue = v;
31760         this.field.setValue(v);
31761         if(this.autoSize){
31762             var sz = this.boundEl.getSize();
31763             switch(this.autoSize){
31764                 case "width":
31765                 this.setSize(sz.width,  "");
31766                 break;
31767                 case "height":
31768                 this.setSize("",  sz.height);
31769                 break;
31770                 default:
31771                 this.setSize(sz.width,  sz.height);
31772             }
31773         }
31774         this.el.alignTo(this.boundEl, this.alignment);
31775         this.editing = true;
31776         if(Roo.QuickTips){
31777             Roo.QuickTips.disable();
31778         }
31779         this.show();
31780     },
31781
31782     /**
31783      * Sets the height and width of this editor.
31784      * @param {Number} width The new width
31785      * @param {Number} height The new height
31786      */
31787     setSize : function(w, h){
31788         this.field.setSize(w, h);
31789         if(this.el){
31790             this.el.sync();
31791         }
31792     },
31793
31794     /**
31795      * Realigns the editor to the bound field based on the current alignment config value.
31796      */
31797     realign : function(){
31798         this.el.alignTo(this.boundEl, this.alignment);
31799     },
31800
31801     /**
31802      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
31803      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
31804      */
31805     completeEdit : function(remainVisible){
31806         if(!this.editing){
31807             return;
31808         }
31809         var v = this.getValue();
31810         if(this.revertInvalid !== false && !this.field.isValid()){
31811             v = this.startValue;
31812             this.cancelEdit(true);
31813         }
31814         if(String(v) === String(this.startValue) && this.ignoreNoChange){
31815             this.editing = false;
31816             this.hide();
31817             return;
31818         }
31819         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
31820             this.editing = false;
31821             if(this.updateEl && this.boundEl){
31822                 this.boundEl.update(v);
31823             }
31824             if(remainVisible !== true){
31825                 this.hide();
31826             }
31827             this.fireEvent("complete", this, v, this.startValue);
31828         }
31829     },
31830
31831     // private
31832     onShow : function(){
31833         this.el.show();
31834         if(this.hideEl !== false){
31835             this.boundEl.hide();
31836         }
31837         this.field.show();
31838         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
31839             this.fixIEFocus = true;
31840             this.deferredFocus.defer(50, this);
31841         }else{
31842             this.field.focus();
31843         }
31844         this.fireEvent("startedit", this.boundEl, this.startValue);
31845     },
31846
31847     deferredFocus : function(){
31848         if(this.editing){
31849             this.field.focus();
31850         }
31851     },
31852
31853     /**
31854      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
31855      * reverted to the original starting value.
31856      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
31857      * cancel (defaults to false)
31858      */
31859     cancelEdit : function(remainVisible){
31860         if(this.editing){
31861             this.setValue(this.startValue);
31862             if(remainVisible !== true){
31863                 this.hide();
31864             }
31865         }
31866     },
31867
31868     // private
31869     onBlur : function(){
31870         if(this.allowBlur !== true && this.editing){
31871             this.completeEdit();
31872         }
31873     },
31874
31875     // private
31876     onHide : function(){
31877         if(this.editing){
31878             this.completeEdit();
31879             return;
31880         }
31881         this.field.blur();
31882         if(this.field.collapse){
31883             this.field.collapse();
31884         }
31885         this.el.hide();
31886         if(this.hideEl !== false){
31887             this.boundEl.show();
31888         }
31889         if(Roo.QuickTips){
31890             Roo.QuickTips.enable();
31891         }
31892     },
31893
31894     /**
31895      * Sets the data value of the editor
31896      * @param {Mixed} value Any valid value supported by the underlying field
31897      */
31898     setValue : function(v){
31899         this.field.setValue(v);
31900     },
31901
31902     /**
31903      * Gets the data value of the editor
31904      * @return {Mixed} The data value
31905      */
31906     getValue : function(){
31907         return this.field.getValue();
31908     }
31909 });/*
31910  * Based on:
31911  * Ext JS Library 1.1.1
31912  * Copyright(c) 2006-2007, Ext JS, LLC.
31913  *
31914  * Originally Released Under LGPL - original licence link has changed is not relivant.
31915  *
31916  * Fork - LGPL
31917  * <script type="text/javascript">
31918  */
31919  
31920 /**
31921  * @class Roo.BasicDialog
31922  * @extends Roo.util.Observable
31923  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
31924  * <pre><code>
31925 var dlg = new Roo.BasicDialog("my-dlg", {
31926     height: 200,
31927     width: 300,
31928     minHeight: 100,
31929     minWidth: 150,
31930     modal: true,
31931     proxyDrag: true,
31932     shadow: true
31933 });
31934 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
31935 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
31936 dlg.addButton('Cancel', dlg.hide, dlg);
31937 dlg.show();
31938 </code></pre>
31939   <b>A Dialog should always be a direct child of the body element.</b>
31940  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
31941  * @cfg {String} title Default text to display in the title bar (defaults to null)
31942  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
31943  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
31944  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
31945  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
31946  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
31947  * (defaults to null with no animation)
31948  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
31949  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
31950  * property for valid values (defaults to 'all')
31951  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
31952  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
31953  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
31954  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
31955  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
31956  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
31957  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
31958  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
31959  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
31960  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
31961  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
31962  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
31963  * draggable = true (defaults to false)
31964  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
31965  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
31966  * shadow (defaults to false)
31967  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
31968  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
31969  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
31970  * @cfg {Array} buttons Array of buttons
31971  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
31972  * @constructor
31973  * Create a new BasicDialog.
31974  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
31975  * @param {Object} config Configuration options
31976  */
31977 Roo.BasicDialog = function(el, config){
31978     this.el = Roo.get(el);
31979     var dh = Roo.DomHelper;
31980     if(!this.el && config && config.autoCreate){
31981         if(typeof config.autoCreate == "object"){
31982             if(!config.autoCreate.id){
31983                 config.autoCreate.id = el;
31984             }
31985             this.el = dh.append(document.body,
31986                         config.autoCreate, true);
31987         }else{
31988             this.el = dh.append(document.body,
31989                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
31990         }
31991     }
31992     el = this.el;
31993     el.setDisplayed(true);
31994     el.hide = this.hideAction;
31995     this.id = el.id;
31996     el.addClass("x-dlg");
31997
31998     Roo.apply(this, config);
31999
32000     this.proxy = el.createProxy("x-dlg-proxy");
32001     this.proxy.hide = this.hideAction;
32002     this.proxy.setOpacity(.5);
32003     this.proxy.hide();
32004
32005     if(config.width){
32006         el.setWidth(config.width);
32007     }
32008     if(config.height){
32009         el.setHeight(config.height);
32010     }
32011     this.size = el.getSize();
32012     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
32013         this.xy = [config.x,config.y];
32014     }else{
32015         this.xy = el.getCenterXY(true);
32016     }
32017     /** The header element @type Roo.Element */
32018     this.header = el.child("> .x-dlg-hd");
32019     /** The body element @type Roo.Element */
32020     this.body = el.child("> .x-dlg-bd");
32021     /** The footer element @type Roo.Element */
32022     this.footer = el.child("> .x-dlg-ft");
32023
32024     if(!this.header){
32025         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
32026     }
32027     if(!this.body){
32028         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
32029     }
32030
32031     this.header.unselectable();
32032     if(this.title){
32033         this.header.update(this.title);
32034     }
32035     // this element allows the dialog to be focused for keyboard event
32036     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
32037     this.focusEl.swallowEvent("click", true);
32038
32039     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
32040
32041     // wrap the body and footer for special rendering
32042     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
32043     if(this.footer){
32044         this.bwrap.dom.appendChild(this.footer.dom);
32045     }
32046
32047     this.bg = this.el.createChild({
32048         tag: "div", cls:"x-dlg-bg",
32049         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
32050     });
32051     this.centerBg = this.bg.child("div.x-dlg-bg-center");
32052
32053
32054     if(this.autoScroll !== false && !this.autoTabs){
32055         this.body.setStyle("overflow", "auto");
32056     }
32057
32058     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
32059
32060     if(this.closable !== false){
32061         this.el.addClass("x-dlg-closable");
32062         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
32063         this.close.on("click", this.closeClick, this);
32064         this.close.addClassOnOver("x-dlg-close-over");
32065     }
32066     if(this.collapsible !== false){
32067         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
32068         this.collapseBtn.on("click", this.collapseClick, this);
32069         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
32070         this.header.on("dblclick", this.collapseClick, this);
32071     }
32072     if(this.resizable !== false){
32073         this.el.addClass("x-dlg-resizable");
32074         this.resizer = new Roo.Resizable(el, {
32075             minWidth: this.minWidth || 80,
32076             minHeight:this.minHeight || 80,
32077             handles: this.resizeHandles || "all",
32078             pinned: true
32079         });
32080         this.resizer.on("beforeresize", this.beforeResize, this);
32081         this.resizer.on("resize", this.onResize, this);
32082     }
32083     if(this.draggable !== false){
32084         el.addClass("x-dlg-draggable");
32085         if (!this.proxyDrag) {
32086             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
32087         }
32088         else {
32089             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
32090         }
32091         dd.setHandleElId(this.header.id);
32092         dd.endDrag = this.endMove.createDelegate(this);
32093         dd.startDrag = this.startMove.createDelegate(this);
32094         dd.onDrag = this.onDrag.createDelegate(this);
32095         dd.scroll = false;
32096         this.dd = dd;
32097     }
32098     if(this.modal){
32099         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
32100         this.mask.enableDisplayMode("block");
32101         this.mask.hide();
32102         this.el.addClass("x-dlg-modal");
32103     }
32104     if(this.shadow){
32105         this.shadow = new Roo.Shadow({
32106             mode : typeof this.shadow == "string" ? this.shadow : "sides",
32107             offset : this.shadowOffset
32108         });
32109     }else{
32110         this.shadowOffset = 0;
32111     }
32112     if(Roo.useShims && this.shim !== false){
32113         this.shim = this.el.createShim();
32114         this.shim.hide = this.hideAction;
32115         this.shim.hide();
32116     }else{
32117         this.shim = false;
32118     }
32119     if(this.autoTabs){
32120         this.initTabs();
32121     }
32122     if (this.buttons) { 
32123         var bts= this.buttons;
32124         this.buttons = [];
32125         Roo.each(bts, function(b) {
32126             this.addButton(b);
32127         }, this);
32128     }
32129     
32130     
32131     this.addEvents({
32132         /**
32133          * @event keydown
32134          * Fires when a key is pressed
32135          * @param {Roo.BasicDialog} this
32136          * @param {Roo.EventObject} e
32137          */
32138         "keydown" : true,
32139         /**
32140          * @event move
32141          * Fires when this dialog is moved by the user.
32142          * @param {Roo.BasicDialog} this
32143          * @param {Number} x The new page X
32144          * @param {Number} y The new page Y
32145          */
32146         "move" : true,
32147         /**
32148          * @event resize
32149          * Fires when this dialog is resized by the user.
32150          * @param {Roo.BasicDialog} this
32151          * @param {Number} width The new width
32152          * @param {Number} height The new height
32153          */
32154         "resize" : true,
32155         /**
32156          * @event beforehide
32157          * Fires before this dialog is hidden.
32158          * @param {Roo.BasicDialog} this
32159          */
32160         "beforehide" : true,
32161         /**
32162          * @event hide
32163          * Fires when this dialog is hidden.
32164          * @param {Roo.BasicDialog} this
32165          */
32166         "hide" : true,
32167         /**
32168          * @event beforeshow
32169          * Fires before this dialog is shown.
32170          * @param {Roo.BasicDialog} this
32171          */
32172         "beforeshow" : true,
32173         /**
32174          * @event show
32175          * Fires when this dialog is shown.
32176          * @param {Roo.BasicDialog} this
32177          */
32178         "show" : true
32179     });
32180     el.on("keydown", this.onKeyDown, this);
32181     el.on("mousedown", this.toFront, this);
32182     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
32183     this.el.hide();
32184     Roo.DialogManager.register(this);
32185     Roo.BasicDialog.superclass.constructor.call(this);
32186 };
32187
32188 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
32189     shadowOffset: Roo.isIE ? 6 : 5,
32190     minHeight: 80,
32191     minWidth: 200,
32192     minButtonWidth: 75,
32193     defaultButton: null,
32194     buttonAlign: "right",
32195     tabTag: 'div',
32196     firstShow: true,
32197
32198     /**
32199      * Sets the dialog title text
32200      * @param {String} text The title text to display
32201      * @return {Roo.BasicDialog} this
32202      */
32203     setTitle : function(text){
32204         this.header.update(text);
32205         return this;
32206     },
32207
32208     // private
32209     closeClick : function(){
32210         this.hide();
32211     },
32212
32213     // private
32214     collapseClick : function(){
32215         this[this.collapsed ? "expand" : "collapse"]();
32216     },
32217
32218     /**
32219      * Collapses the dialog to its minimized state (only the title bar is visible).
32220      * Equivalent to the user clicking the collapse dialog button.
32221      */
32222     collapse : function(){
32223         if(!this.collapsed){
32224             this.collapsed = true;
32225             this.el.addClass("x-dlg-collapsed");
32226             this.restoreHeight = this.el.getHeight();
32227             this.resizeTo(this.el.getWidth(), this.header.getHeight());
32228         }
32229     },
32230
32231     /**
32232      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
32233      * clicking the expand dialog button.
32234      */
32235     expand : function(){
32236         if(this.collapsed){
32237             this.collapsed = false;
32238             this.el.removeClass("x-dlg-collapsed");
32239             this.resizeTo(this.el.getWidth(), this.restoreHeight);
32240         }
32241     },
32242
32243     /**
32244      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
32245      * @return {Roo.TabPanel} The tabs component
32246      */
32247     initTabs : function(){
32248         var tabs = this.getTabs();
32249         while(tabs.getTab(0)){
32250             tabs.removeTab(0);
32251         }
32252         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
32253             var dom = el.dom;
32254             tabs.addTab(Roo.id(dom), dom.title);
32255             dom.title = "";
32256         });
32257         tabs.activate(0);
32258         return tabs;
32259     },
32260
32261     // private
32262     beforeResize : function(){
32263         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
32264     },
32265
32266     // private
32267     onResize : function(){
32268         this.refreshSize();
32269         this.syncBodyHeight();
32270         this.adjustAssets();
32271         this.focus();
32272         this.fireEvent("resize", this, this.size.width, this.size.height);
32273     },
32274
32275     // private
32276     onKeyDown : function(e){
32277         if(this.isVisible()){
32278             this.fireEvent("keydown", this, e);
32279         }
32280     },
32281
32282     /**
32283      * Resizes the dialog.
32284      * @param {Number} width
32285      * @param {Number} height
32286      * @return {Roo.BasicDialog} this
32287      */
32288     resizeTo : function(width, height){
32289         this.el.setSize(width, height);
32290         this.size = {width: width, height: height};
32291         this.syncBodyHeight();
32292         if(this.fixedcenter){
32293             this.center();
32294         }
32295         if(this.isVisible()){
32296             this.constrainXY();
32297             this.adjustAssets();
32298         }
32299         this.fireEvent("resize", this, width, height);
32300         return this;
32301     },
32302
32303
32304     /**
32305      * Resizes the dialog to fit the specified content size.
32306      * @param {Number} width
32307      * @param {Number} height
32308      * @return {Roo.BasicDialog} this
32309      */
32310     setContentSize : function(w, h){
32311         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
32312         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
32313         //if(!this.el.isBorderBox()){
32314             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
32315             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
32316         //}
32317         if(this.tabs){
32318             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
32319             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
32320         }
32321         this.resizeTo(w, h);
32322         return this;
32323     },
32324
32325     /**
32326      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
32327      * executed in response to a particular key being pressed while the dialog is active.
32328      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
32329      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
32330      * @param {Function} fn The function to call
32331      * @param {Object} scope (optional) The scope of the function
32332      * @return {Roo.BasicDialog} this
32333      */
32334     addKeyListener : function(key, fn, scope){
32335         var keyCode, shift, ctrl, alt;
32336         if(typeof key == "object" && !(key instanceof Array)){
32337             keyCode = key["key"];
32338             shift = key["shift"];
32339             ctrl = key["ctrl"];
32340             alt = key["alt"];
32341         }else{
32342             keyCode = key;
32343         }
32344         var handler = function(dlg, e){
32345             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
32346                 var k = e.getKey();
32347                 if(keyCode instanceof Array){
32348                     for(var i = 0, len = keyCode.length; i < len; i++){
32349                         if(keyCode[i] == k){
32350                           fn.call(scope || window, dlg, k, e);
32351                           return;
32352                         }
32353                     }
32354                 }else{
32355                     if(k == keyCode){
32356                         fn.call(scope || window, dlg, k, e);
32357                     }
32358                 }
32359             }
32360         };
32361         this.on("keydown", handler);
32362         return this;
32363     },
32364
32365     /**
32366      * Returns the TabPanel component (creates it if it doesn't exist).
32367      * Note: If you wish to simply check for the existence of tabs without creating them,
32368      * check for a null 'tabs' property.
32369      * @return {Roo.TabPanel} The tabs component
32370      */
32371     getTabs : function(){
32372         if(!this.tabs){
32373             this.el.addClass("x-dlg-auto-tabs");
32374             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
32375             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
32376         }
32377         return this.tabs;
32378     },
32379
32380     /**
32381      * Adds a button to the footer section of the dialog.
32382      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
32383      * object or a valid Roo.DomHelper element config
32384      * @param {Function} handler The function called when the button is clicked
32385      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
32386      * @return {Roo.Button} The new button
32387      */
32388     addButton : function(config, handler, scope){
32389         var dh = Roo.DomHelper;
32390         if(!this.footer){
32391             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
32392         }
32393         if(!this.btnContainer){
32394             var tb = this.footer.createChild({
32395
32396                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
32397                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
32398             }, null, true);
32399             this.btnContainer = tb.firstChild.firstChild.firstChild;
32400         }
32401         var bconfig = {
32402             handler: handler,
32403             scope: scope,
32404             minWidth: this.minButtonWidth,
32405             hideParent:true
32406         };
32407         if(typeof config == "string"){
32408             bconfig.text = config;
32409         }else{
32410             if(config.tag){
32411                 bconfig.dhconfig = config;
32412             }else{
32413                 Roo.apply(bconfig, config);
32414             }
32415         }
32416         var fc = false;
32417         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
32418             bconfig.position = Math.max(0, bconfig.position);
32419             fc = this.btnContainer.childNodes[bconfig.position];
32420         }
32421          
32422         var btn = new Roo.Button(
32423             fc ? 
32424                 this.btnContainer.insertBefore(document.createElement("td"),fc)
32425                 : this.btnContainer.appendChild(document.createElement("td")),
32426             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
32427             bconfig
32428         );
32429         this.syncBodyHeight();
32430         if(!this.buttons){
32431             /**
32432              * Array of all the buttons that have been added to this dialog via addButton
32433              * @type Array
32434              */
32435             this.buttons = [];
32436         }
32437         this.buttons.push(btn);
32438         return btn;
32439     },
32440
32441     /**
32442      * Sets the default button to be focused when the dialog is displayed.
32443      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
32444      * @return {Roo.BasicDialog} this
32445      */
32446     setDefaultButton : function(btn){
32447         this.defaultButton = btn;
32448         return this;
32449     },
32450
32451     // private
32452     getHeaderFooterHeight : function(safe){
32453         var height = 0;
32454         if(this.header){
32455            height += this.header.getHeight();
32456         }
32457         if(this.footer){
32458            var fm = this.footer.getMargins();
32459             height += (this.footer.getHeight()+fm.top+fm.bottom);
32460         }
32461         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
32462         height += this.centerBg.getPadding("tb");
32463         return height;
32464     },
32465
32466     // private
32467     syncBodyHeight : function()
32468     {
32469         var bd = this.body, // the text
32470             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
32471             bw = this.bwrap;
32472         var height = this.size.height - this.getHeaderFooterHeight(false);
32473         bd.setHeight(height-bd.getMargins("tb"));
32474         var hh = this.header.getHeight();
32475         var h = this.size.height-hh;
32476         cb.setHeight(h);
32477         
32478         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
32479         bw.setHeight(h-cb.getPadding("tb"));
32480         
32481         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
32482         bd.setWidth(bw.getWidth(true));
32483         if(this.tabs){
32484             this.tabs.syncHeight();
32485             if(Roo.isIE){
32486                 this.tabs.el.repaint();
32487             }
32488         }
32489     },
32490
32491     /**
32492      * Restores the previous state of the dialog if Roo.state is configured.
32493      * @return {Roo.BasicDialog} this
32494      */
32495     restoreState : function(){
32496         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
32497         if(box && box.width){
32498             this.xy = [box.x, box.y];
32499             this.resizeTo(box.width, box.height);
32500         }
32501         return this;
32502     },
32503
32504     // private
32505     beforeShow : function(){
32506         this.expand();
32507         if(this.fixedcenter){
32508             this.xy = this.el.getCenterXY(true);
32509         }
32510         if(this.modal){
32511             Roo.get(document.body).addClass("x-body-masked");
32512             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32513             this.mask.show();
32514         }
32515         this.constrainXY();
32516     },
32517
32518     // private
32519     animShow : function(){
32520         var b = Roo.get(this.animateTarget).getBox();
32521         this.proxy.setSize(b.width, b.height);
32522         this.proxy.setLocation(b.x, b.y);
32523         this.proxy.show();
32524         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
32525                     true, .35, this.showEl.createDelegate(this));
32526     },
32527
32528     /**
32529      * Shows the dialog.
32530      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
32531      * @return {Roo.BasicDialog} this
32532      */
32533     show : function(animateTarget){
32534         if (this.fireEvent("beforeshow", this) === false){
32535             return;
32536         }
32537         if(this.syncHeightBeforeShow){
32538             this.syncBodyHeight();
32539         }else if(this.firstShow){
32540             this.firstShow = false;
32541             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
32542         }
32543         this.animateTarget = animateTarget || this.animateTarget;
32544         if(!this.el.isVisible()){
32545             this.beforeShow();
32546             if(this.animateTarget && Roo.get(this.animateTarget)){
32547                 this.animShow();
32548             }else{
32549                 this.showEl();
32550             }
32551         }
32552         return this;
32553     },
32554
32555     // private
32556     showEl : function(){
32557         this.proxy.hide();
32558         this.el.setXY(this.xy);
32559         this.el.show();
32560         this.adjustAssets(true);
32561         this.toFront();
32562         this.focus();
32563         // IE peekaboo bug - fix found by Dave Fenwick
32564         if(Roo.isIE){
32565             this.el.repaint();
32566         }
32567         this.fireEvent("show", this);
32568     },
32569
32570     /**
32571      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
32572      * dialog itself will receive focus.
32573      */
32574     focus : function(){
32575         if(this.defaultButton){
32576             this.defaultButton.focus();
32577         }else{
32578             this.focusEl.focus();
32579         }
32580     },
32581
32582     // private
32583     constrainXY : function(){
32584         if(this.constraintoviewport !== false){
32585             if(!this.viewSize){
32586                 if(this.container){
32587                     var s = this.container.getSize();
32588                     this.viewSize = [s.width, s.height];
32589                 }else{
32590                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
32591                 }
32592             }
32593             var s = Roo.get(this.container||document).getScroll();
32594
32595             var x = this.xy[0], y = this.xy[1];
32596             var w = this.size.width, h = this.size.height;
32597             var vw = this.viewSize[0], vh = this.viewSize[1];
32598             // only move it if it needs it
32599             var moved = false;
32600             // first validate right/bottom
32601             if(x + w > vw+s.left){
32602                 x = vw - w;
32603                 moved = true;
32604             }
32605             if(y + h > vh+s.top){
32606                 y = vh - h;
32607                 moved = true;
32608             }
32609             // then make sure top/left isn't negative
32610             if(x < s.left){
32611                 x = s.left;
32612                 moved = true;
32613             }
32614             if(y < s.top){
32615                 y = s.top;
32616                 moved = true;
32617             }
32618             if(moved){
32619                 // cache xy
32620                 this.xy = [x, y];
32621                 if(this.isVisible()){
32622                     this.el.setLocation(x, y);
32623                     this.adjustAssets();
32624                 }
32625             }
32626         }
32627     },
32628
32629     // private
32630     onDrag : function(){
32631         if(!this.proxyDrag){
32632             this.xy = this.el.getXY();
32633             this.adjustAssets();
32634         }
32635     },
32636
32637     // private
32638     adjustAssets : function(doShow){
32639         var x = this.xy[0], y = this.xy[1];
32640         var w = this.size.width, h = this.size.height;
32641         if(doShow === true){
32642             if(this.shadow){
32643                 this.shadow.show(this.el);
32644             }
32645             if(this.shim){
32646                 this.shim.show();
32647             }
32648         }
32649         if(this.shadow && this.shadow.isVisible()){
32650             this.shadow.show(this.el);
32651         }
32652         if(this.shim && this.shim.isVisible()){
32653             this.shim.setBounds(x, y, w, h);
32654         }
32655     },
32656
32657     // private
32658     adjustViewport : function(w, h){
32659         if(!w || !h){
32660             w = Roo.lib.Dom.getViewWidth();
32661             h = Roo.lib.Dom.getViewHeight();
32662         }
32663         // cache the size
32664         this.viewSize = [w, h];
32665         if(this.modal && this.mask.isVisible()){
32666             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
32667             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32668         }
32669         if(this.isVisible()){
32670             this.constrainXY();
32671         }
32672     },
32673
32674     /**
32675      * Destroys this dialog and all its supporting elements (including any tabs, shim,
32676      * shadow, proxy, mask, etc.)  Also removes all event listeners.
32677      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
32678      */
32679     destroy : function(removeEl){
32680         if(this.isVisible()){
32681             this.animateTarget = null;
32682             this.hide();
32683         }
32684         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
32685         if(this.tabs){
32686             this.tabs.destroy(removeEl);
32687         }
32688         Roo.destroy(
32689              this.shim,
32690              this.proxy,
32691              this.resizer,
32692              this.close,
32693              this.mask
32694         );
32695         if(this.dd){
32696             this.dd.unreg();
32697         }
32698         if(this.buttons){
32699            for(var i = 0, len = this.buttons.length; i < len; i++){
32700                this.buttons[i].destroy();
32701            }
32702         }
32703         this.el.removeAllListeners();
32704         if(removeEl === true){
32705             this.el.update("");
32706             this.el.remove();
32707         }
32708         Roo.DialogManager.unregister(this);
32709     },
32710
32711     // private
32712     startMove : function(){
32713         if(this.proxyDrag){
32714             this.proxy.show();
32715         }
32716         if(this.constraintoviewport !== false){
32717             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
32718         }
32719     },
32720
32721     // private
32722     endMove : function(){
32723         if(!this.proxyDrag){
32724             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
32725         }else{
32726             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
32727             this.proxy.hide();
32728         }
32729         this.refreshSize();
32730         this.adjustAssets();
32731         this.focus();
32732         this.fireEvent("move", this, this.xy[0], this.xy[1]);
32733     },
32734
32735     /**
32736      * Brings this dialog to the front of any other visible dialogs
32737      * @return {Roo.BasicDialog} this
32738      */
32739     toFront : function(){
32740         Roo.DialogManager.bringToFront(this);
32741         return this;
32742     },
32743
32744     /**
32745      * Sends this dialog to the back (under) of any other visible dialogs
32746      * @return {Roo.BasicDialog} this
32747      */
32748     toBack : function(){
32749         Roo.DialogManager.sendToBack(this);
32750         return this;
32751     },
32752
32753     /**
32754      * Centers this dialog in the viewport
32755      * @return {Roo.BasicDialog} this
32756      */
32757     center : function(){
32758         var xy = this.el.getCenterXY(true);
32759         this.moveTo(xy[0], xy[1]);
32760         return this;
32761     },
32762
32763     /**
32764      * Moves the dialog's top-left corner to the specified point
32765      * @param {Number} x
32766      * @param {Number} y
32767      * @return {Roo.BasicDialog} this
32768      */
32769     moveTo : function(x, y){
32770         this.xy = [x,y];
32771         if(this.isVisible()){
32772             this.el.setXY(this.xy);
32773             this.adjustAssets();
32774         }
32775         return this;
32776     },
32777
32778     /**
32779      * Aligns the dialog to the specified element
32780      * @param {String/HTMLElement/Roo.Element} element The element to align to.
32781      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
32782      * @param {Array} offsets (optional) Offset the positioning by [x, y]
32783      * @return {Roo.BasicDialog} this
32784      */
32785     alignTo : function(element, position, offsets){
32786         this.xy = this.el.getAlignToXY(element, position, offsets);
32787         if(this.isVisible()){
32788             this.el.setXY(this.xy);
32789             this.adjustAssets();
32790         }
32791         return this;
32792     },
32793
32794     /**
32795      * Anchors an element to another element and realigns it when the window is resized.
32796      * @param {String/HTMLElement/Roo.Element} element The element to align to.
32797      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
32798      * @param {Array} offsets (optional) Offset the positioning by [x, y]
32799      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
32800      * is a number, it is used as the buffer delay (defaults to 50ms).
32801      * @return {Roo.BasicDialog} this
32802      */
32803     anchorTo : function(el, alignment, offsets, monitorScroll){
32804         var action = function(){
32805             this.alignTo(el, alignment, offsets);
32806         };
32807         Roo.EventManager.onWindowResize(action, this);
32808         var tm = typeof monitorScroll;
32809         if(tm != 'undefined'){
32810             Roo.EventManager.on(window, 'scroll', action, this,
32811                 {buffer: tm == 'number' ? monitorScroll : 50});
32812         }
32813         action.call(this);
32814         return this;
32815     },
32816
32817     /**
32818      * Returns true if the dialog is visible
32819      * @return {Boolean}
32820      */
32821     isVisible : function(){
32822         return this.el.isVisible();
32823     },
32824
32825     // private
32826     animHide : function(callback){
32827         var b = Roo.get(this.animateTarget).getBox();
32828         this.proxy.show();
32829         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
32830         this.el.hide();
32831         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
32832                     this.hideEl.createDelegate(this, [callback]));
32833     },
32834
32835     /**
32836      * Hides the dialog.
32837      * @param {Function} callback (optional) Function to call when the dialog is hidden
32838      * @return {Roo.BasicDialog} this
32839      */
32840     hide : function(callback){
32841         if (this.fireEvent("beforehide", this) === false){
32842             return;
32843         }
32844         if(this.shadow){
32845             this.shadow.hide();
32846         }
32847         if(this.shim) {
32848           this.shim.hide();
32849         }
32850         // sometimes animateTarget seems to get set.. causing problems...
32851         // this just double checks..
32852         if(this.animateTarget && Roo.get(this.animateTarget)) {
32853            this.animHide(callback);
32854         }else{
32855             this.el.hide();
32856             this.hideEl(callback);
32857         }
32858         return this;
32859     },
32860
32861     // private
32862     hideEl : function(callback){
32863         this.proxy.hide();
32864         if(this.modal){
32865             this.mask.hide();
32866             Roo.get(document.body).removeClass("x-body-masked");
32867         }
32868         this.fireEvent("hide", this);
32869         if(typeof callback == "function"){
32870             callback();
32871         }
32872     },
32873
32874     // private
32875     hideAction : function(){
32876         this.setLeft("-10000px");
32877         this.setTop("-10000px");
32878         this.setStyle("visibility", "hidden");
32879     },
32880
32881     // private
32882     refreshSize : function(){
32883         this.size = this.el.getSize();
32884         this.xy = this.el.getXY();
32885         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
32886     },
32887
32888     // private
32889     // z-index is managed by the DialogManager and may be overwritten at any time
32890     setZIndex : function(index){
32891         if(this.modal){
32892             this.mask.setStyle("z-index", index);
32893         }
32894         if(this.shim){
32895             this.shim.setStyle("z-index", ++index);
32896         }
32897         if(this.shadow){
32898             this.shadow.setZIndex(++index);
32899         }
32900         this.el.setStyle("z-index", ++index);
32901         if(this.proxy){
32902             this.proxy.setStyle("z-index", ++index);
32903         }
32904         if(this.resizer){
32905             this.resizer.proxy.setStyle("z-index", ++index);
32906         }
32907
32908         this.lastZIndex = index;
32909     },
32910
32911     /**
32912      * Returns the element for this dialog
32913      * @return {Roo.Element} The underlying dialog Element
32914      */
32915     getEl : function(){
32916         return this.el;
32917     }
32918 });
32919
32920 /**
32921  * @class Roo.DialogManager
32922  * Provides global access to BasicDialogs that have been created and
32923  * support for z-indexing (layering) multiple open dialogs.
32924  */
32925 Roo.DialogManager = function(){
32926     var list = {};
32927     var accessList = [];
32928     var front = null;
32929
32930     // private
32931     var sortDialogs = function(d1, d2){
32932         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
32933     };
32934
32935     // private
32936     var orderDialogs = function(){
32937         accessList.sort(sortDialogs);
32938         var seed = Roo.DialogManager.zseed;
32939         for(var i = 0, len = accessList.length; i < len; i++){
32940             var dlg = accessList[i];
32941             if(dlg){
32942                 dlg.setZIndex(seed + (i*10));
32943             }
32944         }
32945     };
32946
32947     return {
32948         /**
32949          * The starting z-index for BasicDialogs (defaults to 9000)
32950          * @type Number The z-index value
32951          */
32952         zseed : 9000,
32953
32954         // private
32955         register : function(dlg){
32956             list[dlg.id] = dlg;
32957             accessList.push(dlg);
32958         },
32959
32960         // private
32961         unregister : function(dlg){
32962             delete list[dlg.id];
32963             var i=0;
32964             var len=0;
32965             if(!accessList.indexOf){
32966                 for(  i = 0, len = accessList.length; i < len; i++){
32967                     if(accessList[i] == dlg){
32968                         accessList.splice(i, 1);
32969                         return;
32970                     }
32971                 }
32972             }else{
32973                  i = accessList.indexOf(dlg);
32974                 if(i != -1){
32975                     accessList.splice(i, 1);
32976                 }
32977             }
32978         },
32979
32980         /**
32981          * Gets a registered dialog by id
32982          * @param {String/Object} id The id of the dialog or a dialog
32983          * @return {Roo.BasicDialog} this
32984          */
32985         get : function(id){
32986             return typeof id == "object" ? id : list[id];
32987         },
32988
32989         /**
32990          * Brings the specified dialog to the front
32991          * @param {String/Object} dlg The id of the dialog or a dialog
32992          * @return {Roo.BasicDialog} this
32993          */
32994         bringToFront : function(dlg){
32995             dlg = this.get(dlg);
32996             if(dlg != front){
32997                 front = dlg;
32998                 dlg._lastAccess = new Date().getTime();
32999                 orderDialogs();
33000             }
33001             return dlg;
33002         },
33003
33004         /**
33005          * Sends the specified dialog to the back
33006          * @param {String/Object} dlg The id of the dialog or a dialog
33007          * @return {Roo.BasicDialog} this
33008          */
33009         sendToBack : function(dlg){
33010             dlg = this.get(dlg);
33011             dlg._lastAccess = -(new Date().getTime());
33012             orderDialogs();
33013             return dlg;
33014         },
33015
33016         /**
33017          * Hides all dialogs
33018          */
33019         hideAll : function(){
33020             for(var id in list){
33021                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
33022                     list[id].hide();
33023                 }
33024             }
33025         }
33026     };
33027 }();
33028
33029 /**
33030  * @class Roo.LayoutDialog
33031  * @extends Roo.BasicDialog
33032  * Dialog which provides adjustments for working with a layout in a Dialog.
33033  * Add your necessary layout config options to the dialog's config.<br>
33034  * Example usage (including a nested layout):
33035  * <pre><code>
33036 if(!dialog){
33037     dialog = new Roo.LayoutDialog("download-dlg", {
33038         modal: true,
33039         width:600,
33040         height:450,
33041         shadow:true,
33042         minWidth:500,
33043         minHeight:350,
33044         autoTabs:true,
33045         proxyDrag:true,
33046         // layout config merges with the dialog config
33047         center:{
33048             tabPosition: "top",
33049             alwaysShowTabs: true
33050         }
33051     });
33052     dialog.addKeyListener(27, dialog.hide, dialog);
33053     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
33054     dialog.addButton("Build It!", this.getDownload, this);
33055
33056     // we can even add nested layouts
33057     var innerLayout = new Roo.BorderLayout("dl-inner", {
33058         east: {
33059             initialSize: 200,
33060             autoScroll:true,
33061             split:true
33062         },
33063         center: {
33064             autoScroll:true
33065         }
33066     });
33067     innerLayout.beginUpdate();
33068     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
33069     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
33070     innerLayout.endUpdate(true);
33071
33072     var layout = dialog.getLayout();
33073     layout.beginUpdate();
33074     layout.add("center", new Roo.ContentPanel("standard-panel",
33075                         {title: "Download the Source", fitToFrame:true}));
33076     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
33077                {title: "Build your own roo.js"}));
33078     layout.getRegion("center").showPanel(sp);
33079     layout.endUpdate();
33080 }
33081 </code></pre>
33082     * @constructor
33083     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
33084     * @param {Object} config configuration options
33085   */
33086 Roo.LayoutDialog = function(el, cfg){
33087     
33088     var config=  cfg;
33089     if (typeof(cfg) == 'undefined') {
33090         config = Roo.apply({}, el);
33091         // not sure why we use documentElement here.. - it should always be body.
33092         // IE7 borks horribly if we use documentElement.
33093         // webkit also does not like documentElement - it creates a body element...
33094         el = Roo.get( document.body || document.documentElement ).createChild();
33095         //config.autoCreate = true;
33096     }
33097     
33098     
33099     config.autoTabs = false;
33100     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
33101     this.body.setStyle({overflow:"hidden", position:"relative"});
33102     this.layout = new Roo.BorderLayout(this.body.dom, config);
33103     this.layout.monitorWindowResize = false;
33104     this.el.addClass("x-dlg-auto-layout");
33105     // fix case when center region overwrites center function
33106     this.center = Roo.BasicDialog.prototype.center;
33107     this.on("show", this.layout.layout, this.layout, true);
33108     if (config.items) {
33109         var xitems = config.items;
33110         delete config.items;
33111         Roo.each(xitems, this.addxtype, this);
33112     }
33113     
33114     
33115 };
33116 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
33117     /**
33118      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
33119      * @deprecated
33120      */
33121     endUpdate : function(){
33122         this.layout.endUpdate();
33123     },
33124
33125     /**
33126      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
33127      *  @deprecated
33128      */
33129     beginUpdate : function(){
33130         this.layout.beginUpdate();
33131     },
33132
33133     /**
33134      * Get the BorderLayout for this dialog
33135      * @return {Roo.BorderLayout}
33136      */
33137     getLayout : function(){
33138         return this.layout;
33139     },
33140
33141     showEl : function(){
33142         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
33143         if(Roo.isIE7){
33144             this.layout.layout();
33145         }
33146     },
33147
33148     // private
33149     // Use the syncHeightBeforeShow config option to control this automatically
33150     syncBodyHeight : function(){
33151         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
33152         if(this.layout){this.layout.layout();}
33153     },
33154     
33155       /**
33156      * Add an xtype element (actually adds to the layout.)
33157      * @return {Object} xdata xtype object data.
33158      */
33159     
33160     addxtype : function(c) {
33161         return this.layout.addxtype(c);
33162     }
33163 });/*
33164  * Based on:
33165  * Ext JS Library 1.1.1
33166  * Copyright(c) 2006-2007, Ext JS, LLC.
33167  *
33168  * Originally Released Under LGPL - original licence link has changed is not relivant.
33169  *
33170  * Fork - LGPL
33171  * <script type="text/javascript">
33172  */
33173  
33174 /**
33175  * @class Roo.MessageBox
33176  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
33177  * Example usage:
33178  *<pre><code>
33179 // Basic alert:
33180 Roo.Msg.alert('Status', 'Changes saved successfully.');
33181
33182 // Prompt for user data:
33183 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
33184     if (btn == 'ok'){
33185         // process text value...
33186     }
33187 });
33188
33189 // Show a dialog using config options:
33190 Roo.Msg.show({
33191    title:'Save Changes?',
33192    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
33193    buttons: Roo.Msg.YESNOCANCEL,
33194    fn: processResult,
33195    animEl: 'elId'
33196 });
33197 </code></pre>
33198  * @singleton
33199  */
33200 Roo.MessageBox = function(){
33201     var dlg, opt, mask, waitTimer;
33202     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
33203     var buttons, activeTextEl, bwidth;
33204
33205     // private
33206     var handleButton = function(button){
33207         dlg.hide();
33208         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
33209     };
33210
33211     // private
33212     var handleHide = function(){
33213         if(opt && opt.cls){
33214             dlg.el.removeClass(opt.cls);
33215         }
33216         if(waitTimer){
33217             Roo.TaskMgr.stop(waitTimer);
33218             waitTimer = null;
33219         }
33220     };
33221
33222     // private
33223     var updateButtons = function(b){
33224         var width = 0;
33225         if(!b){
33226             buttons["ok"].hide();
33227             buttons["cancel"].hide();
33228             buttons["yes"].hide();
33229             buttons["no"].hide();
33230             dlg.footer.dom.style.display = 'none';
33231             return width;
33232         }
33233         dlg.footer.dom.style.display = '';
33234         for(var k in buttons){
33235             if(typeof buttons[k] != "function"){
33236                 if(b[k]){
33237                     buttons[k].show();
33238                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
33239                     width += buttons[k].el.getWidth()+15;
33240                 }else{
33241                     buttons[k].hide();
33242                 }
33243             }
33244         }
33245         return width;
33246     };
33247
33248     // private
33249     var handleEsc = function(d, k, e){
33250         if(opt && opt.closable !== false){
33251             dlg.hide();
33252         }
33253         if(e){
33254             e.stopEvent();
33255         }
33256     };
33257
33258     return {
33259         /**
33260          * Returns a reference to the underlying {@link Roo.BasicDialog} element
33261          * @return {Roo.BasicDialog} The BasicDialog element
33262          */
33263         getDialog : function(){
33264            if(!dlg){
33265                 dlg = new Roo.BasicDialog("x-msg-box", {
33266                     autoCreate : true,
33267                     shadow: true,
33268                     draggable: true,
33269                     resizable:false,
33270                     constraintoviewport:false,
33271                     fixedcenter:true,
33272                     collapsible : false,
33273                     shim:true,
33274                     modal: true,
33275                     width:400, height:100,
33276                     buttonAlign:"center",
33277                     closeClick : function(){
33278                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
33279                             handleButton("no");
33280                         }else{
33281                             handleButton("cancel");
33282                         }
33283                     }
33284                 });
33285                 dlg.on("hide", handleHide);
33286                 mask = dlg.mask;
33287                 dlg.addKeyListener(27, handleEsc);
33288                 buttons = {};
33289                 var bt = this.buttonText;
33290                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
33291                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
33292                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
33293                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
33294                 bodyEl = dlg.body.createChild({
33295
33296                     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>'
33297                 });
33298                 msgEl = bodyEl.dom.firstChild;
33299                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
33300                 textboxEl.enableDisplayMode();
33301                 textboxEl.addKeyListener([10,13], function(){
33302                     if(dlg.isVisible() && opt && opt.buttons){
33303                         if(opt.buttons.ok){
33304                             handleButton("ok");
33305                         }else if(opt.buttons.yes){
33306                             handleButton("yes");
33307                         }
33308                     }
33309                 });
33310                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
33311                 textareaEl.enableDisplayMode();
33312                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
33313                 progressEl.enableDisplayMode();
33314                 var pf = progressEl.dom.firstChild;
33315                 if (pf) {
33316                     pp = Roo.get(pf.firstChild);
33317                     pp.setHeight(pf.offsetHeight);
33318                 }
33319                 
33320             }
33321             return dlg;
33322         },
33323
33324         /**
33325          * Updates the message box body text
33326          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
33327          * the XHTML-compliant non-breaking space character '&amp;#160;')
33328          * @return {Roo.MessageBox} This message box
33329          */
33330         updateText : function(text){
33331             if(!dlg.isVisible() && !opt.width){
33332                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
33333             }
33334             msgEl.innerHTML = text || '&#160;';
33335       
33336             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
33337             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
33338             var w = Math.max(
33339                     Math.min(opt.width || cw , this.maxWidth), 
33340                     Math.max(opt.minWidth || this.minWidth, bwidth)
33341             );
33342             if(opt.prompt){
33343                 activeTextEl.setWidth(w);
33344             }
33345             if(dlg.isVisible()){
33346                 dlg.fixedcenter = false;
33347             }
33348             // to big, make it scroll. = But as usual stupid IE does not support
33349             // !important..
33350             
33351             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
33352                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
33353                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
33354             } else {
33355                 bodyEl.dom.style.height = '';
33356                 bodyEl.dom.style.overflowY = '';
33357             }
33358             if (cw > w) {
33359                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
33360             } else {
33361                 bodyEl.dom.style.overflowX = '';
33362             }
33363             
33364             dlg.setContentSize(w, bodyEl.getHeight());
33365             if(dlg.isVisible()){
33366                 dlg.fixedcenter = true;
33367             }
33368             return this;
33369         },
33370
33371         /**
33372          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
33373          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
33374          * @param {Number} value Any number between 0 and 1 (e.g., .5)
33375          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
33376          * @return {Roo.MessageBox} This message box
33377          */
33378         updateProgress : function(value, text){
33379             if(text){
33380                 this.updateText(text);
33381             }
33382             if (pp) { // weird bug on my firefox - for some reason this is not defined
33383                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
33384             }
33385             return this;
33386         },        
33387
33388         /**
33389          * Returns true if the message box is currently displayed
33390          * @return {Boolean} True if the message box is visible, else false
33391          */
33392         isVisible : function(){
33393             return dlg && dlg.isVisible();  
33394         },
33395
33396         /**
33397          * Hides the message box if it is displayed
33398          */
33399         hide : function(){
33400             if(this.isVisible()){
33401                 dlg.hide();
33402             }  
33403         },
33404
33405         /**
33406          * Displays a new message box, or reinitializes an existing message box, based on the config options
33407          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
33408          * The following config object properties are supported:
33409          * <pre>
33410 Property    Type             Description
33411 ----------  ---------------  ------------------------------------------------------------------------------------
33412 animEl            String/Element   An id or Element from which the message box should animate as it opens and
33413                                    closes (defaults to undefined)
33414 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
33415                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
33416 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
33417                                    progress and wait dialogs will ignore this property and always hide the
33418                                    close button as they can only be closed programmatically.
33419 cls               String           A custom CSS class to apply to the message box element
33420 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
33421                                    displayed (defaults to 75)
33422 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
33423                                    function will be btn (the name of the button that was clicked, if applicable,
33424                                    e.g. "ok"), and text (the value of the active text field, if applicable).
33425                                    Progress and wait dialogs will ignore this option since they do not respond to
33426                                    user actions and can only be closed programmatically, so any required function
33427                                    should be called by the same code after it closes the dialog.
33428 icon              String           A CSS class that provides a background image to be used as an icon for
33429                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
33430 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
33431 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
33432 modal             Boolean          False to allow user interaction with the page while the message box is
33433                                    displayed (defaults to true)
33434 msg               String           A string that will replace the existing message box body text (defaults
33435                                    to the XHTML-compliant non-breaking space character '&#160;')
33436 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
33437 progress          Boolean          True to display a progress bar (defaults to false)
33438 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
33439 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
33440 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
33441 title             String           The title text
33442 value             String           The string value to set into the active textbox element if displayed
33443 wait              Boolean          True to display a progress bar (defaults to false)
33444 width             Number           The width of the dialog in pixels
33445 </pre>
33446          *
33447          * Example usage:
33448          * <pre><code>
33449 Roo.Msg.show({
33450    title: 'Address',
33451    msg: 'Please enter your address:',
33452    width: 300,
33453    buttons: Roo.MessageBox.OKCANCEL,
33454    multiline: true,
33455    fn: saveAddress,
33456    animEl: 'addAddressBtn'
33457 });
33458 </code></pre>
33459          * @param {Object} config Configuration options
33460          * @return {Roo.MessageBox} This message box
33461          */
33462         show : function(options)
33463         {
33464             
33465             // this causes nightmares if you show one dialog after another
33466             // especially on callbacks..
33467              
33468             if(this.isVisible()){
33469                 
33470                 this.hide();
33471                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
33472                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
33473                 Roo.log("New Dialog Message:" +  options.msg )
33474                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
33475                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
33476                 
33477             }
33478             var d = this.getDialog();
33479             opt = options;
33480             d.setTitle(opt.title || "&#160;");
33481             d.close.setDisplayed(opt.closable !== false);
33482             activeTextEl = textboxEl;
33483             opt.prompt = opt.prompt || (opt.multiline ? true : false);
33484             if(opt.prompt){
33485                 if(opt.multiline){
33486                     textboxEl.hide();
33487                     textareaEl.show();
33488                     textareaEl.setHeight(typeof opt.multiline == "number" ?
33489                         opt.multiline : this.defaultTextHeight);
33490                     activeTextEl = textareaEl;
33491                 }else{
33492                     textboxEl.show();
33493                     textareaEl.hide();
33494                 }
33495             }else{
33496                 textboxEl.hide();
33497                 textareaEl.hide();
33498             }
33499             progressEl.setDisplayed(opt.progress === true);
33500             this.updateProgress(0);
33501             activeTextEl.dom.value = opt.value || "";
33502             if(opt.prompt){
33503                 dlg.setDefaultButton(activeTextEl);
33504             }else{
33505                 var bs = opt.buttons;
33506                 var db = null;
33507                 if(bs && bs.ok){
33508                     db = buttons["ok"];
33509                 }else if(bs && bs.yes){
33510                     db = buttons["yes"];
33511                 }
33512                 dlg.setDefaultButton(db);
33513             }
33514             bwidth = updateButtons(opt.buttons);
33515             this.updateText(opt.msg);
33516             if(opt.cls){
33517                 d.el.addClass(opt.cls);
33518             }
33519             d.proxyDrag = opt.proxyDrag === true;
33520             d.modal = opt.modal !== false;
33521             d.mask = opt.modal !== false ? mask : false;
33522             if(!d.isVisible()){
33523                 // force it to the end of the z-index stack so it gets a cursor in FF
33524                 document.body.appendChild(dlg.el.dom);
33525                 d.animateTarget = null;
33526                 d.show(options.animEl);
33527             }
33528             return this;
33529         },
33530
33531         /**
33532          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
33533          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
33534          * and closing the message box when the process is complete.
33535          * @param {String} title The title bar text
33536          * @param {String} msg The message box body text
33537          * @return {Roo.MessageBox} This message box
33538          */
33539         progress : function(title, msg){
33540             this.show({
33541                 title : title,
33542                 msg : msg,
33543                 buttons: false,
33544                 progress:true,
33545                 closable:false,
33546                 minWidth: this.minProgressWidth,
33547                 modal : true
33548             });
33549             return this;
33550         },
33551
33552         /**
33553          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
33554          * If a callback function is passed it will be called after the user clicks the button, and the
33555          * id of the button that was clicked will be passed as the only parameter to the callback
33556          * (could also be the top-right close button).
33557          * @param {String} title The title bar text
33558          * @param {String} msg The message box body text
33559          * @param {Function} fn (optional) The callback function invoked after the message box is closed
33560          * @param {Object} scope (optional) The scope of the callback function
33561          * @return {Roo.MessageBox} This message box
33562          */
33563         alert : function(title, msg, fn, scope){
33564             this.show({
33565                 title : title,
33566                 msg : msg,
33567                 buttons: this.OK,
33568                 fn: fn,
33569                 scope : scope,
33570                 modal : true
33571             });
33572             return this;
33573         },
33574
33575         /**
33576          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
33577          * interaction while waiting for a long-running process to complete that does not have defined intervals.
33578          * You are responsible for closing the message box when the process is complete.
33579          * @param {String} msg The message box body text
33580          * @param {String} title (optional) The title bar text
33581          * @return {Roo.MessageBox} This message box
33582          */
33583         wait : function(msg, title){
33584             this.show({
33585                 title : title,
33586                 msg : msg,
33587                 buttons: false,
33588                 closable:false,
33589                 progress:true,
33590                 modal:true,
33591                 width:300,
33592                 wait:true
33593             });
33594             waitTimer = Roo.TaskMgr.start({
33595                 run: function(i){
33596                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
33597                 },
33598                 interval: 1000
33599             });
33600             return this;
33601         },
33602
33603         /**
33604          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
33605          * If a callback function is passed it will be called after the user clicks either button, and the id of the
33606          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
33607          * @param {String} title The title bar text
33608          * @param {String} msg The message box body text
33609          * @param {Function} fn (optional) The callback function invoked after the message box is closed
33610          * @param {Object} scope (optional) The scope of the callback function
33611          * @return {Roo.MessageBox} This message box
33612          */
33613         confirm : function(title, msg, fn, scope){
33614             this.show({
33615                 title : title,
33616                 msg : msg,
33617                 buttons: this.YESNO,
33618                 fn: fn,
33619                 scope : scope,
33620                 modal : true
33621             });
33622             return this;
33623         },
33624
33625         /**
33626          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
33627          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
33628          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
33629          * (could also be the top-right close button) and the text that was entered will be passed as the two
33630          * parameters to the callback.
33631          * @param {String} title The title bar text
33632          * @param {String} msg The message box body text
33633          * @param {Function} fn (optional) The callback function invoked after the message box is closed
33634          * @param {Object} scope (optional) The scope of the callback function
33635          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
33636          * property, or the height in pixels to create the textbox (defaults to false / single-line)
33637          * @return {Roo.MessageBox} This message box
33638          */
33639         prompt : function(title, msg, fn, scope, multiline){
33640             this.show({
33641                 title : title,
33642                 msg : msg,
33643                 buttons: this.OKCANCEL,
33644                 fn: fn,
33645                 minWidth:250,
33646                 scope : scope,
33647                 prompt:true,
33648                 multiline: multiline,
33649                 modal : true
33650             });
33651             return this;
33652         },
33653
33654         /**
33655          * Button config that displays a single OK button
33656          * @type Object
33657          */
33658         OK : {ok:true},
33659         /**
33660          * Button config that displays Yes and No buttons
33661          * @type Object
33662          */
33663         YESNO : {yes:true, no:true},
33664         /**
33665          * Button config that displays OK and Cancel buttons
33666          * @type Object
33667          */
33668         OKCANCEL : {ok:true, cancel:true},
33669         /**
33670          * Button config that displays Yes, No and Cancel buttons
33671          * @type Object
33672          */
33673         YESNOCANCEL : {yes:true, no:true, cancel:true},
33674
33675         /**
33676          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
33677          * @type Number
33678          */
33679         defaultTextHeight : 75,
33680         /**
33681          * The maximum width in pixels of the message box (defaults to 600)
33682          * @type Number
33683          */
33684         maxWidth : 600,
33685         /**
33686          * The minimum width in pixels of the message box (defaults to 100)
33687          * @type Number
33688          */
33689         minWidth : 100,
33690         /**
33691          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
33692          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
33693          * @type Number
33694          */
33695         minProgressWidth : 250,
33696         /**
33697          * An object containing the default button text strings that can be overriden for localized language support.
33698          * Supported properties are: ok, cancel, yes and no.
33699          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
33700          * @type Object
33701          */
33702         buttonText : {
33703             ok : "OK",
33704             cancel : "Cancel",
33705             yes : "Yes",
33706             no : "No"
33707         }
33708     };
33709 }();
33710
33711 /**
33712  * Shorthand for {@link Roo.MessageBox}
33713  */
33714 Roo.Msg = Roo.MessageBox;/*
33715  * Based on:
33716  * Ext JS Library 1.1.1
33717  * Copyright(c) 2006-2007, Ext JS, LLC.
33718  *
33719  * Originally Released Under LGPL - original licence link has changed is not relivant.
33720  *
33721  * Fork - LGPL
33722  * <script type="text/javascript">
33723  */
33724 /**
33725  * @class Roo.QuickTips
33726  * Provides attractive and customizable tooltips for any element.
33727  * @singleton
33728  */
33729 Roo.QuickTips = function(){
33730     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
33731     var ce, bd, xy, dd;
33732     var visible = false, disabled = true, inited = false;
33733     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
33734     
33735     var onOver = function(e){
33736         if(disabled){
33737             return;
33738         }
33739         var t = e.getTarget();
33740         if(!t || t.nodeType !== 1 || t == document || t == document.body){
33741             return;
33742         }
33743         if(ce && t == ce.el){
33744             clearTimeout(hideProc);
33745             return;
33746         }
33747         if(t && tagEls[t.id]){
33748             tagEls[t.id].el = t;
33749             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
33750             return;
33751         }
33752         var ttp, et = Roo.fly(t);
33753         var ns = cfg.namespace;
33754         if(tm.interceptTitles && t.title){
33755             ttp = t.title;
33756             t.qtip = ttp;
33757             t.removeAttribute("title");
33758             e.preventDefault();
33759         }else{
33760             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
33761         }
33762         if(ttp){
33763             showProc = show.defer(tm.showDelay, tm, [{
33764                 el: t, 
33765                 text: ttp.replace(/\\n/g,'<br/>'),
33766                 width: et.getAttributeNS(ns, cfg.width),
33767                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
33768                 title: et.getAttributeNS(ns, cfg.title),
33769                     cls: et.getAttributeNS(ns, cfg.cls)
33770             }]);
33771         }
33772     };
33773     
33774     var onOut = function(e){
33775         clearTimeout(showProc);
33776         var t = e.getTarget();
33777         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
33778             hideProc = setTimeout(hide, tm.hideDelay);
33779         }
33780     };
33781     
33782     var onMove = function(e){
33783         if(disabled){
33784             return;
33785         }
33786         xy = e.getXY();
33787         xy[1] += 18;
33788         if(tm.trackMouse && ce){
33789             el.setXY(xy);
33790         }
33791     };
33792     
33793     var onDown = function(e){
33794         clearTimeout(showProc);
33795         clearTimeout(hideProc);
33796         if(!e.within(el)){
33797             if(tm.hideOnClick){
33798                 hide();
33799                 tm.disable();
33800                 tm.enable.defer(100, tm);
33801             }
33802         }
33803     };
33804     
33805     var getPad = function(){
33806         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
33807     };
33808
33809     var show = function(o){
33810         if(disabled){
33811             return;
33812         }
33813         clearTimeout(dismissProc);
33814         ce = o;
33815         if(removeCls){ // in case manually hidden
33816             el.removeClass(removeCls);
33817             removeCls = null;
33818         }
33819         if(ce.cls){
33820             el.addClass(ce.cls);
33821             removeCls = ce.cls;
33822         }
33823         if(ce.title){
33824             tipTitle.update(ce.title);
33825             tipTitle.show();
33826         }else{
33827             tipTitle.update('');
33828             tipTitle.hide();
33829         }
33830         el.dom.style.width  = tm.maxWidth+'px';
33831         //tipBody.dom.style.width = '';
33832         tipBodyText.update(o.text);
33833         var p = getPad(), w = ce.width;
33834         if(!w){
33835             var td = tipBodyText.dom;
33836             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
33837             if(aw > tm.maxWidth){
33838                 w = tm.maxWidth;
33839             }else if(aw < tm.minWidth){
33840                 w = tm.minWidth;
33841             }else{
33842                 w = aw;
33843             }
33844         }
33845         //tipBody.setWidth(w);
33846         el.setWidth(parseInt(w, 10) + p);
33847         if(ce.autoHide === false){
33848             close.setDisplayed(true);
33849             if(dd){
33850                 dd.unlock();
33851             }
33852         }else{
33853             close.setDisplayed(false);
33854             if(dd){
33855                 dd.lock();
33856             }
33857         }
33858         if(xy){
33859             el.avoidY = xy[1]-18;
33860             el.setXY(xy);
33861         }
33862         if(tm.animate){
33863             el.setOpacity(.1);
33864             el.setStyle("visibility", "visible");
33865             el.fadeIn({callback: afterShow});
33866         }else{
33867             afterShow();
33868         }
33869     };
33870     
33871     var afterShow = function(){
33872         if(ce){
33873             el.show();
33874             esc.enable();
33875             if(tm.autoDismiss && ce.autoHide !== false){
33876                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
33877             }
33878         }
33879     };
33880     
33881     var hide = function(noanim){
33882         clearTimeout(dismissProc);
33883         clearTimeout(hideProc);
33884         ce = null;
33885         if(el.isVisible()){
33886             esc.disable();
33887             if(noanim !== true && tm.animate){
33888                 el.fadeOut({callback: afterHide});
33889             }else{
33890                 afterHide();
33891             } 
33892         }
33893     };
33894     
33895     var afterHide = function(){
33896         el.hide();
33897         if(removeCls){
33898             el.removeClass(removeCls);
33899             removeCls = null;
33900         }
33901     };
33902     
33903     return {
33904         /**
33905         * @cfg {Number} minWidth
33906         * The minimum width of the quick tip (defaults to 40)
33907         */
33908        minWidth : 40,
33909         /**
33910         * @cfg {Number} maxWidth
33911         * The maximum width of the quick tip (defaults to 300)
33912         */
33913        maxWidth : 300,
33914         /**
33915         * @cfg {Boolean} interceptTitles
33916         * True to automatically use the element's DOM title value if available (defaults to false)
33917         */
33918        interceptTitles : false,
33919         /**
33920         * @cfg {Boolean} trackMouse
33921         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
33922         */
33923        trackMouse : false,
33924         /**
33925         * @cfg {Boolean} hideOnClick
33926         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
33927         */
33928        hideOnClick : true,
33929         /**
33930         * @cfg {Number} showDelay
33931         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
33932         */
33933        showDelay : 500,
33934         /**
33935         * @cfg {Number} hideDelay
33936         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
33937         */
33938        hideDelay : 200,
33939         /**
33940         * @cfg {Boolean} autoHide
33941         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
33942         * Used in conjunction with hideDelay.
33943         */
33944        autoHide : true,
33945         /**
33946         * @cfg {Boolean}
33947         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
33948         * (defaults to true).  Used in conjunction with autoDismissDelay.
33949         */
33950        autoDismiss : true,
33951         /**
33952         * @cfg {Number}
33953         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
33954         */
33955        autoDismissDelay : 5000,
33956        /**
33957         * @cfg {Boolean} animate
33958         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
33959         */
33960        animate : false,
33961
33962        /**
33963         * @cfg {String} title
33964         * Title text to display (defaults to '').  This can be any valid HTML markup.
33965         */
33966         title: '',
33967        /**
33968         * @cfg {String} text
33969         * Body text to display (defaults to '').  This can be any valid HTML markup.
33970         */
33971         text : '',
33972        /**
33973         * @cfg {String} cls
33974         * A CSS class to apply to the base quick tip element (defaults to '').
33975         */
33976         cls : '',
33977        /**
33978         * @cfg {Number} width
33979         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
33980         * minWidth or maxWidth.
33981         */
33982         width : null,
33983
33984     /**
33985      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
33986      * or display QuickTips in a page.
33987      */
33988        init : function(){
33989           tm = Roo.QuickTips;
33990           cfg = tm.tagConfig;
33991           if(!inited){
33992               if(!Roo.isReady){ // allow calling of init() before onReady
33993                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
33994                   return;
33995               }
33996               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
33997               el.fxDefaults = {stopFx: true};
33998               // maximum custom styling
33999               //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>');
34000               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>');              
34001               tipTitle = el.child('h3');
34002               tipTitle.enableDisplayMode("block");
34003               tipBody = el.child('div.x-tip-bd');
34004               tipBodyText = el.child('div.x-tip-bd-inner');
34005               //bdLeft = el.child('div.x-tip-bd-left');
34006               //bdRight = el.child('div.x-tip-bd-right');
34007               close = el.child('div.x-tip-close');
34008               close.enableDisplayMode("block");
34009               close.on("click", hide);
34010               var d = Roo.get(document);
34011               d.on("mousedown", onDown);
34012               d.on("mouseover", onOver);
34013               d.on("mouseout", onOut);
34014               d.on("mousemove", onMove);
34015               esc = d.addKeyListener(27, hide);
34016               esc.disable();
34017               if(Roo.dd.DD){
34018                   dd = el.initDD("default", null, {
34019                       onDrag : function(){
34020                           el.sync();  
34021                       }
34022                   });
34023                   dd.setHandleElId(tipTitle.id);
34024                   dd.lock();
34025               }
34026               inited = true;
34027           }
34028           this.enable(); 
34029        },
34030
34031     /**
34032      * Configures a new quick tip instance and assigns it to a target element.  The following config options
34033      * are supported:
34034      * <pre>
34035 Property    Type                   Description
34036 ----------  ---------------------  ------------------------------------------------------------------------
34037 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
34038      * </ul>
34039      * @param {Object} config The config object
34040      */
34041        register : function(config){
34042            var cs = config instanceof Array ? config : arguments;
34043            for(var i = 0, len = cs.length; i < len; i++) {
34044                var c = cs[i];
34045                var target = c.target;
34046                if(target){
34047                    if(target instanceof Array){
34048                        for(var j = 0, jlen = target.length; j < jlen; j++){
34049                            tagEls[target[j]] = c;
34050                        }
34051                    }else{
34052                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
34053                    }
34054                }
34055            }
34056        },
34057
34058     /**
34059      * Removes this quick tip from its element and destroys it.
34060      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
34061      */
34062        unregister : function(el){
34063            delete tagEls[Roo.id(el)];
34064        },
34065
34066     /**
34067      * Enable this quick tip.
34068      */
34069        enable : function(){
34070            if(inited && disabled){
34071                locks.pop();
34072                if(locks.length < 1){
34073                    disabled = false;
34074                }
34075            }
34076        },
34077
34078     /**
34079      * Disable this quick tip.
34080      */
34081        disable : function(){
34082           disabled = true;
34083           clearTimeout(showProc);
34084           clearTimeout(hideProc);
34085           clearTimeout(dismissProc);
34086           if(ce){
34087               hide(true);
34088           }
34089           locks.push(1);
34090        },
34091
34092     /**
34093      * Returns true if the quick tip is enabled, else false.
34094      */
34095        isEnabled : function(){
34096             return !disabled;
34097        },
34098
34099         // private
34100        tagConfig : {
34101            namespace : "roo", // was ext?? this may break..
34102            alt_namespace : "ext",
34103            attribute : "qtip",
34104            width : "width",
34105            target : "target",
34106            title : "qtitle",
34107            hide : "hide",
34108            cls : "qclass"
34109        }
34110    };
34111 }();
34112
34113 // backwards compat
34114 Roo.QuickTips.tips = Roo.QuickTips.register;/*
34115  * Based on:
34116  * Ext JS Library 1.1.1
34117  * Copyright(c) 2006-2007, Ext JS, LLC.
34118  *
34119  * Originally Released Under LGPL - original licence link has changed is not relivant.
34120  *
34121  * Fork - LGPL
34122  * <script type="text/javascript">
34123  */
34124  
34125
34126 /**
34127  * @class Roo.tree.TreePanel
34128  * @extends Roo.data.Tree
34129
34130  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
34131  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
34132  * @cfg {Boolean} enableDD true to enable drag and drop
34133  * @cfg {Boolean} enableDrag true to enable just drag
34134  * @cfg {Boolean} enableDrop true to enable just drop
34135  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
34136  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
34137  * @cfg {String} ddGroup The DD group this TreePanel belongs to
34138  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
34139  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
34140  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
34141  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
34142  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
34143  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
34144  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
34145  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
34146  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
34147  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
34148  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
34149  * @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>
34150  * @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>
34151  * 
34152  * @constructor
34153  * @param {String/HTMLElement/Element} el The container element
34154  * @param {Object} config
34155  */
34156 Roo.tree.TreePanel = function(el, config){
34157     var root = false;
34158     var loader = false;
34159     if (config.root) {
34160         root = config.root;
34161         delete config.root;
34162     }
34163     if (config.loader) {
34164         loader = config.loader;
34165         delete config.loader;
34166     }
34167     
34168     Roo.apply(this, config);
34169     Roo.tree.TreePanel.superclass.constructor.call(this);
34170     this.el = Roo.get(el);
34171     this.el.addClass('x-tree');
34172     //console.log(root);
34173     if (root) {
34174         this.setRootNode( Roo.factory(root, Roo.tree));
34175     }
34176     if (loader) {
34177         this.loader = Roo.factory(loader, Roo.tree);
34178     }
34179    /**
34180     * Read-only. The id of the container element becomes this TreePanel's id.
34181     */
34182     this.id = this.el.id;
34183     this.addEvents({
34184         /**
34185         * @event beforeload
34186         * Fires before a node is loaded, return false to cancel
34187         * @param {Node} node The node being loaded
34188         */
34189         "beforeload" : true,
34190         /**
34191         * @event load
34192         * Fires when a node is loaded
34193         * @param {Node} node The node that was loaded
34194         */
34195         "load" : true,
34196         /**
34197         * @event textchange
34198         * Fires when the text for a node is changed
34199         * @param {Node} node The node
34200         * @param {String} text The new text
34201         * @param {String} oldText The old text
34202         */
34203         "textchange" : true,
34204         /**
34205         * @event beforeexpand
34206         * Fires before a node is expanded, return false to cancel.
34207         * @param {Node} node The node
34208         * @param {Boolean} deep
34209         * @param {Boolean} anim
34210         */
34211         "beforeexpand" : true,
34212         /**
34213         * @event beforecollapse
34214         * Fires before a node is collapsed, return false to cancel.
34215         * @param {Node} node The node
34216         * @param {Boolean} deep
34217         * @param {Boolean} anim
34218         */
34219         "beforecollapse" : true,
34220         /**
34221         * @event expand
34222         * Fires when a node is expanded
34223         * @param {Node} node The node
34224         */
34225         "expand" : true,
34226         /**
34227         * @event disabledchange
34228         * Fires when the disabled status of a node changes
34229         * @param {Node} node The node
34230         * @param {Boolean} disabled
34231         */
34232         "disabledchange" : true,
34233         /**
34234         * @event collapse
34235         * Fires when a node is collapsed
34236         * @param {Node} node The node
34237         */
34238         "collapse" : true,
34239         /**
34240         * @event beforeclick
34241         * Fires before click processing on a node. Return false to cancel the default action.
34242         * @param {Node} node The node
34243         * @param {Roo.EventObject} e The event object
34244         */
34245         "beforeclick":true,
34246         /**
34247         * @event checkchange
34248         * Fires when a node with a checkbox's checked property changes
34249         * @param {Node} this This node
34250         * @param {Boolean} checked
34251         */
34252         "checkchange":true,
34253         /**
34254         * @event click
34255         * Fires when a node is clicked
34256         * @param {Node} node The node
34257         * @param {Roo.EventObject} e The event object
34258         */
34259         "click":true,
34260         /**
34261         * @event dblclick
34262         * Fires when a node is double clicked
34263         * @param {Node} node The node
34264         * @param {Roo.EventObject} e The event object
34265         */
34266         "dblclick":true,
34267         /**
34268         * @event contextmenu
34269         * Fires when a node is right clicked
34270         * @param {Node} node The node
34271         * @param {Roo.EventObject} e The event object
34272         */
34273         "contextmenu":true,
34274         /**
34275         * @event beforechildrenrendered
34276         * Fires right before the child nodes for a node are rendered
34277         * @param {Node} node The node
34278         */
34279         "beforechildrenrendered":true,
34280         /**
34281         * @event startdrag
34282         * Fires when a node starts being dragged
34283         * @param {Roo.tree.TreePanel} this
34284         * @param {Roo.tree.TreeNode} node
34285         * @param {event} e The raw browser event
34286         */ 
34287        "startdrag" : true,
34288        /**
34289         * @event enddrag
34290         * Fires when a drag operation is complete
34291         * @param {Roo.tree.TreePanel} this
34292         * @param {Roo.tree.TreeNode} node
34293         * @param {event} e The raw browser event
34294         */
34295        "enddrag" : true,
34296        /**
34297         * @event dragdrop
34298         * Fires when a dragged node is dropped on a valid DD target
34299         * @param {Roo.tree.TreePanel} this
34300         * @param {Roo.tree.TreeNode} node
34301         * @param {DD} dd The dd it was dropped on
34302         * @param {event} e The raw browser event
34303         */
34304        "dragdrop" : true,
34305        /**
34306         * @event beforenodedrop
34307         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
34308         * passed to handlers has the following properties:<br />
34309         * <ul style="padding:5px;padding-left:16px;">
34310         * <li>tree - The TreePanel</li>
34311         * <li>target - The node being targeted for the drop</li>
34312         * <li>data - The drag data from the drag source</li>
34313         * <li>point - The point of the drop - append, above or below</li>
34314         * <li>source - The drag source</li>
34315         * <li>rawEvent - Raw mouse event</li>
34316         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
34317         * to be inserted by setting them on this object.</li>
34318         * <li>cancel - Set this to true to cancel the drop.</li>
34319         * </ul>
34320         * @param {Object} dropEvent
34321         */
34322        "beforenodedrop" : true,
34323        /**
34324         * @event nodedrop
34325         * Fires after a DD object is dropped on a node in this tree. The dropEvent
34326         * passed to handlers has the following properties:<br />
34327         * <ul style="padding:5px;padding-left:16px;">
34328         * <li>tree - The TreePanel</li>
34329         * <li>target - The node being targeted for the drop</li>
34330         * <li>data - The drag data from the drag source</li>
34331         * <li>point - The point of the drop - append, above or below</li>
34332         * <li>source - The drag source</li>
34333         * <li>rawEvent - Raw mouse event</li>
34334         * <li>dropNode - Dropped node(s).</li>
34335         * </ul>
34336         * @param {Object} dropEvent
34337         */
34338        "nodedrop" : true,
34339         /**
34340         * @event nodedragover
34341         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
34342         * passed to handlers has the following properties:<br />
34343         * <ul style="padding:5px;padding-left:16px;">
34344         * <li>tree - The TreePanel</li>
34345         * <li>target - The node being targeted for the drop</li>
34346         * <li>data - The drag data from the drag source</li>
34347         * <li>point - The point of the drop - append, above or below</li>
34348         * <li>source - The drag source</li>
34349         * <li>rawEvent - Raw mouse event</li>
34350         * <li>dropNode - Drop node(s) provided by the source.</li>
34351         * <li>cancel - Set this to true to signal drop not allowed.</li>
34352         * </ul>
34353         * @param {Object} dragOverEvent
34354         */
34355        "nodedragover" : true,
34356        /**
34357         * @event appendnode
34358         * Fires when append node to the tree
34359         * @param {Roo.tree.TreePanel} this
34360         * @param {Roo.tree.TreeNode} node
34361         * @param {Number} index The index of the newly appended node
34362         */
34363        "appendnode" : true
34364         
34365     });
34366     if(this.singleExpand){
34367        this.on("beforeexpand", this.restrictExpand, this);
34368     }
34369     if (this.editor) {
34370         this.editor.tree = this;
34371         this.editor = Roo.factory(this.editor, Roo.tree);
34372     }
34373     
34374     if (this.selModel) {
34375         this.selModel = Roo.factory(this.selModel, Roo.tree);
34376     }
34377    
34378 };
34379 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
34380     rootVisible : true,
34381     animate: Roo.enableFx,
34382     lines : true,
34383     enableDD : false,
34384     hlDrop : Roo.enableFx,
34385   
34386     renderer: false,
34387     
34388     rendererTip: false,
34389     // private
34390     restrictExpand : function(node){
34391         var p = node.parentNode;
34392         if(p){
34393             if(p.expandedChild && p.expandedChild.parentNode == p){
34394                 p.expandedChild.collapse();
34395             }
34396             p.expandedChild = node;
34397         }
34398     },
34399
34400     // private override
34401     setRootNode : function(node){
34402         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
34403         if(!this.rootVisible){
34404             node.ui = new Roo.tree.RootTreeNodeUI(node);
34405         }
34406         return node;
34407     },
34408
34409     /**
34410      * Returns the container element for this TreePanel
34411      */
34412     getEl : function(){
34413         return this.el;
34414     },
34415
34416     /**
34417      * Returns the default TreeLoader for this TreePanel
34418      */
34419     getLoader : function(){
34420         return this.loader;
34421     },
34422
34423     /**
34424      * Expand all nodes
34425      */
34426     expandAll : function(){
34427         this.root.expand(true);
34428     },
34429
34430     /**
34431      * Collapse all nodes
34432      */
34433     collapseAll : function(){
34434         this.root.collapse(true);
34435     },
34436
34437     /**
34438      * Returns the selection model used by this TreePanel
34439      */
34440     getSelectionModel : function(){
34441         if(!this.selModel){
34442             this.selModel = new Roo.tree.DefaultSelectionModel();
34443         }
34444         return this.selModel;
34445     },
34446
34447     /**
34448      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
34449      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
34450      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
34451      * @return {Array}
34452      */
34453     getChecked : function(a, startNode){
34454         startNode = startNode || this.root;
34455         var r = [];
34456         var f = function(){
34457             if(this.attributes.checked){
34458                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
34459             }
34460         }
34461         startNode.cascade(f);
34462         return r;
34463     },
34464
34465     /**
34466      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
34467      * @param {String} path
34468      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
34469      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
34470      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
34471      */
34472     expandPath : function(path, attr, callback){
34473         attr = attr || "id";
34474         var keys = path.split(this.pathSeparator);
34475         var curNode = this.root;
34476         if(curNode.attributes[attr] != keys[1]){ // invalid root
34477             if(callback){
34478                 callback(false, null);
34479             }
34480             return;
34481         }
34482         var index = 1;
34483         var f = function(){
34484             if(++index == keys.length){
34485                 if(callback){
34486                     callback(true, curNode);
34487                 }
34488                 return;
34489             }
34490             var c = curNode.findChild(attr, keys[index]);
34491             if(!c){
34492                 if(callback){
34493                     callback(false, curNode);
34494                 }
34495                 return;
34496             }
34497             curNode = c;
34498             c.expand(false, false, f);
34499         };
34500         curNode.expand(false, false, f);
34501     },
34502
34503     /**
34504      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
34505      * @param {String} path
34506      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
34507      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
34508      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
34509      */
34510     selectPath : function(path, attr, callback){
34511         attr = attr || "id";
34512         var keys = path.split(this.pathSeparator);
34513         var v = keys.pop();
34514         if(keys.length > 0){
34515             var f = function(success, node){
34516                 if(success && node){
34517                     var n = node.findChild(attr, v);
34518                     if(n){
34519                         n.select();
34520                         if(callback){
34521                             callback(true, n);
34522                         }
34523                     }else if(callback){
34524                         callback(false, n);
34525                     }
34526                 }else{
34527                     if(callback){
34528                         callback(false, n);
34529                     }
34530                 }
34531             };
34532             this.expandPath(keys.join(this.pathSeparator), attr, f);
34533         }else{
34534             this.root.select();
34535             if(callback){
34536                 callback(true, this.root);
34537             }
34538         }
34539     },
34540
34541     getTreeEl : function(){
34542         return this.el;
34543     },
34544
34545     /**
34546      * Trigger rendering of this TreePanel
34547      */
34548     render : function(){
34549         if (this.innerCt) {
34550             return this; // stop it rendering more than once!!
34551         }
34552         
34553         this.innerCt = this.el.createChild({tag:"ul",
34554                cls:"x-tree-root-ct " +
34555                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
34556
34557         if(this.containerScroll){
34558             Roo.dd.ScrollManager.register(this.el);
34559         }
34560         if((this.enableDD || this.enableDrop) && !this.dropZone){
34561            /**
34562             * The dropZone used by this tree if drop is enabled
34563             * @type Roo.tree.TreeDropZone
34564             */
34565              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
34566                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
34567            });
34568         }
34569         if((this.enableDD || this.enableDrag) && !this.dragZone){
34570            /**
34571             * The dragZone used by this tree if drag is enabled
34572             * @type Roo.tree.TreeDragZone
34573             */
34574             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
34575                ddGroup: this.ddGroup || "TreeDD",
34576                scroll: this.ddScroll
34577            });
34578         }
34579         this.getSelectionModel().init(this);
34580         if (!this.root) {
34581             Roo.log("ROOT not set in tree");
34582             return this;
34583         }
34584         this.root.render();
34585         if(!this.rootVisible){
34586             this.root.renderChildren();
34587         }
34588         return this;
34589     }
34590 });/*
34591  * Based on:
34592  * Ext JS Library 1.1.1
34593  * Copyright(c) 2006-2007, Ext JS, LLC.
34594  *
34595  * Originally Released Under LGPL - original licence link has changed is not relivant.
34596  *
34597  * Fork - LGPL
34598  * <script type="text/javascript">
34599  */
34600  
34601
34602 /**
34603  * @class Roo.tree.DefaultSelectionModel
34604  * @extends Roo.util.Observable
34605  * The default single selection for a TreePanel.
34606  * @param {Object} cfg Configuration
34607  */
34608 Roo.tree.DefaultSelectionModel = function(cfg){
34609    this.selNode = null;
34610    
34611    
34612    
34613    this.addEvents({
34614        /**
34615         * @event selectionchange
34616         * Fires when the selected node changes
34617         * @param {DefaultSelectionModel} this
34618         * @param {TreeNode} node the new selection
34619         */
34620        "selectionchange" : true,
34621
34622        /**
34623         * @event beforeselect
34624         * Fires before the selected node changes, return false to cancel the change
34625         * @param {DefaultSelectionModel} this
34626         * @param {TreeNode} node the new selection
34627         * @param {TreeNode} node the old selection
34628         */
34629        "beforeselect" : true
34630    });
34631    
34632     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
34633 };
34634
34635 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
34636     init : function(tree){
34637         this.tree = tree;
34638         tree.getTreeEl().on("keydown", this.onKeyDown, this);
34639         tree.on("click", this.onNodeClick, this);
34640     },
34641     
34642     onNodeClick : function(node, e){
34643         if (e.ctrlKey && this.selNode == node)  {
34644             this.unselect(node);
34645             return;
34646         }
34647         this.select(node);
34648     },
34649     
34650     /**
34651      * Select a node.
34652      * @param {TreeNode} node The node to select
34653      * @return {TreeNode} The selected node
34654      */
34655     select : function(node){
34656         var last = this.selNode;
34657         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
34658             if(last){
34659                 last.ui.onSelectedChange(false);
34660             }
34661             this.selNode = node;
34662             node.ui.onSelectedChange(true);
34663             this.fireEvent("selectionchange", this, node, last);
34664         }
34665         return node;
34666     },
34667     
34668     /**
34669      * Deselect a node.
34670      * @param {TreeNode} node The node to unselect
34671      */
34672     unselect : function(node){
34673         if(this.selNode == node){
34674             this.clearSelections();
34675         }    
34676     },
34677     
34678     /**
34679      * Clear all selections
34680      */
34681     clearSelections : function(){
34682         var n = this.selNode;
34683         if(n){
34684             n.ui.onSelectedChange(false);
34685             this.selNode = null;
34686             this.fireEvent("selectionchange", this, null);
34687         }
34688         return n;
34689     },
34690     
34691     /**
34692      * Get the selected node
34693      * @return {TreeNode} The selected node
34694      */
34695     getSelectedNode : function(){
34696         return this.selNode;    
34697     },
34698     
34699     /**
34700      * Returns true if the node is selected
34701      * @param {TreeNode} node The node to check
34702      * @return {Boolean}
34703      */
34704     isSelected : function(node){
34705         return this.selNode == node;  
34706     },
34707
34708     /**
34709      * Selects the node above the selected node in the tree, intelligently walking the nodes
34710      * @return TreeNode The new selection
34711      */
34712     selectPrevious : function(){
34713         var s = this.selNode || this.lastSelNode;
34714         if(!s){
34715             return null;
34716         }
34717         var ps = s.previousSibling;
34718         if(ps){
34719             if(!ps.isExpanded() || ps.childNodes.length < 1){
34720                 return this.select(ps);
34721             } else{
34722                 var lc = ps.lastChild;
34723                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
34724                     lc = lc.lastChild;
34725                 }
34726                 return this.select(lc);
34727             }
34728         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
34729             return this.select(s.parentNode);
34730         }
34731         return null;
34732     },
34733
34734     /**
34735      * Selects the node above the selected node in the tree, intelligently walking the nodes
34736      * @return TreeNode The new selection
34737      */
34738     selectNext : function(){
34739         var s = this.selNode || this.lastSelNode;
34740         if(!s){
34741             return null;
34742         }
34743         if(s.firstChild && s.isExpanded()){
34744              return this.select(s.firstChild);
34745          }else if(s.nextSibling){
34746              return this.select(s.nextSibling);
34747          }else if(s.parentNode){
34748             var newS = null;
34749             s.parentNode.bubble(function(){
34750                 if(this.nextSibling){
34751                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
34752                     return false;
34753                 }
34754             });
34755             return newS;
34756          }
34757         return null;
34758     },
34759
34760     onKeyDown : function(e){
34761         var s = this.selNode || this.lastSelNode;
34762         // undesirable, but required
34763         var sm = this;
34764         if(!s){
34765             return;
34766         }
34767         var k = e.getKey();
34768         switch(k){
34769              case e.DOWN:
34770                  e.stopEvent();
34771                  this.selectNext();
34772              break;
34773              case e.UP:
34774                  e.stopEvent();
34775                  this.selectPrevious();
34776              break;
34777              case e.RIGHT:
34778                  e.preventDefault();
34779                  if(s.hasChildNodes()){
34780                      if(!s.isExpanded()){
34781                          s.expand();
34782                      }else if(s.firstChild){
34783                          this.select(s.firstChild, e);
34784                      }
34785                  }
34786              break;
34787              case e.LEFT:
34788                  e.preventDefault();
34789                  if(s.hasChildNodes() && s.isExpanded()){
34790                      s.collapse();
34791                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
34792                      this.select(s.parentNode, e);
34793                  }
34794              break;
34795         };
34796     }
34797 });
34798
34799 /**
34800  * @class Roo.tree.MultiSelectionModel
34801  * @extends Roo.util.Observable
34802  * Multi selection for a TreePanel.
34803  * @param {Object} cfg Configuration
34804  */
34805 Roo.tree.MultiSelectionModel = function(){
34806    this.selNodes = [];
34807    this.selMap = {};
34808    this.addEvents({
34809        /**
34810         * @event selectionchange
34811         * Fires when the selected nodes change
34812         * @param {MultiSelectionModel} this
34813         * @param {Array} nodes Array of the selected nodes
34814         */
34815        "selectionchange" : true
34816    });
34817    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
34818    
34819 };
34820
34821 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
34822     init : function(tree){
34823         this.tree = tree;
34824         tree.getTreeEl().on("keydown", this.onKeyDown, this);
34825         tree.on("click", this.onNodeClick, this);
34826     },
34827     
34828     onNodeClick : function(node, e){
34829         this.select(node, e, e.ctrlKey);
34830     },
34831     
34832     /**
34833      * Select a node.
34834      * @param {TreeNode} node The node to select
34835      * @param {EventObject} e (optional) An event associated with the selection
34836      * @param {Boolean} keepExisting True to retain existing selections
34837      * @return {TreeNode} The selected node
34838      */
34839     select : function(node, e, keepExisting){
34840         if(keepExisting !== true){
34841             this.clearSelections(true);
34842         }
34843         if(this.isSelected(node)){
34844             this.lastSelNode = node;
34845             return node;
34846         }
34847         this.selNodes.push(node);
34848         this.selMap[node.id] = node;
34849         this.lastSelNode = node;
34850         node.ui.onSelectedChange(true);
34851         this.fireEvent("selectionchange", this, this.selNodes);
34852         return node;
34853     },
34854     
34855     /**
34856      * Deselect a node.
34857      * @param {TreeNode} node The node to unselect
34858      */
34859     unselect : function(node){
34860         if(this.selMap[node.id]){
34861             node.ui.onSelectedChange(false);
34862             var sn = this.selNodes;
34863             var index = -1;
34864             if(sn.indexOf){
34865                 index = sn.indexOf(node);
34866             }else{
34867                 for(var i = 0, len = sn.length; i < len; i++){
34868                     if(sn[i] == node){
34869                         index = i;
34870                         break;
34871                     }
34872                 }
34873             }
34874             if(index != -1){
34875                 this.selNodes.splice(index, 1);
34876             }
34877             delete this.selMap[node.id];
34878             this.fireEvent("selectionchange", this, this.selNodes);
34879         }
34880     },
34881     
34882     /**
34883      * Clear all selections
34884      */
34885     clearSelections : function(suppressEvent){
34886         var sn = this.selNodes;
34887         if(sn.length > 0){
34888             for(var i = 0, len = sn.length; i < len; i++){
34889                 sn[i].ui.onSelectedChange(false);
34890             }
34891             this.selNodes = [];
34892             this.selMap = {};
34893             if(suppressEvent !== true){
34894                 this.fireEvent("selectionchange", this, this.selNodes);
34895             }
34896         }
34897     },
34898     
34899     /**
34900      * Returns true if the node is selected
34901      * @param {TreeNode} node The node to check
34902      * @return {Boolean}
34903      */
34904     isSelected : function(node){
34905         return this.selMap[node.id] ? true : false;  
34906     },
34907     
34908     /**
34909      * Returns an array of the selected nodes
34910      * @return {Array}
34911      */
34912     getSelectedNodes : function(){
34913         return this.selNodes;    
34914     },
34915
34916     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
34917
34918     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
34919
34920     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
34921 });/*
34922  * Based on:
34923  * Ext JS Library 1.1.1
34924  * Copyright(c) 2006-2007, Ext JS, LLC.
34925  *
34926  * Originally Released Under LGPL - original licence link has changed is not relivant.
34927  *
34928  * Fork - LGPL
34929  * <script type="text/javascript">
34930  */
34931  
34932 /**
34933  * @class Roo.tree.TreeNode
34934  * @extends Roo.data.Node
34935  * @cfg {String} text The text for this node
34936  * @cfg {Boolean} expanded true to start the node expanded
34937  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
34938  * @cfg {Boolean} allowDrop false if this node cannot be drop on
34939  * @cfg {Boolean} disabled true to start the node disabled
34940  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
34941  *    is to use the cls or iconCls attributes and add the icon via a CSS background image.
34942  * @cfg {String} cls A css class to be added to the node
34943  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
34944  * @cfg {String} href URL of the link used for the node (defaults to #)
34945  * @cfg {String} hrefTarget target frame for the link
34946  * @cfg {String} qtip An Ext QuickTip for the node
34947  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
34948  * @cfg {Boolean} singleClickExpand True for single click expand on this node
34949  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
34950  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
34951  * (defaults to undefined with no checkbox rendered)
34952  * @constructor
34953  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
34954  */
34955 Roo.tree.TreeNode = function(attributes){
34956     attributes = attributes || {};
34957     if(typeof attributes == "string"){
34958         attributes = {text: attributes};
34959     }
34960     this.childrenRendered = false;
34961     this.rendered = false;
34962     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
34963     this.expanded = attributes.expanded === true;
34964     this.isTarget = attributes.isTarget !== false;
34965     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
34966     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
34967
34968     /**
34969      * Read-only. The text for this node. To change it use setText().
34970      * @type String
34971      */
34972     this.text = attributes.text;
34973     /**
34974      * True if this node is disabled.
34975      * @type Boolean
34976      */
34977     this.disabled = attributes.disabled === true;
34978
34979     this.addEvents({
34980         /**
34981         * @event textchange
34982         * Fires when the text for this node is changed
34983         * @param {Node} this This node
34984         * @param {String} text The new text
34985         * @param {String} oldText The old text
34986         */
34987         "textchange" : true,
34988         /**
34989         * @event beforeexpand
34990         * Fires before this node is expanded, return false to cancel.
34991         * @param {Node} this This node
34992         * @param {Boolean} deep
34993         * @param {Boolean} anim
34994         */
34995         "beforeexpand" : true,
34996         /**
34997         * @event beforecollapse
34998         * Fires before this node is collapsed, return false to cancel.
34999         * @param {Node} this This node
35000         * @param {Boolean} deep
35001         * @param {Boolean} anim
35002         */
35003         "beforecollapse" : true,
35004         /**
35005         * @event expand
35006         * Fires when this node is expanded
35007         * @param {Node} this This node
35008         */
35009         "expand" : true,
35010         /**
35011         * @event disabledchange
35012         * Fires when the disabled status of this node changes
35013         * @param {Node} this This node
35014         * @param {Boolean} disabled
35015         */
35016         "disabledchange" : true,
35017         /**
35018         * @event collapse
35019         * Fires when this node is collapsed
35020         * @param {Node} this This node
35021         */
35022         "collapse" : true,
35023         /**
35024         * @event beforeclick
35025         * Fires before click processing. Return false to cancel the default action.
35026         * @param {Node} this This node
35027         * @param {Roo.EventObject} e The event object
35028         */
35029         "beforeclick":true,
35030         /**
35031         * @event checkchange
35032         * Fires when a node with a checkbox's checked property changes
35033         * @param {Node} this This node
35034         * @param {Boolean} checked
35035         */
35036         "checkchange":true,
35037         /**
35038         * @event click
35039         * Fires when this node is clicked
35040         * @param {Node} this This node
35041         * @param {Roo.EventObject} e The event object
35042         */
35043         "click":true,
35044         /**
35045         * @event dblclick
35046         * Fires when this node is double clicked
35047         * @param {Node} this This node
35048         * @param {Roo.EventObject} e The event object
35049         */
35050         "dblclick":true,
35051         /**
35052         * @event contextmenu
35053         * Fires when this node is right clicked
35054         * @param {Node} this This node
35055         * @param {Roo.EventObject} e The event object
35056         */
35057         "contextmenu":true,
35058         /**
35059         * @event beforechildrenrendered
35060         * Fires right before the child nodes for this node are rendered
35061         * @param {Node} this This node
35062         */
35063         "beforechildrenrendered":true
35064     });
35065
35066     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
35067
35068     /**
35069      * Read-only. The UI for this node
35070      * @type TreeNodeUI
35071      */
35072     this.ui = new uiClass(this);
35073     
35074     // finally support items[]
35075     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
35076         return;
35077     }
35078     
35079     
35080     Roo.each(this.attributes.items, function(c) {
35081         this.appendChild(Roo.factory(c,Roo.Tree));
35082     }, this);
35083     delete this.attributes.items;
35084     
35085     
35086     
35087 };
35088 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
35089     preventHScroll: true,
35090     /**
35091      * Returns true if this node is expanded
35092      * @return {Boolean}
35093      */
35094     isExpanded : function(){
35095         return this.expanded;
35096     },
35097
35098     /**
35099      * Returns the UI object for this node
35100      * @return {TreeNodeUI}
35101      */
35102     getUI : function(){
35103         return this.ui;
35104     },
35105
35106     // private override
35107     setFirstChild : function(node){
35108         var of = this.firstChild;
35109         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
35110         if(this.childrenRendered && of && node != of){
35111             of.renderIndent(true, true);
35112         }
35113         if(this.rendered){
35114             this.renderIndent(true, true);
35115         }
35116     },
35117
35118     // private override
35119     setLastChild : function(node){
35120         var ol = this.lastChild;
35121         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
35122         if(this.childrenRendered && ol && node != ol){
35123             ol.renderIndent(true, true);
35124         }
35125         if(this.rendered){
35126             this.renderIndent(true, true);
35127         }
35128     },
35129
35130     // these methods are overridden to provide lazy rendering support
35131     // private override
35132     appendChild : function()
35133     {
35134         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
35135         if(node && this.childrenRendered){
35136             node.render();
35137         }
35138         this.ui.updateExpandIcon();
35139         return node;
35140     },
35141
35142     // private override
35143     removeChild : function(node){
35144         this.ownerTree.getSelectionModel().unselect(node);
35145         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
35146         // if it's been rendered remove dom node
35147         if(this.childrenRendered){
35148             node.ui.remove();
35149         }
35150         if(this.childNodes.length < 1){
35151             this.collapse(false, false);
35152         }else{
35153             this.ui.updateExpandIcon();
35154         }
35155         if(!this.firstChild) {
35156             this.childrenRendered = false;
35157         }
35158         return node;
35159     },
35160
35161     // private override
35162     insertBefore : function(node, refNode){
35163         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
35164         if(newNode && refNode && this.childrenRendered){
35165             node.render();
35166         }
35167         this.ui.updateExpandIcon();
35168         return newNode;
35169     },
35170
35171     /**
35172      * Sets the text for this node
35173      * @param {String} text
35174      */
35175     setText : function(text){
35176         var oldText = this.text;
35177         this.text = text;
35178         this.attributes.text = text;
35179         if(this.rendered){ // event without subscribing
35180             this.ui.onTextChange(this, text, oldText);
35181         }
35182         this.fireEvent("textchange", this, text, oldText);
35183     },
35184
35185     /**
35186      * Triggers selection of this node
35187      */
35188     select : function(){
35189         this.getOwnerTree().getSelectionModel().select(this);
35190     },
35191
35192     /**
35193      * Triggers deselection of this node
35194      */
35195     unselect : function(){
35196         this.getOwnerTree().getSelectionModel().unselect(this);
35197     },
35198
35199     /**
35200      * Returns true if this node is selected
35201      * @return {Boolean}
35202      */
35203     isSelected : function(){
35204         return this.getOwnerTree().getSelectionModel().isSelected(this);
35205     },
35206
35207     /**
35208      * Expand this node.
35209      * @param {Boolean} deep (optional) True to expand all children as well
35210      * @param {Boolean} anim (optional) false to cancel the default animation
35211      * @param {Function} callback (optional) A callback to be called when
35212      * expanding this node completes (does not wait for deep expand to complete).
35213      * Called with 1 parameter, this node.
35214      */
35215     expand : function(deep, anim, callback){
35216         if(!this.expanded){
35217             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
35218                 return;
35219             }
35220             if(!this.childrenRendered){
35221                 this.renderChildren();
35222             }
35223             this.expanded = true;
35224             
35225             if(!this.isHiddenRoot() && (this.getOwnerTree() && this.getOwnerTree().animate && anim !== false) || anim){
35226                 this.ui.animExpand(function(){
35227                     this.fireEvent("expand", this);
35228                     if(typeof callback == "function"){
35229                         callback(this);
35230                     }
35231                     if(deep === true){
35232                         this.expandChildNodes(true);
35233                     }
35234                 }.createDelegate(this));
35235                 return;
35236             }else{
35237                 this.ui.expand();
35238                 this.fireEvent("expand", this);
35239                 if(typeof callback == "function"){
35240                     callback(this);
35241                 }
35242             }
35243         }else{
35244            if(typeof callback == "function"){
35245                callback(this);
35246            }
35247         }
35248         if(deep === true){
35249             this.expandChildNodes(true);
35250         }
35251     },
35252
35253     isHiddenRoot : function(){
35254         return this.isRoot && !this.getOwnerTree().rootVisible;
35255     },
35256
35257     /**
35258      * Collapse this node.
35259      * @param {Boolean} deep (optional) True to collapse all children as well
35260      * @param {Boolean} anim (optional) false to cancel the default animation
35261      */
35262     collapse : function(deep, anim){
35263         if(this.expanded && !this.isHiddenRoot()){
35264             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
35265                 return;
35266             }
35267             this.expanded = false;
35268             if((this.getOwnerTree().animate && anim !== false) || anim){
35269                 this.ui.animCollapse(function(){
35270                     this.fireEvent("collapse", this);
35271                     if(deep === true){
35272                         this.collapseChildNodes(true);
35273                     }
35274                 }.createDelegate(this));
35275                 return;
35276             }else{
35277                 this.ui.collapse();
35278                 this.fireEvent("collapse", this);
35279             }
35280         }
35281         if(deep === true){
35282             var cs = this.childNodes;
35283             for(var i = 0, len = cs.length; i < len; i++) {
35284                 cs[i].collapse(true, false);
35285             }
35286         }
35287     },
35288
35289     // private
35290     delayedExpand : function(delay){
35291         if(!this.expandProcId){
35292             this.expandProcId = this.expand.defer(delay, this);
35293         }
35294     },
35295
35296     // private
35297     cancelExpand : function(){
35298         if(this.expandProcId){
35299             clearTimeout(this.expandProcId);
35300         }
35301         this.expandProcId = false;
35302     },
35303
35304     /**
35305      * Toggles expanded/collapsed state of the node
35306      */
35307     toggle : function(){
35308         if(this.expanded){
35309             this.collapse();
35310         }else{
35311             this.expand();
35312         }
35313     },
35314
35315     /**
35316      * Ensures all parent nodes are expanded
35317      */
35318     ensureVisible : function(callback){
35319         var tree = this.getOwnerTree();
35320         tree.expandPath(this.parentNode.getPath(), false, function(){
35321             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
35322             Roo.callback(callback);
35323         }.createDelegate(this));
35324     },
35325
35326     /**
35327      * Expand all child nodes
35328      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
35329      */
35330     expandChildNodes : function(deep){
35331         var cs = this.childNodes;
35332         for(var i = 0, len = cs.length; i < len; i++) {
35333                 cs[i].expand(deep);
35334         }
35335     },
35336
35337     /**
35338      * Collapse all child nodes
35339      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
35340      */
35341     collapseChildNodes : function(deep){
35342         var cs = this.childNodes;
35343         for(var i = 0, len = cs.length; i < len; i++) {
35344                 cs[i].collapse(deep);
35345         }
35346     },
35347
35348     /**
35349      * Disables this node
35350      */
35351     disable : function(){
35352         this.disabled = true;
35353         this.unselect();
35354         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
35355             this.ui.onDisableChange(this, true);
35356         }
35357         this.fireEvent("disabledchange", this, true);
35358     },
35359
35360     /**
35361      * Enables this node
35362      */
35363     enable : function(){
35364         this.disabled = false;
35365         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
35366             this.ui.onDisableChange(this, false);
35367         }
35368         this.fireEvent("disabledchange", this, false);
35369     },
35370
35371     // private
35372     renderChildren : function(suppressEvent){
35373         if(suppressEvent !== false){
35374             this.fireEvent("beforechildrenrendered", this);
35375         }
35376         var cs = this.childNodes;
35377         for(var i = 0, len = cs.length; i < len; i++){
35378             cs[i].render(true);
35379         }
35380         this.childrenRendered = true;
35381     },
35382
35383     // private
35384     sort : function(fn, scope){
35385         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
35386         if(this.childrenRendered){
35387             var cs = this.childNodes;
35388             for(var i = 0, len = cs.length; i < len; i++){
35389                 cs[i].render(true);
35390             }
35391         }
35392     },
35393
35394     // private
35395     render : function(bulkRender){
35396         this.ui.render(bulkRender);
35397         if(!this.rendered){
35398             this.rendered = true;
35399             if(this.expanded){
35400                 this.expanded = false;
35401                 this.expand(false, false);
35402             }
35403         }
35404     },
35405
35406     // private
35407     renderIndent : function(deep, refresh){
35408         if(refresh){
35409             this.ui.childIndent = null;
35410         }
35411         this.ui.renderIndent();
35412         if(deep === true && this.childrenRendered){
35413             var cs = this.childNodes;
35414             for(var i = 0, len = cs.length; i < len; i++){
35415                 cs[i].renderIndent(true, refresh);
35416             }
35417         }
35418     }
35419 });/*
35420  * Based on:
35421  * Ext JS Library 1.1.1
35422  * Copyright(c) 2006-2007, Ext JS, LLC.
35423  *
35424  * Originally Released Under LGPL - original licence link has changed is not relivant.
35425  *
35426  * Fork - LGPL
35427  * <script type="text/javascript">
35428  */
35429  
35430 /**
35431  * @class Roo.tree.AsyncTreeNode
35432  * @extends Roo.tree.TreeNode
35433  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
35434  * @constructor
35435  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
35436  */
35437  Roo.tree.AsyncTreeNode = function(config){
35438     this.loaded = false;
35439     this.loading = false;
35440     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
35441     /**
35442     * @event beforeload
35443     * Fires before this node is loaded, return false to cancel
35444     * @param {Node} this This node
35445     */
35446     this.addEvents({'beforeload':true, 'load': true});
35447     /**
35448     * @event load
35449     * Fires when this node is loaded
35450     * @param {Node} this This node
35451     */
35452     /**
35453      * The loader used by this node (defaults to using the tree's defined loader)
35454      * @type TreeLoader
35455      * @property loader
35456      */
35457 };
35458 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
35459     expand : function(deep, anim, callback){
35460         if(this.loading){ // if an async load is already running, waiting til it's done
35461             var timer;
35462             var f = function(){
35463                 if(!this.loading){ // done loading
35464                     clearInterval(timer);
35465                     this.expand(deep, anim, callback);
35466                 }
35467             }.createDelegate(this);
35468             timer = setInterval(f, 200);
35469             return;
35470         }
35471         if(!this.loaded){
35472             if(this.fireEvent("beforeload", this) === false){
35473                 return;
35474             }
35475             this.loading = true;
35476             this.ui.beforeLoad(this);
35477             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
35478             if(loader){
35479                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
35480                 return;
35481             }
35482         }
35483         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
35484     },
35485     
35486     /**
35487      * Returns true if this node is currently loading
35488      * @return {Boolean}
35489      */
35490     isLoading : function(){
35491         return this.loading;  
35492     },
35493     
35494     loadComplete : function(deep, anim, callback){
35495         this.loading = false;
35496         this.loaded = true;
35497         this.ui.afterLoad(this);
35498         this.fireEvent("load", this);
35499         this.expand(deep, anim, callback);
35500     },
35501     
35502     /**
35503      * Returns true if this node has been loaded
35504      * @return {Boolean}
35505      */
35506     isLoaded : function(){
35507         return this.loaded;
35508     },
35509     
35510     hasChildNodes : function(){
35511         if(!this.isLeaf() && !this.loaded){
35512             return true;
35513         }else{
35514             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
35515         }
35516     },
35517
35518     /**
35519      * Trigger a reload for this node
35520      * @param {Function} callback
35521      */
35522     reload : function(callback){
35523         this.collapse(false, false);
35524         while(this.firstChild){
35525             this.removeChild(this.firstChild);
35526         }
35527         this.childrenRendered = false;
35528         this.loaded = false;
35529         if(this.isHiddenRoot()){
35530             this.expanded = false;
35531         }
35532         this.expand(false, false, callback);
35533     }
35534 });/*
35535  * Based on:
35536  * Ext JS Library 1.1.1
35537  * Copyright(c) 2006-2007, Ext JS, LLC.
35538  *
35539  * Originally Released Under LGPL - original licence link has changed is not relivant.
35540  *
35541  * Fork - LGPL
35542  * <script type="text/javascript">
35543  */
35544  
35545 /**
35546  * @class Roo.tree.TreeNodeUI
35547  * @constructor
35548  * @param {Object} node The node to render
35549  * The TreeNode UI implementation is separate from the
35550  * tree implementation. Unless you are customizing the tree UI,
35551  * you should never have to use this directly.
35552  */
35553 Roo.tree.TreeNodeUI = function(node){
35554     this.node = node;
35555     this.rendered = false;
35556     this.animating = false;
35557     this.emptyIcon = Roo.BLANK_IMAGE_URL;
35558 };
35559
35560 Roo.tree.TreeNodeUI.prototype = {
35561     removeChild : function(node){
35562         if(this.rendered){
35563             this.ctNode.removeChild(node.ui.getEl());
35564         }
35565     },
35566
35567     beforeLoad : function(){
35568          this.addClass("x-tree-node-loading");
35569     },
35570
35571     afterLoad : function(){
35572          this.removeClass("x-tree-node-loading");
35573     },
35574
35575     onTextChange : function(node, text, oldText){
35576         if(this.rendered){
35577             this.textNode.innerHTML = text;
35578         }
35579     },
35580
35581     onDisableChange : function(node, state){
35582         this.disabled = state;
35583         if(state){
35584             this.addClass("x-tree-node-disabled");
35585         }else{
35586             this.removeClass("x-tree-node-disabled");
35587         }
35588     },
35589
35590     onSelectedChange : function(state){
35591         if(state){
35592             this.focus();
35593             this.addClass("x-tree-selected");
35594         }else{
35595             //this.blur();
35596             this.removeClass("x-tree-selected");
35597         }
35598     },
35599
35600     onMove : function(tree, node, oldParent, newParent, index, refNode){
35601         this.childIndent = null;
35602         if(this.rendered){
35603             var targetNode = newParent.ui.getContainer();
35604             if(!targetNode){//target not rendered
35605                 this.holder = document.createElement("div");
35606                 this.holder.appendChild(this.wrap);
35607                 return;
35608             }
35609             var insertBefore = refNode ? refNode.ui.getEl() : null;
35610             if(insertBefore){
35611                 targetNode.insertBefore(this.wrap, insertBefore);
35612             }else{
35613                 targetNode.appendChild(this.wrap);
35614             }
35615             this.node.renderIndent(true);
35616         }
35617     },
35618
35619     addClass : function(cls){
35620         if(this.elNode){
35621             Roo.fly(this.elNode).addClass(cls);
35622         }
35623     },
35624
35625     removeClass : function(cls){
35626         if(this.elNode){
35627             Roo.fly(this.elNode).removeClass(cls);
35628         }
35629     },
35630
35631     remove : function(){
35632         if(this.rendered){
35633             this.holder = document.createElement("div");
35634             this.holder.appendChild(this.wrap);
35635         }
35636     },
35637
35638     fireEvent : function(){
35639         return this.node.fireEvent.apply(this.node, arguments);
35640     },
35641
35642     initEvents : function(){
35643         this.node.on("move", this.onMove, this);
35644         var E = Roo.EventManager;
35645         var a = this.anchor;
35646
35647         var el = Roo.fly(a, '_treeui');
35648
35649         if(Roo.isOpera){ // opera render bug ignores the CSS
35650             el.setStyle("text-decoration", "none");
35651         }
35652
35653         el.on("click", this.onClick, this);
35654         el.on("dblclick", this.onDblClick, this);
35655
35656         if(this.checkbox){
35657             Roo.EventManager.on(this.checkbox,
35658                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
35659         }
35660
35661         el.on("contextmenu", this.onContextMenu, this);
35662
35663         var icon = Roo.fly(this.iconNode);
35664         icon.on("click", this.onClick, this);
35665         icon.on("dblclick", this.onDblClick, this);
35666         icon.on("contextmenu", this.onContextMenu, this);
35667         E.on(this.ecNode, "click", this.ecClick, this, true);
35668
35669         if(this.node.disabled){
35670             this.addClass("x-tree-node-disabled");
35671         }
35672         if(this.node.hidden){
35673             this.addClass("x-tree-node-disabled");
35674         }
35675         var ot = this.node.getOwnerTree();
35676         var dd = ot ? (ot.enableDD || ot.enableDrag || ot.enableDrop) : false;
35677         if(dd && (!this.node.isRoot || ot.rootVisible)){
35678             Roo.dd.Registry.register(this.elNode, {
35679                 node: this.node,
35680                 handles: this.getDDHandles(),
35681                 isHandle: false
35682             });
35683         }
35684     },
35685
35686     getDDHandles : function(){
35687         return [this.iconNode, this.textNode];
35688     },
35689
35690     hide : function(){
35691         if(this.rendered){
35692             this.wrap.style.display = "none";
35693         }
35694     },
35695
35696     show : function(){
35697         if(this.rendered){
35698             this.wrap.style.display = "";
35699         }
35700     },
35701
35702     onContextMenu : function(e){
35703         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
35704             e.preventDefault();
35705             this.focus();
35706             this.fireEvent("contextmenu", this.node, e);
35707         }
35708     },
35709
35710     onClick : function(e){
35711         if(this.dropping){
35712             e.stopEvent();
35713             return;
35714         }
35715         if(this.fireEvent("beforeclick", this.node, e) !== false){
35716             if(!this.disabled && this.node.attributes.href){
35717                 this.fireEvent("click", this.node, e);
35718                 return;
35719             }
35720             e.preventDefault();
35721             if(this.disabled){
35722                 return;
35723             }
35724
35725             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
35726                 this.node.toggle();
35727             }
35728
35729             this.fireEvent("click", this.node, e);
35730         }else{
35731             e.stopEvent();
35732         }
35733     },
35734
35735     onDblClick : function(e){
35736         e.preventDefault();
35737         if(this.disabled){
35738             return;
35739         }
35740         if(this.checkbox){
35741             this.toggleCheck();
35742         }
35743         if(!this.animating && this.node.hasChildNodes()){
35744             this.node.toggle();
35745         }
35746         this.fireEvent("dblclick", this.node, e);
35747     },
35748
35749     onCheckChange : function(){
35750         var checked = this.checkbox.checked;
35751         this.node.attributes.checked = checked;
35752         this.fireEvent('checkchange', this.node, checked);
35753     },
35754
35755     ecClick : function(e){
35756         if(!this.animating && this.node.hasChildNodes()){
35757             this.node.toggle();
35758         }
35759     },
35760
35761     startDrop : function(){
35762         this.dropping = true;
35763     },
35764
35765     // delayed drop so the click event doesn't get fired on a drop
35766     endDrop : function(){
35767        setTimeout(function(){
35768            this.dropping = false;
35769        }.createDelegate(this), 50);
35770     },
35771
35772     expand : function(){
35773         this.updateExpandIcon();
35774         this.ctNode.style.display = "";
35775     },
35776
35777     focus : function(){
35778         if(!this.node.preventHScroll){
35779             try{this.anchor.focus();
35780             }catch(e){}
35781         }else if(!Roo.isIE){
35782             try{
35783                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
35784                 var l = noscroll.scrollLeft;
35785                 this.anchor.focus();
35786                 noscroll.scrollLeft = l;
35787             }catch(e){}
35788         }
35789     },
35790
35791     toggleCheck : function(value){
35792         var cb = this.checkbox;
35793         if(cb){
35794             cb.checked = (value === undefined ? !cb.checked : value);
35795         }
35796     },
35797
35798     blur : function(){
35799         try{
35800             this.anchor.blur();
35801         }catch(e){}
35802     },
35803
35804     animExpand : function(callback){
35805         var ct = Roo.get(this.ctNode);
35806         ct.stopFx();
35807         if(!this.node.hasChildNodes()){
35808             this.updateExpandIcon();
35809             this.ctNode.style.display = "";
35810             Roo.callback(callback);
35811             return;
35812         }
35813         this.animating = true;
35814         this.updateExpandIcon();
35815
35816         ct.slideIn('t', {
35817            callback : function(){
35818                this.animating = false;
35819                Roo.callback(callback);
35820             },
35821             scope: this,
35822             duration: this.node.ownerTree.duration || .25
35823         });
35824     },
35825
35826     highlight : function(){
35827         var tree = this.node.getOwnerTree();
35828         Roo.fly(this.wrap).highlight(
35829             tree.hlColor || "C3DAF9",
35830             {endColor: tree.hlBaseColor}
35831         );
35832     },
35833
35834     collapse : function(){
35835         this.updateExpandIcon();
35836         this.ctNode.style.display = "none";
35837     },
35838
35839     animCollapse : function(callback){
35840         var ct = Roo.get(this.ctNode);
35841         ct.enableDisplayMode('block');
35842         ct.stopFx();
35843
35844         this.animating = true;
35845         this.updateExpandIcon();
35846
35847         ct.slideOut('t', {
35848             callback : function(){
35849                this.animating = false;
35850                Roo.callback(callback);
35851             },
35852             scope: this,
35853             duration: this.node.ownerTree.duration || .25
35854         });
35855     },
35856
35857     getContainer : function(){
35858         return this.ctNode;
35859     },
35860
35861     getEl : function(){
35862         return this.wrap;
35863     },
35864
35865     appendDDGhost : function(ghostNode){
35866         ghostNode.appendChild(this.elNode.cloneNode(true));
35867     },
35868
35869     getDDRepairXY : function(){
35870         return Roo.lib.Dom.getXY(this.iconNode);
35871     },
35872
35873     onRender : function(){
35874         this.render();
35875     },
35876
35877     render : function(bulkRender){
35878         var n = this.node, a = n.attributes;
35879         var targetNode = n.parentNode ?
35880               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
35881
35882         if(!this.rendered){
35883             this.rendered = true;
35884
35885             this.renderElements(n, a, targetNode, bulkRender);
35886
35887             if(a.qtip){
35888                if(this.textNode.setAttributeNS){
35889                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
35890                    if(a.qtipTitle){
35891                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
35892                    }
35893                }else{
35894                    this.textNode.setAttribute("ext:qtip", a.qtip);
35895                    if(a.qtipTitle){
35896                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
35897                    }
35898                }
35899             }else if(a.qtipCfg){
35900                 a.qtipCfg.target = Roo.id(this.textNode);
35901                 Roo.QuickTips.register(a.qtipCfg);
35902             }
35903             this.initEvents();
35904             if(!this.node.expanded){
35905                 this.updateExpandIcon();
35906             }
35907         }else{
35908             if(bulkRender === true) {
35909                 targetNode.appendChild(this.wrap);
35910             }
35911         }
35912     },
35913
35914     renderElements : function(n, a, targetNode, bulkRender)
35915     {
35916         // add some indent caching, this helps performance when rendering a large tree
35917         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
35918         var t = n.getOwnerTree();
35919         var txt = t && t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
35920         if (typeof(n.attributes.html) != 'undefined') {
35921             txt = n.attributes.html;
35922         }
35923         var tip = t && t.rendererTip ? t.rendererTip(n.attributes) : txt;
35924         var cb = typeof a.checked == 'boolean';
35925         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
35926         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
35927             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
35928             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
35929             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
35930             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
35931             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
35932              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
35933                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
35934             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
35935             "</li>"];
35936
35937         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
35938             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
35939                                 n.nextSibling.ui.getEl(), buf.join(""));
35940         }else{
35941             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
35942         }
35943
35944         this.elNode = this.wrap.childNodes[0];
35945         this.ctNode = this.wrap.childNodes[1];
35946         var cs = this.elNode.childNodes;
35947         this.indentNode = cs[0];
35948         this.ecNode = cs[1];
35949         this.iconNode = cs[2];
35950         var index = 3;
35951         if(cb){
35952             this.checkbox = cs[3];
35953             index++;
35954         }
35955         this.anchor = cs[index];
35956         this.textNode = cs[index].firstChild;
35957     },
35958
35959     getAnchor : function(){
35960         return this.anchor;
35961     },
35962
35963     getTextEl : function(){
35964         return this.textNode;
35965     },
35966
35967     getIconEl : function(){
35968         return this.iconNode;
35969     },
35970
35971     isChecked : function(){
35972         return this.checkbox ? this.checkbox.checked : false;
35973     },
35974
35975     updateExpandIcon : function(){
35976         if(this.rendered){
35977             var n = this.node, c1, c2;
35978             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
35979             var hasChild = n.hasChildNodes();
35980             if(hasChild){
35981                 if(n.expanded){
35982                     cls += "-minus";
35983                     c1 = "x-tree-node-collapsed";
35984                     c2 = "x-tree-node-expanded";
35985                 }else{
35986                     cls += "-plus";
35987                     c1 = "x-tree-node-expanded";
35988                     c2 = "x-tree-node-collapsed";
35989                 }
35990                 if(this.wasLeaf){
35991                     this.removeClass("x-tree-node-leaf");
35992                     this.wasLeaf = false;
35993                 }
35994                 if(this.c1 != c1 || this.c2 != c2){
35995                     Roo.fly(this.elNode).replaceClass(c1, c2);
35996                     this.c1 = c1; this.c2 = c2;
35997                 }
35998             }else{
35999                 // this changes non-leafs into leafs if they have no children.
36000                 // it's not very rational behaviour..
36001                 
36002                 if(!this.wasLeaf && this.node.leaf){
36003                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
36004                     delete this.c1;
36005                     delete this.c2;
36006                     this.wasLeaf = true;
36007                 }
36008             }
36009             var ecc = "x-tree-ec-icon "+cls;
36010             if(this.ecc != ecc){
36011                 this.ecNode.className = ecc;
36012                 this.ecc = ecc;
36013             }
36014         }
36015     },
36016
36017     getChildIndent : function(){
36018         if(!this.childIndent){
36019             var buf = [];
36020             var p = this.node;
36021             while(p){
36022                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
36023                     if(!p.isLast()) {
36024                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
36025                     } else {
36026                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
36027                     }
36028                 }
36029                 p = p.parentNode;
36030             }
36031             this.childIndent = buf.join("");
36032         }
36033         return this.childIndent;
36034     },
36035
36036     renderIndent : function(){
36037         if(this.rendered){
36038             var indent = "";
36039             var p = this.node.parentNode;
36040             if(p){
36041                 indent = p.ui.getChildIndent();
36042             }
36043             if(this.indentMarkup != indent){ // don't rerender if not required
36044                 this.indentNode.innerHTML = indent;
36045                 this.indentMarkup = indent;
36046             }
36047             this.updateExpandIcon();
36048         }
36049     }
36050 };
36051
36052 Roo.tree.RootTreeNodeUI = function(){
36053     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
36054 };
36055 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
36056     render : function(){
36057         if(!this.rendered){
36058             var targetNode = this.node.ownerTree.innerCt.dom;
36059             this.node.expanded = true;
36060             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
36061             this.wrap = this.ctNode = targetNode.firstChild;
36062         }
36063     },
36064     collapse : function(){
36065     },
36066     expand : function(){
36067     }
36068 });/*
36069  * Based on:
36070  * Ext JS Library 1.1.1
36071  * Copyright(c) 2006-2007, Ext JS, LLC.
36072  *
36073  * Originally Released Under LGPL - original licence link has changed is not relivant.
36074  *
36075  * Fork - LGPL
36076  * <script type="text/javascript">
36077  */
36078 /**
36079  * @class Roo.tree.TreeLoader
36080  * @extends Roo.util.Observable
36081  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
36082  * nodes from a specified URL. The response must be a javascript Array definition
36083  * who's elements are node definition objects. eg:
36084  * <pre><code>
36085 {  success : true,
36086    data :      [
36087    
36088     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
36089     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
36090     ]
36091 }
36092
36093
36094 </code></pre>
36095  * <br><br>
36096  * The old style respose with just an array is still supported, but not recommended.
36097  * <br><br>
36098  *
36099  * A server request is sent, and child nodes are loaded only when a node is expanded.
36100  * The loading node's id is passed to the server under the parameter name "node" to
36101  * enable the server to produce the correct child nodes.
36102  * <br><br>
36103  * To pass extra parameters, an event handler may be attached to the "beforeload"
36104  * event, and the parameters specified in the TreeLoader's baseParams property:
36105  * <pre><code>
36106     myTreeLoader.on("beforeload", function(treeLoader, node) {
36107         this.baseParams.category = node.attributes.category;
36108     }, this);
36109     
36110 </code></pre>
36111  *
36112  * This would pass an HTTP parameter called "category" to the server containing
36113  * the value of the Node's "category" attribute.
36114  * @constructor
36115  * Creates a new Treeloader.
36116  * @param {Object} config A config object containing config properties.
36117  */
36118 Roo.tree.TreeLoader = function(config){
36119     this.baseParams = {};
36120     this.requestMethod = "POST";
36121     Roo.apply(this, config);
36122
36123     this.addEvents({
36124     
36125         /**
36126          * @event beforeload
36127          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
36128          * @param {Object} This TreeLoader object.
36129          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
36130          * @param {Object} callback The callback function specified in the {@link #load} call.
36131          */
36132         beforeload : true,
36133         /**
36134          * @event load
36135          * Fires when the node has been successfuly loaded.
36136          * @param {Object} This TreeLoader object.
36137          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
36138          * @param {Object} response The response object containing the data from the server.
36139          */
36140         load : true,
36141         /**
36142          * @event loadexception
36143          * Fires if the network request failed.
36144          * @param {Object} This TreeLoader object.
36145          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
36146          * @param {Object} response The response object containing the data from the server.
36147          */
36148         loadexception : true,
36149         /**
36150          * @event create
36151          * Fires before a node is created, enabling you to return custom Node types 
36152          * @param {Object} This TreeLoader object.
36153          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
36154          */
36155         create : true
36156     });
36157
36158     Roo.tree.TreeLoader.superclass.constructor.call(this);
36159 };
36160
36161 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
36162     /**
36163     * @cfg {String} dataUrl The URL from which to request a Json string which
36164     * specifies an array of node definition object representing the child nodes
36165     * to be loaded.
36166     */
36167     /**
36168     * @cfg {String} requestMethod either GET or POST
36169     * defaults to POST (due to BC)
36170     * to be loaded.
36171     */
36172     /**
36173     * @cfg {Object} baseParams (optional) An object containing properties which
36174     * specify HTTP parameters to be passed to each request for child nodes.
36175     */
36176     /**
36177     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
36178     * created by this loader. If the attributes sent by the server have an attribute in this object,
36179     * they take priority.
36180     */
36181     /**
36182     * @cfg {Object} uiProviders (optional) An object containing properties which
36183     * 
36184     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
36185     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
36186     * <i>uiProvider</i> attribute of a returned child node is a string rather
36187     * than a reference to a TreeNodeUI implementation, this that string value
36188     * is used as a property name in the uiProviders object. You can define the provider named
36189     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
36190     */
36191     uiProviders : {},
36192
36193     /**
36194     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
36195     * child nodes before loading.
36196     */
36197     clearOnLoad : true,
36198
36199     /**
36200     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
36201     * property on loading, rather than expecting an array. (eg. more compatible to a standard
36202     * Grid query { data : [ .....] }
36203     */
36204     
36205     root : false,
36206      /**
36207     * @cfg {String} queryParam (optional) 
36208     * Name of the query as it will be passed on the querystring (defaults to 'node')
36209     * eg. the request will be ?node=[id]
36210     */
36211     
36212     
36213     queryParam: false,
36214     
36215     /**
36216      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
36217      * This is called automatically when a node is expanded, but may be used to reload
36218      * a node (or append new children if the {@link #clearOnLoad} option is false.)
36219      * @param {Roo.tree.TreeNode} node
36220      * @param {Function} callback
36221      */
36222     load : function(node, callback){
36223         if(this.clearOnLoad){
36224             while(node.firstChild){
36225                 node.removeChild(node.firstChild);
36226             }
36227         }
36228         if(node.attributes.children){ // preloaded json children
36229             var cs = node.attributes.children;
36230             for(var i = 0, len = cs.length; i < len; i++){
36231                 node.appendChild(this.createNode(cs[i]));
36232             }
36233             if(typeof callback == "function"){
36234                 callback();
36235             }
36236         }else if(this.dataUrl){
36237             this.requestData(node, callback);
36238         }
36239     },
36240
36241     getParams: function(node){
36242         var buf = [], bp = this.baseParams;
36243         for(var key in bp){
36244             if(typeof bp[key] != "function"){
36245                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
36246             }
36247         }
36248         var n = this.queryParam === false ? 'node' : this.queryParam;
36249         buf.push(n + "=", encodeURIComponent(node.id));
36250         return buf.join("");
36251     },
36252
36253     requestData : function(node, callback){
36254         if(this.fireEvent("beforeload", this, node, callback) !== false){
36255             this.transId = Roo.Ajax.request({
36256                 method:this.requestMethod,
36257                 url: this.dataUrl||this.url,
36258                 success: this.handleResponse,
36259                 failure: this.handleFailure,
36260                 scope: this,
36261                 argument: {callback: callback, node: node},
36262                 params: this.getParams(node)
36263             });
36264         }else{
36265             // if the load is cancelled, make sure we notify
36266             // the node that we are done
36267             if(typeof callback == "function"){
36268                 callback();
36269             }
36270         }
36271     },
36272
36273     isLoading : function(){
36274         return this.transId ? true : false;
36275     },
36276
36277     abort : function(){
36278         if(this.isLoading()){
36279             Roo.Ajax.abort(this.transId);
36280         }
36281     },
36282
36283     // private
36284     createNode : function(attr)
36285     {
36286         // apply baseAttrs, nice idea Corey!
36287         if(this.baseAttrs){
36288             Roo.applyIf(attr, this.baseAttrs);
36289         }
36290         if(this.applyLoader !== false){
36291             attr.loader = this;
36292         }
36293         // uiProvider = depreciated..
36294         
36295         if(typeof(attr.uiProvider) == 'string'){
36296            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
36297                 /**  eval:var:attr */ eval(attr.uiProvider);
36298         }
36299         if(typeof(this.uiProviders['default']) != 'undefined') {
36300             attr.uiProvider = this.uiProviders['default'];
36301         }
36302         
36303         this.fireEvent('create', this, attr);
36304         
36305         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
36306         return(attr.leaf ?
36307                         new Roo.tree.TreeNode(attr) :
36308                         new Roo.tree.AsyncTreeNode(attr));
36309     },
36310
36311     processResponse : function(response, node, callback)
36312     {
36313         var json = response.responseText;
36314         try {
36315             
36316             var o = Roo.decode(json);
36317             
36318             if (this.root === false && typeof(o.success) != undefined) {
36319                 this.root = 'data'; // the default behaviour for list like data..
36320                 }
36321                 
36322             if (this.root !== false &&  !o.success) {
36323                 // it's a failure condition.
36324                 var a = response.argument;
36325                 this.fireEvent("loadexception", this, a.node, response);
36326                 Roo.log("Load failed - should have a handler really");
36327                 return;
36328             }
36329             
36330             
36331             
36332             if (this.root !== false) {
36333                  o = o[this.root];
36334             }
36335             
36336             for(var i = 0, len = o.length; i < len; i++){
36337                 var n = this.createNode(o[i]);
36338                 if(n){
36339                     node.appendChild(n);
36340                 }
36341             }
36342             if(typeof callback == "function"){
36343                 callback(this, node);
36344             }
36345         }catch(e){
36346             this.handleFailure(response);
36347         }
36348     },
36349
36350     handleResponse : function(response){
36351         this.transId = false;
36352         var a = response.argument;
36353         this.processResponse(response, a.node, a.callback);
36354         this.fireEvent("load", this, a.node, response);
36355     },
36356
36357     handleFailure : function(response)
36358     {
36359         // should handle failure better..
36360         this.transId = false;
36361         var a = response.argument;
36362         this.fireEvent("loadexception", this, a.node, response);
36363         if(typeof a.callback == "function"){
36364             a.callback(this, a.node);
36365         }
36366     }
36367 });/*
36368  * Based on:
36369  * Ext JS Library 1.1.1
36370  * Copyright(c) 2006-2007, Ext JS, LLC.
36371  *
36372  * Originally Released Under LGPL - original licence link has changed is not relivant.
36373  *
36374  * Fork - LGPL
36375  * <script type="text/javascript">
36376  */
36377
36378 /**
36379 * @class Roo.tree.TreeFilter
36380 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
36381 * @param {TreePanel} tree
36382 * @param {Object} config (optional)
36383  */
36384 Roo.tree.TreeFilter = function(tree, config){
36385     this.tree = tree;
36386     this.filtered = {};
36387     Roo.apply(this, config);
36388 };
36389
36390 Roo.tree.TreeFilter.prototype = {
36391     clearBlank:false,
36392     reverse:false,
36393     autoClear:false,
36394     remove:false,
36395
36396      /**
36397      * Filter the data by a specific attribute.
36398      * @param {String/RegExp} value Either string that the attribute value
36399      * should start with or a RegExp to test against the attribute
36400      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
36401      * @param {TreeNode} startNode (optional) The node to start the filter at.
36402      */
36403     filter : function(value, attr, startNode){
36404         attr = attr || "text";
36405         var f;
36406         if(typeof value == "string"){
36407             var vlen = value.length;
36408             // auto clear empty filter
36409             if(vlen == 0 && this.clearBlank){
36410                 this.clear();
36411                 return;
36412             }
36413             value = value.toLowerCase();
36414             f = function(n){
36415                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
36416             };
36417         }else if(value.exec){ // regex?
36418             f = function(n){
36419                 return value.test(n.attributes[attr]);
36420             };
36421         }else{
36422             throw 'Illegal filter type, must be string or regex';
36423         }
36424         this.filterBy(f, null, startNode);
36425         },
36426
36427     /**
36428      * Filter by a function. The passed function will be called with each
36429      * node in the tree (or from the startNode). If the function returns true, the node is kept
36430      * otherwise it is filtered. If a node is filtered, its children are also filtered.
36431      * @param {Function} fn The filter function
36432      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
36433      */
36434     filterBy : function(fn, scope, startNode){
36435         startNode = startNode || this.tree.root;
36436         if(this.autoClear){
36437             this.clear();
36438         }
36439         var af = this.filtered, rv = this.reverse;
36440         var f = function(n){
36441             if(n == startNode){
36442                 return true;
36443             }
36444             if(af[n.id]){
36445                 return false;
36446             }
36447             var m = fn.call(scope || n, n);
36448             if(!m || rv){
36449                 af[n.id] = n;
36450                 n.ui.hide();
36451                 return false;
36452             }
36453             return true;
36454         };
36455         startNode.cascade(f);
36456         if(this.remove){
36457            for(var id in af){
36458                if(typeof id != "function"){
36459                    var n = af[id];
36460                    if(n && n.parentNode){
36461                        n.parentNode.removeChild(n);
36462                    }
36463                }
36464            }
36465         }
36466     },
36467
36468     /**
36469      * Clears the current filter. Note: with the "remove" option
36470      * set a filter cannot be cleared.
36471      */
36472     clear : function(){
36473         var t = this.tree;
36474         var af = this.filtered;
36475         for(var id in af){
36476             if(typeof id != "function"){
36477                 var n = af[id];
36478                 if(n){
36479                     n.ui.show();
36480                 }
36481             }
36482         }
36483         this.filtered = {};
36484     }
36485 };
36486 /*
36487  * Based on:
36488  * Ext JS Library 1.1.1
36489  * Copyright(c) 2006-2007, Ext JS, LLC.
36490  *
36491  * Originally Released Under LGPL - original licence link has changed is not relivant.
36492  *
36493  * Fork - LGPL
36494  * <script type="text/javascript">
36495  */
36496  
36497
36498 /**
36499  * @class Roo.tree.TreeSorter
36500  * Provides sorting of nodes in a TreePanel
36501  * 
36502  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
36503  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
36504  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
36505  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
36506  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
36507  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
36508  * @constructor
36509  * @param {TreePanel} tree
36510  * @param {Object} config
36511  */
36512 Roo.tree.TreeSorter = function(tree, config){
36513     Roo.apply(this, config);
36514     tree.on("beforechildrenrendered", this.doSort, this);
36515     tree.on("append", this.updateSort, this);
36516     tree.on("insert", this.updateSort, this);
36517     
36518     var dsc = this.dir && this.dir.toLowerCase() == "desc";
36519     var p = this.property || "text";
36520     var sortType = this.sortType;
36521     var fs = this.folderSort;
36522     var cs = this.caseSensitive === true;
36523     var leafAttr = this.leafAttr || 'leaf';
36524
36525     this.sortFn = function(n1, n2){
36526         if(fs){
36527             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
36528                 return 1;
36529             }
36530             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
36531                 return -1;
36532             }
36533         }
36534         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
36535         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
36536         if(v1 < v2){
36537                         return dsc ? +1 : -1;
36538                 }else if(v1 > v2){
36539                         return dsc ? -1 : +1;
36540         }else{
36541                 return 0;
36542         }
36543     };
36544 };
36545
36546 Roo.tree.TreeSorter.prototype = {
36547     doSort : function(node){
36548         node.sort(this.sortFn);
36549     },
36550     
36551     compareNodes : function(n1, n2){
36552         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
36553     },
36554     
36555     updateSort : function(tree, node){
36556         if(node.childrenRendered){
36557             this.doSort.defer(1, this, [node]);
36558         }
36559     }
36560 };/*
36561  * Based on:
36562  * Ext JS Library 1.1.1
36563  * Copyright(c) 2006-2007, Ext JS, LLC.
36564  *
36565  * Originally Released Under LGPL - original licence link has changed is not relivant.
36566  *
36567  * Fork - LGPL
36568  * <script type="text/javascript">
36569  */
36570
36571 if(Roo.dd.DropZone){
36572     
36573 Roo.tree.TreeDropZone = function(tree, config){
36574     this.allowParentInsert = false;
36575     this.allowContainerDrop = false;
36576     this.appendOnly = false;
36577     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
36578     this.tree = tree;
36579     this.lastInsertClass = "x-tree-no-status";
36580     this.dragOverData = {};
36581 };
36582
36583 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
36584     ddGroup : "TreeDD",
36585     scroll:  true,
36586     
36587     expandDelay : 1000,
36588     
36589     expandNode : function(node){
36590         if(node.hasChildNodes() && !node.isExpanded()){
36591             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
36592         }
36593     },
36594     
36595     queueExpand : function(node){
36596         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
36597     },
36598     
36599     cancelExpand : function(){
36600         if(this.expandProcId){
36601             clearTimeout(this.expandProcId);
36602             this.expandProcId = false;
36603         }
36604     },
36605     
36606     isValidDropPoint : function(n, pt, dd, e, data){
36607         if(!n || !data){ return false; }
36608         var targetNode = n.node;
36609         var dropNode = data.node;
36610         // default drop rules
36611         if(!(targetNode && targetNode.isTarget && pt)){
36612             return false;
36613         }
36614         if(pt == "append" && targetNode.allowChildren === false){
36615             return false;
36616         }
36617         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
36618             return false;
36619         }
36620         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
36621             return false;
36622         }
36623         // reuse the object
36624         var overEvent = this.dragOverData;
36625         overEvent.tree = this.tree;
36626         overEvent.target = targetNode;
36627         overEvent.data = data;
36628         overEvent.point = pt;
36629         overEvent.source = dd;
36630         overEvent.rawEvent = e;
36631         overEvent.dropNode = dropNode;
36632         overEvent.cancel = false;  
36633         var result = this.tree.fireEvent("nodedragover", overEvent);
36634         return overEvent.cancel === false && result !== false;
36635     },
36636     
36637     getDropPoint : function(e, n, dd)
36638     {
36639         var tn = n.node;
36640         if(tn.isRoot){
36641             return tn.allowChildren !== false ? "append" : false; // always append for root
36642         }
36643         var dragEl = n.ddel;
36644         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
36645         var y = Roo.lib.Event.getPageY(e);
36646         //var noAppend = tn.allowChildren === false || tn.isLeaf();
36647         
36648         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
36649         var noAppend = tn.allowChildren === false;
36650         if(this.appendOnly || tn.parentNode.allowChildren === false){
36651             return noAppend ? false : "append";
36652         }
36653         var noBelow = false;
36654         if(!this.allowParentInsert){
36655             noBelow = tn.hasChildNodes() && tn.isExpanded();
36656         }
36657         var q = (b - t) / (noAppend ? 2 : 3);
36658         if(y >= t && y < (t + q)){
36659             return "above";
36660         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
36661             return "below";
36662         }else{
36663             return "append";
36664         }
36665     },
36666     
36667     onNodeEnter : function(n, dd, e, data)
36668     {
36669         this.cancelExpand();
36670     },
36671     
36672     onNodeOver : function(n, dd, e, data)
36673     {
36674        
36675         var pt = this.getDropPoint(e, n, dd);
36676         var node = n.node;
36677         
36678         // auto node expand check
36679         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
36680             this.queueExpand(node);
36681         }else if(pt != "append"){
36682             this.cancelExpand();
36683         }
36684         
36685         // set the insert point style on the target node
36686         var returnCls = this.dropNotAllowed;
36687         if(this.isValidDropPoint(n, pt, dd, e, data)){
36688            if(pt){
36689                var el = n.ddel;
36690                var cls;
36691                if(pt == "above"){
36692                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
36693                    cls = "x-tree-drag-insert-above";
36694                }else if(pt == "below"){
36695                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
36696                    cls = "x-tree-drag-insert-below";
36697                }else{
36698                    returnCls = "x-tree-drop-ok-append";
36699                    cls = "x-tree-drag-append";
36700                }
36701                if(this.lastInsertClass != cls){
36702                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
36703                    this.lastInsertClass = cls;
36704                }
36705            }
36706        }
36707        return returnCls;
36708     },
36709     
36710     onNodeOut : function(n, dd, e, data){
36711         
36712         this.cancelExpand();
36713         this.removeDropIndicators(n);
36714     },
36715     
36716     onNodeDrop : function(n, dd, e, data){
36717         var point = this.getDropPoint(e, n, dd);
36718         var targetNode = n.node;
36719         targetNode.ui.startDrop();
36720         if(!this.isValidDropPoint(n, point, dd, e, data)){
36721             targetNode.ui.endDrop();
36722             return false;
36723         }
36724         // first try to find the drop node
36725         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
36726         var dropEvent = {
36727             tree : this.tree,
36728             target: targetNode,
36729             data: data,
36730             point: point,
36731             source: dd,
36732             rawEvent: e,
36733             dropNode: dropNode,
36734             cancel: !dropNode   
36735         };
36736         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
36737         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
36738             targetNode.ui.endDrop();
36739             return false;
36740         }
36741         // allow target changing
36742         targetNode = dropEvent.target;
36743         if(point == "append" && !targetNode.isExpanded()){
36744             targetNode.expand(false, null, function(){
36745                 this.completeDrop(dropEvent);
36746             }.createDelegate(this));
36747         }else{
36748             this.completeDrop(dropEvent);
36749         }
36750         return true;
36751     },
36752     
36753     completeDrop : function(de){
36754         var ns = de.dropNode, p = de.point, t = de.target;
36755         if(!(ns instanceof Array)){
36756             ns = [ns];
36757         }
36758         var n;
36759         for(var i = 0, len = ns.length; i < len; i++){
36760             n = ns[i];
36761             if(p == "above"){
36762                 t.parentNode.insertBefore(n, t);
36763             }else if(p == "below"){
36764                 t.parentNode.insertBefore(n, t.nextSibling);
36765             }else{
36766                 t.appendChild(n);
36767             }
36768         }
36769         n.ui.focus();
36770         if(this.tree.hlDrop){
36771             n.ui.highlight();
36772         }
36773         t.ui.endDrop();
36774         this.tree.fireEvent("nodedrop", de);
36775     },
36776     
36777     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
36778         if(this.tree.hlDrop){
36779             dropNode.ui.focus();
36780             dropNode.ui.highlight();
36781         }
36782         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
36783     },
36784     
36785     getTree : function(){
36786         return this.tree;
36787     },
36788     
36789     removeDropIndicators : function(n){
36790         if(n && n.ddel){
36791             var el = n.ddel;
36792             Roo.fly(el).removeClass([
36793                     "x-tree-drag-insert-above",
36794                     "x-tree-drag-insert-below",
36795                     "x-tree-drag-append"]);
36796             this.lastInsertClass = "_noclass";
36797         }
36798     },
36799     
36800     beforeDragDrop : function(target, e, id){
36801         this.cancelExpand();
36802         return true;
36803     },
36804     
36805     afterRepair : function(data){
36806         if(data && Roo.enableFx){
36807             data.node.ui.highlight();
36808         }
36809         this.hideProxy();
36810     } 
36811     
36812 });
36813
36814 }
36815 /*
36816  * Based on:
36817  * Ext JS Library 1.1.1
36818  * Copyright(c) 2006-2007, Ext JS, LLC.
36819  *
36820  * Originally Released Under LGPL - original licence link has changed is not relivant.
36821  *
36822  * Fork - LGPL
36823  * <script type="text/javascript">
36824  */
36825  
36826
36827 if(Roo.dd.DragZone){
36828 Roo.tree.TreeDragZone = function(tree, config){
36829     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
36830     this.tree = tree;
36831 };
36832
36833 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
36834     ddGroup : "TreeDD",
36835    
36836     onBeforeDrag : function(data, e){
36837         var n = data.node;
36838         return n && n.draggable && !n.disabled;
36839     },
36840      
36841     
36842     onInitDrag : function(e){
36843         var data = this.dragData;
36844         this.tree.getSelectionModel().select(data.node);
36845         this.proxy.update("");
36846         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
36847         this.tree.fireEvent("startdrag", this.tree, data.node, e);
36848     },
36849     
36850     getRepairXY : function(e, data){
36851         return data.node.ui.getDDRepairXY();
36852     },
36853     
36854     onEndDrag : function(data, e){
36855         this.tree.fireEvent("enddrag", this.tree, data.node, e);
36856         
36857         
36858     },
36859     
36860     onValidDrop : function(dd, e, id){
36861         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
36862         this.hideProxy();
36863     },
36864     
36865     beforeInvalidDrop : function(e, id){
36866         // this scrolls the original position back into view
36867         var sm = this.tree.getSelectionModel();
36868         sm.clearSelections();
36869         sm.select(this.dragData.node);
36870     }
36871 });
36872 }/*
36873  * Based on:
36874  * Ext JS Library 1.1.1
36875  * Copyright(c) 2006-2007, Ext JS, LLC.
36876  *
36877  * Originally Released Under LGPL - original licence link has changed is not relivant.
36878  *
36879  * Fork - LGPL
36880  * <script type="text/javascript">
36881  */
36882 /**
36883  * @class Roo.tree.TreeEditor
36884  * @extends Roo.Editor
36885  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
36886  * as the editor field.
36887  * @constructor
36888  * @param {Object} config (used to be the tree panel.)
36889  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
36890  * 
36891  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
36892  * @cfg {Roo.form.TextField|Object} field The field configuration
36893  *
36894  * 
36895  */
36896 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
36897     var tree = config;
36898     var field;
36899     if (oldconfig) { // old style..
36900         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
36901     } else {
36902         // new style..
36903         tree = config.tree;
36904         config.field = config.field  || {};
36905         config.field.xtype = 'TextField';
36906         field = Roo.factory(config.field, Roo.form);
36907     }
36908     config = config || {};
36909     
36910     
36911     this.addEvents({
36912         /**
36913          * @event beforenodeedit
36914          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
36915          * false from the handler of this event.
36916          * @param {Editor} this
36917          * @param {Roo.tree.Node} node 
36918          */
36919         "beforenodeedit" : true
36920     });
36921     
36922     //Roo.log(config);
36923     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
36924
36925     this.tree = tree;
36926
36927     tree.on('beforeclick', this.beforeNodeClick, this);
36928     tree.getTreeEl().on('mousedown', this.hide, this);
36929     this.on('complete', this.updateNode, this);
36930     this.on('beforestartedit', this.fitToTree, this);
36931     this.on('startedit', this.bindScroll, this, {delay:10});
36932     this.on('specialkey', this.onSpecialKey, this);
36933 };
36934
36935 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
36936     /**
36937      * @cfg {String} alignment
36938      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
36939      */
36940     alignment: "l-l",
36941     // inherit
36942     autoSize: false,
36943     /**
36944      * @cfg {Boolean} hideEl
36945      * True to hide the bound element while the editor is displayed (defaults to false)
36946      */
36947     hideEl : false,
36948     /**
36949      * @cfg {String} cls
36950      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
36951      */
36952     cls: "x-small-editor x-tree-editor",
36953     /**
36954      * @cfg {Boolean} shim
36955      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
36956      */
36957     shim:false,
36958     // inherit
36959     shadow:"frame",
36960     /**
36961      * @cfg {Number} maxWidth
36962      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
36963      * the containing tree element's size, it will be automatically limited for you to the container width, taking
36964      * scroll and client offsets into account prior to each edit.
36965      */
36966     maxWidth: 250,
36967
36968     editDelay : 350,
36969
36970     // private
36971     fitToTree : function(ed, el){
36972         var td = this.tree.getTreeEl().dom, nd = el.dom;
36973         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
36974             td.scrollLeft = nd.offsetLeft;
36975         }
36976         var w = Math.min(
36977                 this.maxWidth,
36978                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
36979         this.setSize(w, '');
36980         
36981         return this.fireEvent('beforenodeedit', this, this.editNode);
36982         
36983     },
36984
36985     // private
36986     triggerEdit : function(node){
36987         this.completeEdit();
36988         this.editNode = node;
36989         this.startEdit(node.ui.textNode, node.text);
36990     },
36991
36992     // private
36993     bindScroll : function(){
36994         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
36995     },
36996
36997     // private
36998     beforeNodeClick : function(node, e){
36999         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
37000         this.lastClick = new Date();
37001         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
37002             e.stopEvent();
37003             this.triggerEdit(node);
37004             return false;
37005         }
37006         return true;
37007     },
37008
37009     // private
37010     updateNode : function(ed, value){
37011         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
37012         this.editNode.setText(value);
37013     },
37014
37015     // private
37016     onHide : function(){
37017         Roo.tree.TreeEditor.superclass.onHide.call(this);
37018         if(this.editNode){
37019             this.editNode.ui.focus();
37020         }
37021     },
37022
37023     // private
37024     onSpecialKey : function(field, e){
37025         var k = e.getKey();
37026         if(k == e.ESC){
37027             e.stopEvent();
37028             this.cancelEdit();
37029         }else if(k == e.ENTER && !e.hasModifier()){
37030             e.stopEvent();
37031             this.completeEdit();
37032         }
37033     }
37034 });//<Script type="text/javascript">
37035 /*
37036  * Based on:
37037  * Ext JS Library 1.1.1
37038  * Copyright(c) 2006-2007, Ext JS, LLC.
37039  *
37040  * Originally Released Under LGPL - original licence link has changed is not relivant.
37041  *
37042  * Fork - LGPL
37043  * <script type="text/javascript">
37044  */
37045  
37046 /**
37047  * Not documented??? - probably should be...
37048  */
37049
37050 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
37051     //focus: Roo.emptyFn, // prevent odd scrolling behavior
37052     
37053     renderElements : function(n, a, targetNode, bulkRender){
37054         //consel.log("renderElements?");
37055         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
37056
37057         var t = n.getOwnerTree();
37058         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
37059         
37060         var cols = t.columns;
37061         var bw = t.borderWidth;
37062         var c = cols[0];
37063         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
37064          var cb = typeof a.checked == "boolean";
37065         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
37066         var colcls = 'x-t-' + tid + '-c0';
37067         var buf = [
37068             '<li class="x-tree-node">',
37069             
37070                 
37071                 '<div class="x-tree-node-el ', a.cls,'">',
37072                     // extran...
37073                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
37074                 
37075                 
37076                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
37077                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
37078                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
37079                            (a.icon ? ' x-tree-node-inline-icon' : ''),
37080                            (a.iconCls ? ' '+a.iconCls : ''),
37081                            '" unselectable="on" />',
37082                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
37083                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
37084                              
37085                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
37086                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
37087                             '<span unselectable="on" qtip="' + tx + '">',
37088                              tx,
37089                              '</span></a>' ,
37090                     '</div>',
37091                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
37092                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
37093                  ];
37094         for(var i = 1, len = cols.length; i < len; i++){
37095             c = cols[i];
37096             colcls = 'x-t-' + tid + '-c' +i;
37097             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
37098             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
37099                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
37100                       "</div>");
37101          }
37102          
37103          buf.push(
37104             '</a>',
37105             '<div class="x-clear"></div></div>',
37106             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
37107             "</li>");
37108         
37109         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
37110             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
37111                                 n.nextSibling.ui.getEl(), buf.join(""));
37112         }else{
37113             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
37114         }
37115         var el = this.wrap.firstChild;
37116         this.elRow = el;
37117         this.elNode = el.firstChild;
37118         this.ranchor = el.childNodes[1];
37119         this.ctNode = this.wrap.childNodes[1];
37120         var cs = el.firstChild.childNodes;
37121         this.indentNode = cs[0];
37122         this.ecNode = cs[1];
37123         this.iconNode = cs[2];
37124         var index = 3;
37125         if(cb){
37126             this.checkbox = cs[3];
37127             index++;
37128         }
37129         this.anchor = cs[index];
37130         
37131         this.textNode = cs[index].firstChild;
37132         
37133         //el.on("click", this.onClick, this);
37134         //el.on("dblclick", this.onDblClick, this);
37135         
37136         
37137        // console.log(this);
37138     },
37139     initEvents : function(){
37140         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
37141         
37142             
37143         var a = this.ranchor;
37144
37145         var el = Roo.get(a);
37146
37147         if(Roo.isOpera){ // opera render bug ignores the CSS
37148             el.setStyle("text-decoration", "none");
37149         }
37150
37151         el.on("click", this.onClick, this);
37152         el.on("dblclick", this.onDblClick, this);
37153         el.on("contextmenu", this.onContextMenu, this);
37154         
37155     },
37156     
37157     /*onSelectedChange : function(state){
37158         if(state){
37159             this.focus();
37160             this.addClass("x-tree-selected");
37161         }else{
37162             //this.blur();
37163             this.removeClass("x-tree-selected");
37164         }
37165     },*/
37166     addClass : function(cls){
37167         if(this.elRow){
37168             Roo.fly(this.elRow).addClass(cls);
37169         }
37170         
37171     },
37172     
37173     
37174     removeClass : function(cls){
37175         if(this.elRow){
37176             Roo.fly(this.elRow).removeClass(cls);
37177         }
37178     }
37179
37180     
37181     
37182 });//<Script type="text/javascript">
37183
37184 /*
37185  * Based on:
37186  * Ext JS Library 1.1.1
37187  * Copyright(c) 2006-2007, Ext JS, LLC.
37188  *
37189  * Originally Released Under LGPL - original licence link has changed is not relivant.
37190  *
37191  * Fork - LGPL
37192  * <script type="text/javascript">
37193  */
37194  
37195
37196 /**
37197  * @class Roo.tree.ColumnTree
37198  * @extends Roo.data.TreePanel
37199  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
37200  * @cfg {int} borderWidth  compined right/left border allowance
37201  * @constructor
37202  * @param {String/HTMLElement/Element} el The container element
37203  * @param {Object} config
37204  */
37205 Roo.tree.ColumnTree =  function(el, config)
37206 {
37207    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
37208    this.addEvents({
37209         /**
37210         * @event resize
37211         * Fire this event on a container when it resizes
37212         * @param {int} w Width
37213         * @param {int} h Height
37214         */
37215        "resize" : true
37216     });
37217     this.on('resize', this.onResize, this);
37218 };
37219
37220 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
37221     //lines:false,
37222     
37223     
37224     borderWidth: Roo.isBorderBox ? 0 : 2, 
37225     headEls : false,
37226     
37227     render : function(){
37228         // add the header.....
37229        
37230         Roo.tree.ColumnTree.superclass.render.apply(this);
37231         
37232         this.el.addClass('x-column-tree');
37233         
37234         this.headers = this.el.createChild(
37235             {cls:'x-tree-headers'},this.innerCt.dom);
37236    
37237         var cols = this.columns, c;
37238         var totalWidth = 0;
37239         this.headEls = [];
37240         var  len = cols.length;
37241         for(var i = 0; i < len; i++){
37242              c = cols[i];
37243              totalWidth += c.width;
37244             this.headEls.push(this.headers.createChild({
37245                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
37246                  cn: {
37247                      cls:'x-tree-hd-text',
37248                      html: c.header
37249                  },
37250                  style:'width:'+(c.width-this.borderWidth)+'px;'
37251              }));
37252         }
37253         this.headers.createChild({cls:'x-clear'});
37254         // prevent floats from wrapping when clipped
37255         this.headers.setWidth(totalWidth);
37256         //this.innerCt.setWidth(totalWidth);
37257         this.innerCt.setStyle({ overflow: 'auto' });
37258         this.onResize(this.width, this.height);
37259              
37260         
37261     },
37262     onResize : function(w,h)
37263     {
37264         this.height = h;
37265         this.width = w;
37266         // resize cols..
37267         this.innerCt.setWidth(this.width);
37268         this.innerCt.setHeight(this.height-20);
37269         
37270         // headers...
37271         var cols = this.columns, c;
37272         var totalWidth = 0;
37273         var expEl = false;
37274         var len = cols.length;
37275         for(var i = 0; i < len; i++){
37276             c = cols[i];
37277             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
37278                 // it's the expander..
37279                 expEl  = this.headEls[i];
37280                 continue;
37281             }
37282             totalWidth += c.width;
37283             
37284         }
37285         if (expEl) {
37286             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
37287         }
37288         this.headers.setWidth(w-20);
37289
37290         
37291         
37292         
37293     }
37294 });
37295 /*
37296  * Based on:
37297  * Ext JS Library 1.1.1
37298  * Copyright(c) 2006-2007, Ext JS, LLC.
37299  *
37300  * Originally Released Under LGPL - original licence link has changed is not relivant.
37301  *
37302  * Fork - LGPL
37303  * <script type="text/javascript">
37304  */
37305  
37306 /**
37307  * @class Roo.menu.Menu
37308  * @extends Roo.util.Observable
37309  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
37310  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
37311  * @constructor
37312  * Creates a new Menu
37313  * @param {Object} config Configuration options
37314  */
37315 Roo.menu.Menu = function(config){
37316     
37317     Roo.menu.Menu.superclass.constructor.call(this, config);
37318     
37319     this.id = this.id || Roo.id();
37320     this.addEvents({
37321         /**
37322          * @event beforeshow
37323          * Fires before this menu is displayed
37324          * @param {Roo.menu.Menu} this
37325          */
37326         beforeshow : true,
37327         /**
37328          * @event beforehide
37329          * Fires before this menu is hidden
37330          * @param {Roo.menu.Menu} this
37331          */
37332         beforehide : true,
37333         /**
37334          * @event show
37335          * Fires after this menu is displayed
37336          * @param {Roo.menu.Menu} this
37337          */
37338         show : true,
37339         /**
37340          * @event hide
37341          * Fires after this menu is hidden
37342          * @param {Roo.menu.Menu} this
37343          */
37344         hide : true,
37345         /**
37346          * @event click
37347          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
37348          * @param {Roo.menu.Menu} this
37349          * @param {Roo.menu.Item} menuItem The menu item that was clicked
37350          * @param {Roo.EventObject} e
37351          */
37352         click : true,
37353         /**
37354          * @event mouseover
37355          * Fires when the mouse is hovering over this menu
37356          * @param {Roo.menu.Menu} this
37357          * @param {Roo.EventObject} e
37358          * @param {Roo.menu.Item} menuItem The menu item that was clicked
37359          */
37360         mouseover : true,
37361         /**
37362          * @event mouseout
37363          * Fires when the mouse exits this menu
37364          * @param {Roo.menu.Menu} this
37365          * @param {Roo.EventObject} e
37366          * @param {Roo.menu.Item} menuItem The menu item that was clicked
37367          */
37368         mouseout : true,
37369         /**
37370          * @event itemclick
37371          * Fires when a menu item contained in this menu is clicked
37372          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
37373          * @param {Roo.EventObject} e
37374          */
37375         itemclick: true
37376     });
37377     if (this.registerMenu) {
37378         Roo.menu.MenuMgr.register(this);
37379     }
37380     
37381     var mis = this.items;
37382     this.items = new Roo.util.MixedCollection();
37383     if(mis){
37384         this.add.apply(this, mis);
37385     }
37386 };
37387
37388 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
37389     /**
37390      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
37391      */
37392     minWidth : 120,
37393     /**
37394      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
37395      * for bottom-right shadow (defaults to "sides")
37396      */
37397     shadow : "sides",
37398     /**
37399      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
37400      * this menu (defaults to "tl-tr?")
37401      */
37402     subMenuAlign : "tl-tr?",
37403     /**
37404      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
37405      * relative to its element of origin (defaults to "tl-bl?")
37406      */
37407     defaultAlign : "tl-bl?",
37408     /**
37409      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
37410      */
37411     allowOtherMenus : false,
37412     /**
37413      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
37414      */
37415     registerMenu : true,
37416
37417     hidden:true,
37418
37419     // private
37420     render : function(){
37421         if(this.el){
37422             return;
37423         }
37424         var el = this.el = new Roo.Layer({
37425             cls: "x-menu",
37426             shadow:this.shadow,
37427             constrain: false,
37428             parentEl: this.parentEl || document.body,
37429             zindex:15000
37430         });
37431
37432         this.keyNav = new Roo.menu.MenuNav(this);
37433
37434         if(this.plain){
37435             el.addClass("x-menu-plain");
37436         }
37437         if(this.cls){
37438             el.addClass(this.cls);
37439         }
37440         // generic focus element
37441         this.focusEl = el.createChild({
37442             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
37443         });
37444         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
37445         //disabling touch- as it's causing issues ..
37446         //ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
37447         ul.on('click'   , this.onClick, this);
37448         
37449         
37450         ul.on("mouseover", this.onMouseOver, this);
37451         ul.on("mouseout", this.onMouseOut, this);
37452         this.items.each(function(item){
37453             if (item.hidden) {
37454                 return;
37455             }
37456             
37457             var li = document.createElement("li");
37458             li.className = "x-menu-list-item";
37459             ul.dom.appendChild(li);
37460             item.render(li, this);
37461         }, this);
37462         this.ul = ul;
37463         this.autoWidth();
37464     },
37465
37466     // private
37467     autoWidth : function(){
37468         var el = this.el, ul = this.ul;
37469         if(!el){
37470             return;
37471         }
37472         var w = this.width;
37473         if(w){
37474             el.setWidth(w);
37475         }else if(Roo.isIE){
37476             el.setWidth(this.minWidth);
37477             var t = el.dom.offsetWidth; // force recalc
37478             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
37479         }
37480     },
37481
37482     // private
37483     delayAutoWidth : function(){
37484         if(this.rendered){
37485             if(!this.awTask){
37486                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
37487             }
37488             this.awTask.delay(20);
37489         }
37490     },
37491
37492     // private
37493     findTargetItem : function(e){
37494         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
37495         if(t && t.menuItemId){
37496             return this.items.get(t.menuItemId);
37497         }
37498     },
37499
37500     // private
37501     onClick : function(e){
37502         Roo.log("menu.onClick");
37503         var t = this.findTargetItem(e);
37504         if(!t){
37505             return;
37506         }
37507         Roo.log(e);
37508         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
37509             if(t == this.activeItem && t.shouldDeactivate(e)){
37510                 this.activeItem.deactivate();
37511                 delete this.activeItem;
37512                 return;
37513             }
37514             if(t.canActivate){
37515                 this.setActiveItem(t, true);
37516             }
37517             return;
37518             
37519             
37520         }
37521         
37522         t.onClick(e);
37523         this.fireEvent("click", this, t, e);
37524     },
37525
37526     // private
37527     setActiveItem : function(item, autoExpand){
37528         if(item != this.activeItem){
37529             if(this.activeItem){
37530                 this.activeItem.deactivate();
37531             }
37532             this.activeItem = item;
37533             item.activate(autoExpand);
37534         }else if(autoExpand){
37535             item.expandMenu();
37536         }
37537     },
37538
37539     // private
37540     tryActivate : function(start, step){
37541         var items = this.items;
37542         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
37543             var item = items.get(i);
37544             if(!item.disabled && item.canActivate){
37545                 this.setActiveItem(item, false);
37546                 return item;
37547             }
37548         }
37549         return false;
37550     },
37551
37552     // private
37553     onMouseOver : function(e){
37554         var t;
37555         if(t = this.findTargetItem(e)){
37556             if(t.canActivate && !t.disabled){
37557                 this.setActiveItem(t, true);
37558             }
37559         }
37560         this.fireEvent("mouseover", this, e, t);
37561     },
37562
37563     // private
37564     onMouseOut : function(e){
37565         var t;
37566         if(t = this.findTargetItem(e)){
37567             if(t == this.activeItem && t.shouldDeactivate(e)){
37568                 this.activeItem.deactivate();
37569                 delete this.activeItem;
37570             }
37571         }
37572         this.fireEvent("mouseout", this, e, t);
37573     },
37574
37575     /**
37576      * Read-only.  Returns true if the menu is currently displayed, else false.
37577      * @type Boolean
37578      */
37579     isVisible : function(){
37580         return this.el && !this.hidden;
37581     },
37582
37583     /**
37584      * Displays this menu relative to another element
37585      * @param {String/HTMLElement/Roo.Element} element The element to align to
37586      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
37587      * the element (defaults to this.defaultAlign)
37588      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
37589      */
37590     show : function(el, pos, parentMenu){
37591         this.parentMenu = parentMenu;
37592         if(!this.el){
37593             this.render();
37594         }
37595         this.fireEvent("beforeshow", this);
37596         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
37597     },
37598
37599     /**
37600      * Displays this menu at a specific xy position
37601      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
37602      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
37603      */
37604     showAt : function(xy, parentMenu, /* private: */_e){
37605         this.parentMenu = parentMenu;
37606         if(!this.el){
37607             this.render();
37608         }
37609         if(_e !== false){
37610             this.fireEvent("beforeshow", this);
37611             xy = this.el.adjustForConstraints(xy);
37612         }
37613         this.el.setXY(xy);
37614         this.el.show();
37615         this.hidden = false;
37616         this.focus();
37617         this.fireEvent("show", this);
37618     },
37619
37620     focus : function(){
37621         if(!this.hidden){
37622             this.doFocus.defer(50, this);
37623         }
37624     },
37625
37626     doFocus : function(){
37627         if(!this.hidden){
37628             this.focusEl.focus();
37629         }
37630     },
37631
37632     /**
37633      * Hides this menu and optionally all parent menus
37634      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
37635      */
37636     hide : function(deep){
37637         if(this.el && this.isVisible()){
37638             this.fireEvent("beforehide", this);
37639             if(this.activeItem){
37640                 this.activeItem.deactivate();
37641                 this.activeItem = null;
37642             }
37643             this.el.hide();
37644             this.hidden = true;
37645             this.fireEvent("hide", this);
37646         }
37647         if(deep === true && this.parentMenu){
37648             this.parentMenu.hide(true);
37649         }
37650     },
37651
37652     /**
37653      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
37654      * Any of the following are valid:
37655      * <ul>
37656      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
37657      * <li>An HTMLElement object which will be converted to a menu item</li>
37658      * <li>A menu item config object that will be created as a new menu item</li>
37659      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
37660      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
37661      * </ul>
37662      * Usage:
37663      * <pre><code>
37664 // Create the menu
37665 var menu = new Roo.menu.Menu();
37666
37667 // Create a menu item to add by reference
37668 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
37669
37670 // Add a bunch of items at once using different methods.
37671 // Only the last item added will be returned.
37672 var item = menu.add(
37673     menuItem,                // add existing item by ref
37674     'Dynamic Item',          // new TextItem
37675     '-',                     // new separator
37676     { text: 'Config Item' }  // new item by config
37677 );
37678 </code></pre>
37679      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
37680      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
37681      */
37682     add : function(){
37683         var a = arguments, l = a.length, item;
37684         for(var i = 0; i < l; i++){
37685             var el = a[i];
37686             if ((typeof(el) == "object") && el.xtype && el.xns) {
37687                 el = Roo.factory(el, Roo.menu);
37688             }
37689             
37690             if(el.render){ // some kind of Item
37691                 item = this.addItem(el);
37692             }else if(typeof el == "string"){ // string
37693                 if(el == "separator" || el == "-"){
37694                     item = this.addSeparator();
37695                 }else{
37696                     item = this.addText(el);
37697                 }
37698             }else if(el.tagName || el.el){ // element
37699                 item = this.addElement(el);
37700             }else if(typeof el == "object"){ // must be menu item config?
37701                 item = this.addMenuItem(el);
37702             }
37703         }
37704         return item;
37705     },
37706
37707     /**
37708      * Returns this menu's underlying {@link Roo.Element} object
37709      * @return {Roo.Element} The element
37710      */
37711     getEl : function(){
37712         if(!this.el){
37713             this.render();
37714         }
37715         return this.el;
37716     },
37717
37718     /**
37719      * Adds a separator bar to the menu
37720      * @return {Roo.menu.Item} The menu item that was added
37721      */
37722     addSeparator : function(){
37723         return this.addItem(new Roo.menu.Separator());
37724     },
37725
37726     /**
37727      * Adds an {@link Roo.Element} object to the menu
37728      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
37729      * @return {Roo.menu.Item} The menu item that was added
37730      */
37731     addElement : function(el){
37732         return this.addItem(new Roo.menu.BaseItem(el));
37733     },
37734
37735     /**
37736      * Adds an existing object based on {@link Roo.menu.Item} to the menu
37737      * @param {Roo.menu.Item} item The menu item to add
37738      * @return {Roo.menu.Item} The menu item that was added
37739      */
37740     addItem : function(item){
37741         this.items.add(item);
37742         if(this.ul){
37743             var li = document.createElement("li");
37744             li.className = "x-menu-list-item";
37745             this.ul.dom.appendChild(li);
37746             item.render(li, this);
37747             this.delayAutoWidth();
37748         }
37749         return item;
37750     },
37751
37752     /**
37753      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
37754      * @param {Object} config A MenuItem config object
37755      * @return {Roo.menu.Item} The menu item that was added
37756      */
37757     addMenuItem : function(config){
37758         if(!(config instanceof Roo.menu.Item)){
37759             if(typeof config.checked == "boolean"){ // must be check menu item config?
37760                 config = new Roo.menu.CheckItem(config);
37761             }else{
37762                 config = new Roo.menu.Item(config);
37763             }
37764         }
37765         return this.addItem(config);
37766     },
37767
37768     /**
37769      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
37770      * @param {String} text The text to display in the menu item
37771      * @return {Roo.menu.Item} The menu item that was added
37772      */
37773     addText : function(text){
37774         return this.addItem(new Roo.menu.TextItem({ text : text }));
37775     },
37776
37777     /**
37778      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
37779      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
37780      * @param {Roo.menu.Item} item The menu item to add
37781      * @return {Roo.menu.Item} The menu item that was added
37782      */
37783     insert : function(index, item){
37784         this.items.insert(index, item);
37785         if(this.ul){
37786             var li = document.createElement("li");
37787             li.className = "x-menu-list-item";
37788             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
37789             item.render(li, this);
37790             this.delayAutoWidth();
37791         }
37792         return item;
37793     },
37794
37795     /**
37796      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
37797      * @param {Roo.menu.Item} item The menu item to remove
37798      */
37799     remove : function(item){
37800         this.items.removeKey(item.id);
37801         item.destroy();
37802     },
37803
37804     /**
37805      * Removes and destroys all items in the menu
37806      */
37807     removeAll : function(){
37808         var f;
37809         while(f = this.items.first()){
37810             this.remove(f);
37811         }
37812     }
37813 });
37814
37815 // MenuNav is a private utility class used internally by the Menu
37816 Roo.menu.MenuNav = function(menu){
37817     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
37818     this.scope = this.menu = menu;
37819 };
37820
37821 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
37822     doRelay : function(e, h){
37823         var k = e.getKey();
37824         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
37825             this.menu.tryActivate(0, 1);
37826             return false;
37827         }
37828         return h.call(this.scope || this, e, this.menu);
37829     },
37830
37831     up : function(e, m){
37832         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
37833             m.tryActivate(m.items.length-1, -1);
37834         }
37835     },
37836
37837     down : function(e, m){
37838         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
37839             m.tryActivate(0, 1);
37840         }
37841     },
37842
37843     right : function(e, m){
37844         if(m.activeItem){
37845             m.activeItem.expandMenu(true);
37846         }
37847     },
37848
37849     left : function(e, m){
37850         m.hide();
37851         if(m.parentMenu && m.parentMenu.activeItem){
37852             m.parentMenu.activeItem.activate();
37853         }
37854     },
37855
37856     enter : function(e, m){
37857         if(m.activeItem){
37858             e.stopPropagation();
37859             m.activeItem.onClick(e);
37860             m.fireEvent("click", this, m.activeItem);
37861             return true;
37862         }
37863     }
37864 });/*
37865  * Based on:
37866  * Ext JS Library 1.1.1
37867  * Copyright(c) 2006-2007, Ext JS, LLC.
37868  *
37869  * Originally Released Under LGPL - original licence link has changed is not relivant.
37870  *
37871  * Fork - LGPL
37872  * <script type="text/javascript">
37873  */
37874  
37875 /**
37876  * @class Roo.menu.MenuMgr
37877  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
37878  * @singleton
37879  */
37880 Roo.menu.MenuMgr = function(){
37881    var menus, active, groups = {}, attached = false, lastShow = new Date();
37882
37883    // private - called when first menu is created
37884    function init(){
37885        menus = {};
37886        active = new Roo.util.MixedCollection();
37887        Roo.get(document).addKeyListener(27, function(){
37888            if(active.length > 0){
37889                hideAll();
37890            }
37891        });
37892    }
37893
37894    // private
37895    function hideAll(){
37896        if(active && active.length > 0){
37897            var c = active.clone();
37898            c.each(function(m){
37899                m.hide();
37900            });
37901        }
37902    }
37903
37904    // private
37905    function onHide(m){
37906        active.remove(m);
37907        if(active.length < 1){
37908            Roo.get(document).un("mousedown", onMouseDown);
37909            attached = false;
37910        }
37911    }
37912
37913    // private
37914    function onShow(m){
37915        var last = active.last();
37916        lastShow = new Date();
37917        active.add(m);
37918        if(!attached){
37919            Roo.get(document).on("mousedown", onMouseDown);
37920            attached = true;
37921        }
37922        if(m.parentMenu){
37923           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
37924           m.parentMenu.activeChild = m;
37925        }else if(last && last.isVisible()){
37926           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
37927        }
37928    }
37929
37930    // private
37931    function onBeforeHide(m){
37932        if(m.activeChild){
37933            m.activeChild.hide();
37934        }
37935        if(m.autoHideTimer){
37936            clearTimeout(m.autoHideTimer);
37937            delete m.autoHideTimer;
37938        }
37939    }
37940
37941    // private
37942    function onBeforeShow(m){
37943        var pm = m.parentMenu;
37944        if(!pm && !m.allowOtherMenus){
37945            hideAll();
37946        }else if(pm && pm.activeChild && active != m){
37947            pm.activeChild.hide();
37948        }
37949    }
37950
37951    // private
37952    function onMouseDown(e){
37953        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
37954            hideAll();
37955        }
37956    }
37957
37958    // private
37959    function onBeforeCheck(mi, state){
37960        if(state){
37961            var g = groups[mi.group];
37962            for(var i = 0, l = g.length; i < l; i++){
37963                if(g[i] != mi){
37964                    g[i].setChecked(false);
37965                }
37966            }
37967        }
37968    }
37969
37970    return {
37971
37972        /**
37973         * Hides all menus that are currently visible
37974         */
37975        hideAll : function(){
37976             hideAll();  
37977        },
37978
37979        // private
37980        register : function(menu){
37981            if(!menus){
37982                init();
37983            }
37984            menus[menu.id] = menu;
37985            menu.on("beforehide", onBeforeHide);
37986            menu.on("hide", onHide);
37987            menu.on("beforeshow", onBeforeShow);
37988            menu.on("show", onShow);
37989            var g = menu.group;
37990            if(g && menu.events["checkchange"]){
37991                if(!groups[g]){
37992                    groups[g] = [];
37993                }
37994                groups[g].push(menu);
37995                menu.on("checkchange", onCheck);
37996            }
37997        },
37998
37999         /**
38000          * Returns a {@link Roo.menu.Menu} object
38001          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
38002          * be used to generate and return a new Menu instance.
38003          */
38004        get : function(menu){
38005            if(typeof menu == "string"){ // menu id
38006                return menus[menu];
38007            }else if(menu.events){  // menu instance
38008                return menu;
38009            }else if(typeof menu.length == 'number'){ // array of menu items?
38010                return new Roo.menu.Menu({items:menu});
38011            }else{ // otherwise, must be a config
38012                return new Roo.menu.Menu(menu);
38013            }
38014        },
38015
38016        // private
38017        unregister : function(menu){
38018            delete menus[menu.id];
38019            menu.un("beforehide", onBeforeHide);
38020            menu.un("hide", onHide);
38021            menu.un("beforeshow", onBeforeShow);
38022            menu.un("show", onShow);
38023            var g = menu.group;
38024            if(g && menu.events["checkchange"]){
38025                groups[g].remove(menu);
38026                menu.un("checkchange", onCheck);
38027            }
38028        },
38029
38030        // private
38031        registerCheckable : function(menuItem){
38032            var g = menuItem.group;
38033            if(g){
38034                if(!groups[g]){
38035                    groups[g] = [];
38036                }
38037                groups[g].push(menuItem);
38038                menuItem.on("beforecheckchange", onBeforeCheck);
38039            }
38040        },
38041
38042        // private
38043        unregisterCheckable : function(menuItem){
38044            var g = menuItem.group;
38045            if(g){
38046                groups[g].remove(menuItem);
38047                menuItem.un("beforecheckchange", onBeforeCheck);
38048            }
38049        }
38050    };
38051 }();/*
38052  * Based on:
38053  * Ext JS Library 1.1.1
38054  * Copyright(c) 2006-2007, Ext JS, LLC.
38055  *
38056  * Originally Released Under LGPL - original licence link has changed is not relivant.
38057  *
38058  * Fork - LGPL
38059  * <script type="text/javascript">
38060  */
38061  
38062
38063 /**
38064  * @class Roo.menu.BaseItem
38065  * @extends Roo.Component
38066  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
38067  * management and base configuration options shared by all menu components.
38068  * @constructor
38069  * Creates a new BaseItem
38070  * @param {Object} config Configuration options
38071  */
38072 Roo.menu.BaseItem = function(config){
38073     Roo.menu.BaseItem.superclass.constructor.call(this, config);
38074
38075     this.addEvents({
38076         /**
38077          * @event click
38078          * Fires when this item is clicked
38079          * @param {Roo.menu.BaseItem} this
38080          * @param {Roo.EventObject} e
38081          */
38082         click: true,
38083         /**
38084          * @event activate
38085          * Fires when this item is activated
38086          * @param {Roo.menu.BaseItem} this
38087          */
38088         activate : true,
38089         /**
38090          * @event deactivate
38091          * Fires when this item is deactivated
38092          * @param {Roo.menu.BaseItem} this
38093          */
38094         deactivate : true
38095     });
38096
38097     if(this.handler){
38098         this.on("click", this.handler, this.scope, true);
38099     }
38100 };
38101
38102 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
38103     /**
38104      * @cfg {Function} handler
38105      * A function that will handle the click event of this menu item (defaults to undefined)
38106      */
38107     /**
38108      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
38109      */
38110     canActivate : false,
38111     
38112      /**
38113      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
38114      */
38115     hidden: false,
38116     
38117     /**
38118      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
38119      */
38120     activeClass : "x-menu-item-active",
38121     /**
38122      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
38123      */
38124     hideOnClick : true,
38125     /**
38126      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
38127      */
38128     hideDelay : 100,
38129
38130     // private
38131     ctype: "Roo.menu.BaseItem",
38132
38133     // private
38134     actionMode : "container",
38135
38136     // private
38137     render : function(container, parentMenu){
38138         this.parentMenu = parentMenu;
38139         Roo.menu.BaseItem.superclass.render.call(this, container);
38140         this.container.menuItemId = this.id;
38141     },
38142
38143     // private
38144     onRender : function(container, position){
38145         this.el = Roo.get(this.el);
38146         container.dom.appendChild(this.el.dom);
38147     },
38148
38149     // private
38150     onClick : function(e){
38151         if(!this.disabled && this.fireEvent("click", this, e) !== false
38152                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
38153             this.handleClick(e);
38154         }else{
38155             e.stopEvent();
38156         }
38157     },
38158
38159     // private
38160     activate : function(){
38161         if(this.disabled){
38162             return false;
38163         }
38164         var li = this.container;
38165         li.addClass(this.activeClass);
38166         this.region = li.getRegion().adjust(2, 2, -2, -2);
38167         this.fireEvent("activate", this);
38168         return true;
38169     },
38170
38171     // private
38172     deactivate : function(){
38173         this.container.removeClass(this.activeClass);
38174         this.fireEvent("deactivate", this);
38175     },
38176
38177     // private
38178     shouldDeactivate : function(e){
38179         return !this.region || !this.region.contains(e.getPoint());
38180     },
38181
38182     // private
38183     handleClick : function(e){
38184         if(this.hideOnClick){
38185             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
38186         }
38187     },
38188
38189     // private
38190     expandMenu : function(autoActivate){
38191         // do nothing
38192     },
38193
38194     // private
38195     hideMenu : function(){
38196         // do nothing
38197     }
38198 });/*
38199  * Based on:
38200  * Ext JS Library 1.1.1
38201  * Copyright(c) 2006-2007, Ext JS, LLC.
38202  *
38203  * Originally Released Under LGPL - original licence link has changed is not relivant.
38204  *
38205  * Fork - LGPL
38206  * <script type="text/javascript">
38207  */
38208  
38209 /**
38210  * @class Roo.menu.Adapter
38211  * @extends Roo.menu.BaseItem
38212  * 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.
38213  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
38214  * @constructor
38215  * Creates a new Adapter
38216  * @param {Object} config Configuration options
38217  */
38218 Roo.menu.Adapter = function(component, config){
38219     Roo.menu.Adapter.superclass.constructor.call(this, config);
38220     this.component = component;
38221 };
38222 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
38223     // private
38224     canActivate : true,
38225
38226     // private
38227     onRender : function(container, position){
38228         this.component.render(container);
38229         this.el = this.component.getEl();
38230     },
38231
38232     // private
38233     activate : function(){
38234         if(this.disabled){
38235             return false;
38236         }
38237         this.component.focus();
38238         this.fireEvent("activate", this);
38239         return true;
38240     },
38241
38242     // private
38243     deactivate : function(){
38244         this.fireEvent("deactivate", this);
38245     },
38246
38247     // private
38248     disable : function(){
38249         this.component.disable();
38250         Roo.menu.Adapter.superclass.disable.call(this);
38251     },
38252
38253     // private
38254     enable : function(){
38255         this.component.enable();
38256         Roo.menu.Adapter.superclass.enable.call(this);
38257     }
38258 });/*
38259  * Based on:
38260  * Ext JS Library 1.1.1
38261  * Copyright(c) 2006-2007, Ext JS, LLC.
38262  *
38263  * Originally Released Under LGPL - original licence link has changed is not relivant.
38264  *
38265  * Fork - LGPL
38266  * <script type="text/javascript">
38267  */
38268
38269 /**
38270  * @class Roo.menu.TextItem
38271  * @extends Roo.menu.BaseItem
38272  * Adds a static text string to a menu, usually used as either a heading or group separator.
38273  * Note: old style constructor with text is still supported.
38274  * 
38275  * @constructor
38276  * Creates a new TextItem
38277  * @param {Object} cfg Configuration
38278  */
38279 Roo.menu.TextItem = function(cfg){
38280     if (typeof(cfg) == 'string') {
38281         this.text = cfg;
38282     } else {
38283         Roo.apply(this,cfg);
38284     }
38285     
38286     Roo.menu.TextItem.superclass.constructor.call(this);
38287 };
38288
38289 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
38290     /**
38291      * @cfg {Boolean} text Text to show on item.
38292      */
38293     text : '',
38294     
38295     /**
38296      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
38297      */
38298     hideOnClick : false,
38299     /**
38300      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
38301      */
38302     itemCls : "x-menu-text",
38303
38304     // private
38305     onRender : function(){
38306         var s = document.createElement("span");
38307         s.className = this.itemCls;
38308         s.innerHTML = this.text;
38309         this.el = s;
38310         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
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.Separator
38325  * @extends Roo.menu.BaseItem
38326  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
38327  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
38328  * @constructor
38329  * @param {Object} config Configuration options
38330  */
38331 Roo.menu.Separator = function(config){
38332     Roo.menu.Separator.superclass.constructor.call(this, config);
38333 };
38334
38335 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
38336     /**
38337      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
38338      */
38339     itemCls : "x-menu-sep",
38340     /**
38341      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
38342      */
38343     hideOnClick : false,
38344
38345     // private
38346     onRender : function(li){
38347         var s = document.createElement("span");
38348         s.className = this.itemCls;
38349         s.innerHTML = "&#160;";
38350         this.el = s;
38351         li.addClass("x-menu-sep-li");
38352         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
38353     }
38354 });/*
38355  * Based on:
38356  * Ext JS Library 1.1.1
38357  * Copyright(c) 2006-2007, Ext JS, LLC.
38358  *
38359  * Originally Released Under LGPL - original licence link has changed is not relivant.
38360  *
38361  * Fork - LGPL
38362  * <script type="text/javascript">
38363  */
38364 /**
38365  * @class Roo.menu.Item
38366  * @extends Roo.menu.BaseItem
38367  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
38368  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
38369  * activation and click handling.
38370  * @constructor
38371  * Creates a new Item
38372  * @param {Object} config Configuration options
38373  */
38374 Roo.menu.Item = function(config){
38375     Roo.menu.Item.superclass.constructor.call(this, config);
38376     if(this.menu){
38377         this.menu = Roo.menu.MenuMgr.get(this.menu);
38378     }
38379 };
38380 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
38381     
38382     /**
38383      * @cfg {String} text
38384      * The text to show on the menu item.
38385      */
38386     text: '',
38387      /**
38388      * @cfg {String} HTML to render in menu
38389      * The text to show on the menu item (HTML version).
38390      */
38391     html: '',
38392     /**
38393      * @cfg {String} icon
38394      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
38395      */
38396     icon: undefined,
38397     /**
38398      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
38399      */
38400     itemCls : "x-menu-item",
38401     /**
38402      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
38403      */
38404     canActivate : true,
38405     /**
38406      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
38407      */
38408     showDelay: 200,
38409     // doc'd in BaseItem
38410     hideDelay: 200,
38411
38412     // private
38413     ctype: "Roo.menu.Item",
38414     
38415     // private
38416     onRender : function(container, position){
38417         var el = document.createElement("a");
38418         el.hideFocus = true;
38419         el.unselectable = "on";
38420         el.href = this.href || "#";
38421         if(this.hrefTarget){
38422             el.target = this.hrefTarget;
38423         }
38424         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
38425         
38426         var html = this.html.length ? this.html  : String.format('{0}',this.text);
38427         
38428         el.innerHTML = String.format(
38429                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
38430                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
38431         this.el = el;
38432         Roo.menu.Item.superclass.onRender.call(this, container, position);
38433     },
38434
38435     /**
38436      * Sets the text to display in this menu item
38437      * @param {String} text The text to display
38438      * @param {Boolean} isHTML true to indicate text is pure html.
38439      */
38440     setText : function(text, isHTML){
38441         if (isHTML) {
38442             this.html = text;
38443         } else {
38444             this.text = text;
38445             this.html = '';
38446         }
38447         if(this.rendered){
38448             var html = this.html.length ? this.html  : String.format('{0}',this.text);
38449      
38450             this.el.update(String.format(
38451                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
38452                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
38453             this.parentMenu.autoWidth();
38454         }
38455     },
38456
38457     // private
38458     handleClick : function(e){
38459         if(!this.href){ // if no link defined, stop the event automatically
38460             e.stopEvent();
38461         }
38462         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
38463     },
38464
38465     // private
38466     activate : function(autoExpand){
38467         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
38468             this.focus();
38469             if(autoExpand){
38470                 this.expandMenu();
38471             }
38472         }
38473         return true;
38474     },
38475
38476     // private
38477     shouldDeactivate : function(e){
38478         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
38479             if(this.menu && this.menu.isVisible()){
38480                 return !this.menu.getEl().getRegion().contains(e.getPoint());
38481             }
38482             return true;
38483         }
38484         return false;
38485     },
38486
38487     // private
38488     deactivate : function(){
38489         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
38490         this.hideMenu();
38491     },
38492
38493     // private
38494     expandMenu : function(autoActivate){
38495         if(!this.disabled && this.menu){
38496             clearTimeout(this.hideTimer);
38497             delete this.hideTimer;
38498             if(!this.menu.isVisible() && !this.showTimer){
38499                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
38500             }else if (this.menu.isVisible() && autoActivate){
38501                 this.menu.tryActivate(0, 1);
38502             }
38503         }
38504     },
38505
38506     // private
38507     deferExpand : function(autoActivate){
38508         delete this.showTimer;
38509         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
38510         if(autoActivate){
38511             this.menu.tryActivate(0, 1);
38512         }
38513     },
38514
38515     // private
38516     hideMenu : function(){
38517         clearTimeout(this.showTimer);
38518         delete this.showTimer;
38519         if(!this.hideTimer && this.menu && this.menu.isVisible()){
38520             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
38521         }
38522     },
38523
38524     // private
38525     deferHide : function(){
38526         delete this.hideTimer;
38527         this.menu.hide();
38528     }
38529 });/*
38530  * Based on:
38531  * Ext JS Library 1.1.1
38532  * Copyright(c) 2006-2007, Ext JS, LLC.
38533  *
38534  * Originally Released Under LGPL - original licence link has changed is not relivant.
38535  *
38536  * Fork - LGPL
38537  * <script type="text/javascript">
38538  */
38539  
38540 /**
38541  * @class Roo.menu.CheckItem
38542  * @extends Roo.menu.Item
38543  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
38544  * @constructor
38545  * Creates a new CheckItem
38546  * @param {Object} config Configuration options
38547  */
38548 Roo.menu.CheckItem = function(config){
38549     Roo.menu.CheckItem.superclass.constructor.call(this, config);
38550     this.addEvents({
38551         /**
38552          * @event beforecheckchange
38553          * Fires before the checked value is set, providing an opportunity to cancel if needed
38554          * @param {Roo.menu.CheckItem} this
38555          * @param {Boolean} checked The new checked value that will be set
38556          */
38557         "beforecheckchange" : true,
38558         /**
38559          * @event checkchange
38560          * Fires after the checked value has been set
38561          * @param {Roo.menu.CheckItem} this
38562          * @param {Boolean} checked The checked value that was set
38563          */
38564         "checkchange" : true
38565     });
38566     if(this.checkHandler){
38567         this.on('checkchange', this.checkHandler, this.scope);
38568     }
38569 };
38570 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
38571     /**
38572      * @cfg {String} group
38573      * All check items with the same group name will automatically be grouped into a single-select
38574      * radio button group (defaults to '')
38575      */
38576     /**
38577      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
38578      */
38579     itemCls : "x-menu-item x-menu-check-item",
38580     /**
38581      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
38582      */
38583     groupClass : "x-menu-group-item",
38584
38585     /**
38586      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
38587      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
38588      * initialized with checked = true will be rendered as checked.
38589      */
38590     checked: false,
38591
38592     // private
38593     ctype: "Roo.menu.CheckItem",
38594
38595     // private
38596     onRender : function(c){
38597         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
38598         if(this.group){
38599             this.el.addClass(this.groupClass);
38600         }
38601         Roo.menu.MenuMgr.registerCheckable(this);
38602         if(this.checked){
38603             this.checked = false;
38604             this.setChecked(true, true);
38605         }
38606     },
38607
38608     // private
38609     destroy : function(){
38610         if(this.rendered){
38611             Roo.menu.MenuMgr.unregisterCheckable(this);
38612         }
38613         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
38614     },
38615
38616     /**
38617      * Set the checked state of this item
38618      * @param {Boolean} checked The new checked value
38619      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
38620      */
38621     setChecked : function(state, suppressEvent){
38622         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
38623             if(this.container){
38624                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
38625             }
38626             this.checked = state;
38627             if(suppressEvent !== true){
38628                 this.fireEvent("checkchange", this, state);
38629             }
38630         }
38631     },
38632
38633     // private
38634     handleClick : function(e){
38635        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
38636            this.setChecked(!this.checked);
38637        }
38638        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
38639     }
38640 });/*
38641  * Based on:
38642  * Ext JS Library 1.1.1
38643  * Copyright(c) 2006-2007, Ext JS, LLC.
38644  *
38645  * Originally Released Under LGPL - original licence link has changed is not relivant.
38646  *
38647  * Fork - LGPL
38648  * <script type="text/javascript">
38649  */
38650  
38651 /**
38652  * @class Roo.menu.DateItem
38653  * @extends Roo.menu.Adapter
38654  * A menu item that wraps the {@link Roo.DatPicker} component.
38655  * @constructor
38656  * Creates a new DateItem
38657  * @param {Object} config Configuration options
38658  */
38659 Roo.menu.DateItem = function(config){
38660     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
38661     /** The Roo.DatePicker object @type Roo.DatePicker */
38662     this.picker = this.component;
38663     this.addEvents({select: true});
38664     
38665     this.picker.on("render", function(picker){
38666         picker.getEl().swallowEvent("click");
38667         picker.container.addClass("x-menu-date-item");
38668     });
38669
38670     this.picker.on("select", this.onSelect, this);
38671 };
38672
38673 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
38674     // private
38675     onSelect : function(picker, date){
38676         this.fireEvent("select", this, date, picker);
38677         Roo.menu.DateItem.superclass.handleClick.call(this);
38678     }
38679 });/*
38680  * Based on:
38681  * Ext JS Library 1.1.1
38682  * Copyright(c) 2006-2007, Ext JS, LLC.
38683  *
38684  * Originally Released Under LGPL - original licence link has changed is not relivant.
38685  *
38686  * Fork - LGPL
38687  * <script type="text/javascript">
38688  */
38689  
38690 /**
38691  * @class Roo.menu.ColorItem
38692  * @extends Roo.menu.Adapter
38693  * A menu item that wraps the {@link Roo.ColorPalette} component.
38694  * @constructor
38695  * Creates a new ColorItem
38696  * @param {Object} config Configuration options
38697  */
38698 Roo.menu.ColorItem = function(config){
38699     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
38700     /** The Roo.ColorPalette object @type Roo.ColorPalette */
38701     this.palette = this.component;
38702     this.relayEvents(this.palette, ["select"]);
38703     if(this.selectHandler){
38704         this.on('select', this.selectHandler, this.scope);
38705     }
38706 };
38707 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
38708  * Based on:
38709  * Ext JS Library 1.1.1
38710  * Copyright(c) 2006-2007, Ext JS, LLC.
38711  *
38712  * Originally Released Under LGPL - original licence link has changed is not relivant.
38713  *
38714  * Fork - LGPL
38715  * <script type="text/javascript">
38716  */
38717  
38718
38719 /**
38720  * @class Roo.menu.DateMenu
38721  * @extends Roo.menu.Menu
38722  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
38723  * @constructor
38724  * Creates a new DateMenu
38725  * @param {Object} config Configuration options
38726  */
38727 Roo.menu.DateMenu = function(config){
38728     Roo.menu.DateMenu.superclass.constructor.call(this, config);
38729     this.plain = true;
38730     var di = new Roo.menu.DateItem(config);
38731     this.add(di);
38732     /**
38733      * The {@link Roo.DatePicker} instance for this DateMenu
38734      * @type DatePicker
38735      */
38736     this.picker = di.picker;
38737     /**
38738      * @event select
38739      * @param {DatePicker} picker
38740      * @param {Date} date
38741      */
38742     this.relayEvents(di, ["select"]);
38743     this.on('beforeshow', function(){
38744         if(this.picker){
38745             this.picker.hideMonthPicker(false);
38746         }
38747     }, this);
38748 };
38749 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
38750     cls:'x-date-menu'
38751 });/*
38752  * Based on:
38753  * Ext JS Library 1.1.1
38754  * Copyright(c) 2006-2007, Ext JS, LLC.
38755  *
38756  * Originally Released Under LGPL - original licence link has changed is not relivant.
38757  *
38758  * Fork - LGPL
38759  * <script type="text/javascript">
38760  */
38761  
38762
38763 /**
38764  * @class Roo.menu.ColorMenu
38765  * @extends Roo.menu.Menu
38766  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
38767  * @constructor
38768  * Creates a new ColorMenu
38769  * @param {Object} config Configuration options
38770  */
38771 Roo.menu.ColorMenu = function(config){
38772     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
38773     this.plain = true;
38774     var ci = new Roo.menu.ColorItem(config);
38775     this.add(ci);
38776     /**
38777      * The {@link Roo.ColorPalette} instance for this ColorMenu
38778      * @type ColorPalette
38779      */
38780     this.palette = ci.palette;
38781     /**
38782      * @event select
38783      * @param {ColorPalette} palette
38784      * @param {String} color
38785      */
38786     this.relayEvents(ci, ["select"]);
38787 };
38788 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
38789  * Based on:
38790  * Ext JS Library 1.1.1
38791  * Copyright(c) 2006-2007, Ext JS, LLC.
38792  *
38793  * Originally Released Under LGPL - original licence link has changed is not relivant.
38794  *
38795  * Fork - LGPL
38796  * <script type="text/javascript">
38797  */
38798  
38799 /**
38800  * @class Roo.form.TextItem
38801  * @extends Roo.BoxComponent
38802  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
38803  * @constructor
38804  * Creates a new TextItem
38805  * @param {Object} config Configuration options
38806  */
38807 Roo.form.TextItem = function(config){
38808     Roo.form.TextItem.superclass.constructor.call(this, config);
38809 };
38810
38811 Roo.extend(Roo.form.TextItem, Roo.BoxComponent,  {
38812     
38813     /**
38814      * @cfg {String} tag the tag for this item (default div)
38815      */
38816     tag : 'div',
38817     /**
38818      * @cfg {String} html the content for this item
38819      */
38820     html : '',
38821     
38822     getAutoCreate : function()
38823     {
38824         var cfg = {
38825             id: this.id,
38826             tag: this.tag,
38827             html: this.html,
38828             cls: 'x-form-item'
38829         };
38830         
38831         return cfg;
38832         
38833     },
38834     
38835     onRender : function(ct, position)
38836     {
38837         Roo.form.TextItem.superclass.onRender.call(this, ct, position);
38838         
38839         if(!this.el){
38840             var cfg = this.getAutoCreate();
38841             if(!cfg.name){
38842                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
38843             }
38844             if (!cfg.name.length) {
38845                 delete cfg.name;
38846             }
38847             this.el = ct.createChild(cfg, position);
38848         }
38849     }
38850     
38851 });/*
38852  * Based on:
38853  * Ext JS Library 1.1.1
38854  * Copyright(c) 2006-2007, Ext JS, LLC.
38855  *
38856  * Originally Released Under LGPL - original licence link has changed is not relivant.
38857  *
38858  * Fork - LGPL
38859  * <script type="text/javascript">
38860  */
38861  
38862 /**
38863  * @class Roo.form.Field
38864  * @extends Roo.BoxComponent
38865  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
38866  * @constructor
38867  * Creates a new Field
38868  * @param {Object} config Configuration options
38869  */
38870 Roo.form.Field = function(config){
38871     Roo.form.Field.superclass.constructor.call(this, config);
38872 };
38873
38874 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
38875     /**
38876      * @cfg {String} fieldLabel Label to use when rendering a form.
38877      */
38878        /**
38879      * @cfg {String} qtip Mouse over tip
38880      */
38881      
38882     /**
38883      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
38884      */
38885     invalidClass : "x-form-invalid",
38886     /**
38887      * @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")
38888      */
38889     invalidText : "The value in this field is invalid",
38890     /**
38891      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
38892      */
38893     focusClass : "x-form-focus",
38894     /**
38895      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
38896       automatic validation (defaults to "keyup").
38897      */
38898     validationEvent : "keyup",
38899     /**
38900      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
38901      */
38902     validateOnBlur : true,
38903     /**
38904      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
38905      */
38906     validationDelay : 250,
38907     /**
38908      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
38909      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
38910      */
38911     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
38912     /**
38913      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
38914      */
38915     fieldClass : "x-form-field",
38916     /**
38917      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
38918      *<pre>
38919 Value         Description
38920 -----------   ----------------------------------------------------------------------
38921 qtip          Display a quick tip when the user hovers over the field
38922 title         Display a default browser title attribute popup
38923 under         Add a block div beneath the field containing the error text
38924 side          Add an error icon to the right of the field with a popup on hover
38925 [element id]  Add the error text directly to the innerHTML of the specified element
38926 </pre>
38927      */
38928     msgTarget : 'qtip',
38929     /**
38930      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
38931      */
38932     msgFx : 'normal',
38933
38934     /**
38935      * @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.
38936      */
38937     readOnly : false,
38938
38939     /**
38940      * @cfg {Boolean} disabled True to disable the field (defaults to false).
38941      */
38942     disabled : false,
38943
38944     /**
38945      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
38946      */
38947     inputType : undefined,
38948     
38949     /**
38950      * @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).
38951          */
38952         tabIndex : undefined,
38953         
38954     // private
38955     isFormField : true,
38956
38957     // private
38958     hasFocus : false,
38959     /**
38960      * @property {Roo.Element} fieldEl
38961      * Element Containing the rendered Field (with label etc.)
38962      */
38963     /**
38964      * @cfg {Mixed} value A value to initialize this field with.
38965      */
38966     value : undefined,
38967
38968     /**
38969      * @cfg {String} name The field's HTML name attribute.
38970      */
38971     /**
38972      * @cfg {String} cls A CSS class to apply to the field's underlying element.
38973      */
38974     // private
38975     loadedValue : false,
38976      
38977      
38978         // private ??
38979         initComponent : function(){
38980         Roo.form.Field.superclass.initComponent.call(this);
38981         this.addEvents({
38982             /**
38983              * @event focus
38984              * Fires when this field receives input focus.
38985              * @param {Roo.form.Field} this
38986              */
38987             focus : true,
38988             /**
38989              * @event blur
38990              * Fires when this field loses input focus.
38991              * @param {Roo.form.Field} this
38992              */
38993             blur : true,
38994             /**
38995              * @event specialkey
38996              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
38997              * {@link Roo.EventObject#getKey} to determine which key was pressed.
38998              * @param {Roo.form.Field} this
38999              * @param {Roo.EventObject} e The event object
39000              */
39001             specialkey : true,
39002             /**
39003              * @event change
39004              * Fires just before the field blurs if the field value has changed.
39005              * @param {Roo.form.Field} this
39006              * @param {Mixed} newValue The new value
39007              * @param {Mixed} oldValue The original value
39008              */
39009             change : true,
39010             /**
39011              * @event invalid
39012              * Fires after the field has been marked as invalid.
39013              * @param {Roo.form.Field} this
39014              * @param {String} msg The validation message
39015              */
39016             invalid : true,
39017             /**
39018              * @event valid
39019              * Fires after the field has been validated with no errors.
39020              * @param {Roo.form.Field} this
39021              */
39022             valid : true,
39023              /**
39024              * @event keyup
39025              * Fires after the key up
39026              * @param {Roo.form.Field} this
39027              * @param {Roo.EventObject}  e The event Object
39028              */
39029             keyup : true
39030         });
39031     },
39032
39033     /**
39034      * Returns the name attribute of the field if available
39035      * @return {String} name The field name
39036      */
39037     getName: function(){
39038          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
39039     },
39040
39041     // private
39042     onRender : function(ct, position){
39043         Roo.form.Field.superclass.onRender.call(this, ct, position);
39044         if(!this.el){
39045             var cfg = this.getAutoCreate();
39046             if(!cfg.name){
39047                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
39048             }
39049             if (!cfg.name.length) {
39050                 delete cfg.name;
39051             }
39052             if(this.inputType){
39053                 cfg.type = this.inputType;
39054             }
39055             this.el = ct.createChild(cfg, position);
39056         }
39057         var type = this.el.dom.type;
39058         if(type){
39059             if(type == 'password'){
39060                 type = 'text';
39061             }
39062             this.el.addClass('x-form-'+type);
39063         }
39064         if(this.readOnly){
39065             this.el.dom.readOnly = true;
39066         }
39067         if(this.tabIndex !== undefined){
39068             this.el.dom.setAttribute('tabIndex', this.tabIndex);
39069         }
39070
39071         this.el.addClass([this.fieldClass, this.cls]);
39072         this.initValue();
39073     },
39074
39075     /**
39076      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
39077      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
39078      * @return {Roo.form.Field} this
39079      */
39080     applyTo : function(target){
39081         this.allowDomMove = false;
39082         this.el = Roo.get(target);
39083         this.render(this.el.dom.parentNode);
39084         return this;
39085     },
39086
39087     // private
39088     initValue : function(){
39089         if(this.value !== undefined){
39090             this.setValue(this.value);
39091         }else if(this.el.dom.value.length > 0){
39092             this.setValue(this.el.dom.value);
39093         }
39094     },
39095
39096     /**
39097      * Returns true if this field has been changed since it was originally loaded and is not disabled.
39098      * DEPRICATED  - it never worked well - use hasChanged/resetHasChanged.
39099      */
39100     isDirty : function() {
39101         if(this.disabled) {
39102             return false;
39103         }
39104         return String(this.getValue()) !== String(this.originalValue);
39105     },
39106
39107     /**
39108      * stores the current value in loadedValue
39109      */
39110     resetHasChanged : function()
39111     {
39112         this.loadedValue = String(this.getValue());
39113     },
39114     /**
39115      * checks the current value against the 'loaded' value.
39116      * Note - will return false if 'resetHasChanged' has not been called first.
39117      */
39118     hasChanged : function()
39119     {
39120         if(this.disabled || this.readOnly) {
39121             return false;
39122         }
39123         return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
39124     },
39125     
39126     
39127     
39128     // private
39129     afterRender : function(){
39130         Roo.form.Field.superclass.afterRender.call(this);
39131         this.initEvents();
39132     },
39133
39134     // private
39135     fireKey : function(e){
39136         //Roo.log('field ' + e.getKey());
39137         if(e.isNavKeyPress()){
39138             this.fireEvent("specialkey", this, e);
39139         }
39140     },
39141
39142     /**
39143      * Resets the current field value to the originally loaded value and clears any validation messages
39144      */
39145     reset : function(){
39146         this.setValue(this.resetValue);
39147         this.originalValue = this.getValue();
39148         this.clearInvalid();
39149     },
39150
39151     // private
39152     initEvents : function(){
39153         // safari killled keypress - so keydown is now used..
39154         this.el.on("keydown" , this.fireKey,  this);
39155         this.el.on("focus", this.onFocus,  this);
39156         this.el.on("blur", this.onBlur,  this);
39157         this.el.relayEvent('keyup', this);
39158
39159         // reference to original value for reset
39160         this.originalValue = this.getValue();
39161         this.resetValue =  this.getValue();
39162     },
39163
39164     // private
39165     onFocus : function(){
39166         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
39167             this.el.addClass(this.focusClass);
39168         }
39169         if(!this.hasFocus){
39170             this.hasFocus = true;
39171             this.startValue = this.getValue();
39172             this.fireEvent("focus", this);
39173         }
39174     },
39175
39176     beforeBlur : Roo.emptyFn,
39177
39178     // private
39179     onBlur : function(){
39180         this.beforeBlur();
39181         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
39182             this.el.removeClass(this.focusClass);
39183         }
39184         this.hasFocus = false;
39185         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
39186             this.validate();
39187         }
39188         var v = this.getValue();
39189         if(String(v) !== String(this.startValue)){
39190             this.fireEvent('change', this, v, this.startValue);
39191         }
39192         this.fireEvent("blur", this);
39193     },
39194
39195     /**
39196      * Returns whether or not the field value is currently valid
39197      * @param {Boolean} preventMark True to disable marking the field invalid
39198      * @return {Boolean} True if the value is valid, else false
39199      */
39200     isValid : function(preventMark){
39201         if(this.disabled){
39202             return true;
39203         }
39204         var restore = this.preventMark;
39205         this.preventMark = preventMark === true;
39206         var v = this.validateValue(this.processValue(this.getRawValue()));
39207         this.preventMark = restore;
39208         return v;
39209     },
39210
39211     /**
39212      * Validates the field value
39213      * @return {Boolean} True if the value is valid, else false
39214      */
39215     validate : function(){
39216         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
39217             this.clearInvalid();
39218             return true;
39219         }
39220         return false;
39221     },
39222
39223     processValue : function(value){
39224         return value;
39225     },
39226
39227     // private
39228     // Subclasses should provide the validation implementation by overriding this
39229     validateValue : function(value){
39230         return true;
39231     },
39232
39233     /**
39234      * Mark this field as invalid
39235      * @param {String} msg The validation message
39236      */
39237     markInvalid : function(msg){
39238         if(!this.rendered || this.preventMark){ // not rendered
39239             return;
39240         }
39241         
39242         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
39243         
39244         obj.el.addClass(this.invalidClass);
39245         msg = msg || this.invalidText;
39246         switch(this.msgTarget){
39247             case 'qtip':
39248                 obj.el.dom.qtip = msg;
39249                 obj.el.dom.qclass = 'x-form-invalid-tip';
39250                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
39251                     Roo.QuickTips.enable();
39252                 }
39253                 break;
39254             case 'title':
39255                 this.el.dom.title = msg;
39256                 break;
39257             case 'under':
39258                 if(!this.errorEl){
39259                     var elp = this.el.findParent('.x-form-element', 5, true);
39260                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
39261                     this.errorEl.setWidth(elp.getWidth(true)-20);
39262                 }
39263                 this.errorEl.update(msg);
39264                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
39265                 break;
39266             case 'side':
39267                 if(!this.errorIcon){
39268                     var elp = this.el.findParent('.x-form-element', 5, true);
39269                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
39270                 }
39271                 this.alignErrorIcon();
39272                 this.errorIcon.dom.qtip = msg;
39273                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
39274                 this.errorIcon.show();
39275                 this.on('resize', this.alignErrorIcon, this);
39276                 break;
39277             default:
39278                 var t = Roo.getDom(this.msgTarget);
39279                 t.innerHTML = msg;
39280                 t.style.display = this.msgDisplay;
39281                 break;
39282         }
39283         this.fireEvent('invalid', this, msg);
39284     },
39285
39286     // private
39287     alignErrorIcon : function(){
39288         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
39289     },
39290
39291     /**
39292      * Clear any invalid styles/messages for this field
39293      */
39294     clearInvalid : function(){
39295         if(!this.rendered || this.preventMark){ // not rendered
39296             return;
39297         }
39298         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
39299         
39300         obj.el.removeClass(this.invalidClass);
39301         switch(this.msgTarget){
39302             case 'qtip':
39303                 obj.el.dom.qtip = '';
39304                 break;
39305             case 'title':
39306                 this.el.dom.title = '';
39307                 break;
39308             case 'under':
39309                 if(this.errorEl){
39310                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
39311                 }
39312                 break;
39313             case 'side':
39314                 if(this.errorIcon){
39315                     this.errorIcon.dom.qtip = '';
39316                     this.errorIcon.hide();
39317                     this.un('resize', this.alignErrorIcon, this);
39318                 }
39319                 break;
39320             default:
39321                 var t = Roo.getDom(this.msgTarget);
39322                 t.innerHTML = '';
39323                 t.style.display = 'none';
39324                 break;
39325         }
39326         this.fireEvent('valid', this);
39327     },
39328
39329     /**
39330      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
39331      * @return {Mixed} value The field value
39332      */
39333     getRawValue : function(){
39334         var v = this.el.getValue();
39335         
39336         return v;
39337     },
39338
39339     /**
39340      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
39341      * @return {Mixed} value The field value
39342      */
39343     getValue : function(){
39344         var v = this.el.getValue();
39345          
39346         return v;
39347     },
39348
39349     /**
39350      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
39351      * @param {Mixed} value The value to set
39352      */
39353     setRawValue : function(v){
39354         return this.el.dom.value = (v === null || v === undefined ? '' : v);
39355     },
39356
39357     /**
39358      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
39359      * @param {Mixed} value The value to set
39360      */
39361     setValue : function(v){
39362         this.value = v;
39363         if(this.rendered){
39364             this.el.dom.value = (v === null || v === undefined ? '' : v);
39365              this.validate();
39366         }
39367     },
39368
39369     adjustSize : function(w, h){
39370         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
39371         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
39372         return s;
39373     },
39374
39375     adjustWidth : function(tag, w){
39376         tag = tag.toLowerCase();
39377         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
39378             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
39379                 if(tag == 'input'){
39380                     return w + 2;
39381                 }
39382                 if(tag == 'textarea'){
39383                     return w-2;
39384                 }
39385             }else if(Roo.isOpera){
39386                 if(tag == 'input'){
39387                     return w + 2;
39388                 }
39389                 if(tag == 'textarea'){
39390                     return w-2;
39391                 }
39392             }
39393         }
39394         return w;
39395     }
39396 });
39397
39398
39399 // anything other than normal should be considered experimental
39400 Roo.form.Field.msgFx = {
39401     normal : {
39402         show: function(msgEl, f){
39403             msgEl.setDisplayed('block');
39404         },
39405
39406         hide : function(msgEl, f){
39407             msgEl.setDisplayed(false).update('');
39408         }
39409     },
39410
39411     slide : {
39412         show: function(msgEl, f){
39413             msgEl.slideIn('t', {stopFx:true});
39414         },
39415
39416         hide : function(msgEl, f){
39417             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
39418         }
39419     },
39420
39421     slideRight : {
39422         show: function(msgEl, f){
39423             msgEl.fixDisplay();
39424             msgEl.alignTo(f.el, 'tl-tr');
39425             msgEl.slideIn('l', {stopFx:true});
39426         },
39427
39428         hide : function(msgEl, f){
39429             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
39430         }
39431     }
39432 };/*
39433  * Based on:
39434  * Ext JS Library 1.1.1
39435  * Copyright(c) 2006-2007, Ext JS, LLC.
39436  *
39437  * Originally Released Under LGPL - original licence link has changed is not relivant.
39438  *
39439  * Fork - LGPL
39440  * <script type="text/javascript">
39441  */
39442  
39443
39444 /**
39445  * @class Roo.form.TextField
39446  * @extends Roo.form.Field
39447  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
39448  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
39449  * @constructor
39450  * Creates a new TextField
39451  * @param {Object} config Configuration options
39452  */
39453 Roo.form.TextField = function(config){
39454     Roo.form.TextField.superclass.constructor.call(this, config);
39455     this.addEvents({
39456         /**
39457          * @event autosize
39458          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
39459          * according to the default logic, but this event provides a hook for the developer to apply additional
39460          * logic at runtime to resize the field if needed.
39461              * @param {Roo.form.Field} this This text field
39462              * @param {Number} width The new field width
39463              */
39464         autosize : true
39465     });
39466 };
39467
39468 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
39469     /**
39470      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
39471      */
39472     grow : false,
39473     /**
39474      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
39475      */
39476     growMin : 30,
39477     /**
39478      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
39479      */
39480     growMax : 800,
39481     /**
39482      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
39483      */
39484     vtype : null,
39485     /**
39486      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
39487      */
39488     maskRe : null,
39489     /**
39490      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
39491      */
39492     disableKeyFilter : false,
39493     /**
39494      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
39495      */
39496     allowBlank : true,
39497     /**
39498      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
39499      */
39500     minLength : 0,
39501     /**
39502      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
39503      */
39504     maxLength : Number.MAX_VALUE,
39505     /**
39506      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
39507      */
39508     minLengthText : "The minimum length for this field is {0}",
39509     /**
39510      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
39511      */
39512     maxLengthText : "The maximum length for this field is {0}",
39513     /**
39514      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
39515      */
39516     selectOnFocus : false,
39517     /**
39518      * @cfg {Boolean} allowLeadingSpace True to prevent the stripping of leading white space 
39519      */    
39520     allowLeadingSpace : false,
39521     /**
39522      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
39523      */
39524     blankText : "This field is required",
39525     /**
39526      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
39527      * If available, this function will be called only after the basic validators all return true, and will be passed the
39528      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
39529      */
39530     validator : null,
39531     /**
39532      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
39533      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
39534      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
39535      */
39536     regex : null,
39537     /**
39538      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
39539      */
39540     regexText : "",
39541     /**
39542      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
39543      */
39544     emptyText : null,
39545    
39546
39547     // private
39548     initEvents : function()
39549     {
39550         if (this.emptyText) {
39551             this.el.attr('placeholder', this.emptyText);
39552         }
39553         
39554         Roo.form.TextField.superclass.initEvents.call(this);
39555         if(this.validationEvent == 'keyup'){
39556             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
39557             this.el.on('keyup', this.filterValidation, this);
39558         }
39559         else if(this.validationEvent !== false){
39560             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
39561         }
39562         
39563         if(this.selectOnFocus){
39564             this.on("focus", this.preFocus, this);
39565         }
39566         if (!this.allowLeadingSpace) {
39567             this.on('blur', this.cleanLeadingSpace, this);
39568         }
39569         
39570         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
39571             this.el.on("keypress", this.filterKeys, this);
39572         }
39573         if(this.grow){
39574             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
39575             this.el.on("click", this.autoSize,  this);
39576         }
39577         if(this.el.is('input[type=password]') && Roo.isSafari){
39578             this.el.on('keydown', this.SafariOnKeyDown, this);
39579         }
39580     },
39581
39582     processValue : function(value){
39583         if(this.stripCharsRe){
39584             var newValue = value.replace(this.stripCharsRe, '');
39585             if(newValue !== value){
39586                 this.setRawValue(newValue);
39587                 return newValue;
39588             }
39589         }
39590         return value;
39591     },
39592
39593     filterValidation : function(e){
39594         if(!e.isNavKeyPress()){
39595             this.validationTask.delay(this.validationDelay);
39596         }
39597     },
39598
39599     // private
39600     onKeyUp : function(e){
39601         if(!e.isNavKeyPress()){
39602             this.autoSize();
39603         }
39604     },
39605     // private - clean the leading white space
39606     cleanLeadingSpace : function(e)
39607     {
39608         if ( this.inputType == 'file') {
39609             return;
39610         }
39611         
39612         this.setValue((this.getValue() + '').replace(/^\s+/,''));
39613     },
39614     /**
39615      * Resets the current field value to the originally-loaded value and clears any validation messages.
39616      *  
39617      */
39618     reset : function(){
39619         Roo.form.TextField.superclass.reset.call(this);
39620        
39621     }, 
39622     // private
39623     preFocus : function(){
39624         
39625         if(this.selectOnFocus){
39626             this.el.dom.select();
39627         }
39628     },
39629
39630     
39631     // private
39632     filterKeys : function(e){
39633         var k = e.getKey();
39634         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
39635             return;
39636         }
39637         var c = e.getCharCode(), cc = String.fromCharCode(c);
39638         if(Roo.isIE && (e.isSpecialKey() || !cc)){
39639             return;
39640         }
39641         if(!this.maskRe.test(cc)){
39642             e.stopEvent();
39643         }
39644     },
39645
39646     setValue : function(v){
39647         
39648         Roo.form.TextField.superclass.setValue.apply(this, arguments);
39649         
39650         this.autoSize();
39651     },
39652
39653     /**
39654      * Validates a value according to the field's validation rules and marks the field as invalid
39655      * if the validation fails
39656      * @param {Mixed} value The value to validate
39657      * @return {Boolean} True if the value is valid, else false
39658      */
39659     validateValue : function(value){
39660         if(value.length < 1)  { // if it's blank
39661              if(this.allowBlank){
39662                 this.clearInvalid();
39663                 return true;
39664              }else{
39665                 this.markInvalid(this.blankText);
39666                 return false;
39667              }
39668         }
39669         if(value.length < this.minLength){
39670             this.markInvalid(String.format(this.minLengthText, this.minLength));
39671             return false;
39672         }
39673         if(value.length > this.maxLength){
39674             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
39675             return false;
39676         }
39677         if(this.vtype){
39678             var vt = Roo.form.VTypes;
39679             if(!vt[this.vtype](value, this)){
39680                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
39681                 return false;
39682             }
39683         }
39684         if(typeof this.validator == "function"){
39685             var msg = this.validator(value);
39686             if(msg !== true){
39687                 this.markInvalid(msg);
39688                 return false;
39689             }
39690         }
39691         if(this.regex && !this.regex.test(value)){
39692             this.markInvalid(this.regexText);
39693             return false;
39694         }
39695         return true;
39696     },
39697
39698     /**
39699      * Selects text in this field
39700      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
39701      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
39702      */
39703     selectText : function(start, end){
39704         var v = this.getRawValue();
39705         if(v.length > 0){
39706             start = start === undefined ? 0 : start;
39707             end = end === undefined ? v.length : end;
39708             var d = this.el.dom;
39709             if(d.setSelectionRange){
39710                 d.setSelectionRange(start, end);
39711             }else if(d.createTextRange){
39712                 var range = d.createTextRange();
39713                 range.moveStart("character", start);
39714                 range.moveEnd("character", v.length-end);
39715                 range.select();
39716             }
39717         }
39718     },
39719
39720     /**
39721      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
39722      * This only takes effect if grow = true, and fires the autosize event.
39723      */
39724     autoSize : function(){
39725         if(!this.grow || !this.rendered){
39726             return;
39727         }
39728         if(!this.metrics){
39729             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
39730         }
39731         var el = this.el;
39732         var v = el.dom.value;
39733         var d = document.createElement('div');
39734         d.appendChild(document.createTextNode(v));
39735         v = d.innerHTML;
39736         d = null;
39737         v += "&#160;";
39738         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
39739         this.el.setWidth(w);
39740         this.fireEvent("autosize", this, w);
39741     },
39742     
39743     // private
39744     SafariOnKeyDown : function(event)
39745     {
39746         // this is a workaround for a password hang bug on chrome/ webkit.
39747         
39748         var isSelectAll = false;
39749         
39750         if(this.el.dom.selectionEnd > 0){
39751             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
39752         }
39753         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
39754             event.preventDefault();
39755             this.setValue('');
39756             return;
39757         }
39758         
39759         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
39760             
39761             event.preventDefault();
39762             // this is very hacky as keydown always get's upper case.
39763             
39764             var cc = String.fromCharCode(event.getCharCode());
39765             
39766             
39767             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
39768             
39769         }
39770         
39771         
39772     }
39773 });/*
39774  * Based on:
39775  * Ext JS Library 1.1.1
39776  * Copyright(c) 2006-2007, Ext JS, LLC.
39777  *
39778  * Originally Released Under LGPL - original licence link has changed is not relivant.
39779  *
39780  * Fork - LGPL
39781  * <script type="text/javascript">
39782  */
39783  
39784 /**
39785  * @class Roo.form.Hidden
39786  * @extends Roo.form.TextField
39787  * Simple Hidden element used on forms 
39788  * 
39789  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
39790  * 
39791  * @constructor
39792  * Creates a new Hidden form element.
39793  * @param {Object} config Configuration options
39794  */
39795
39796
39797
39798 // easy hidden field...
39799 Roo.form.Hidden = function(config){
39800     Roo.form.Hidden.superclass.constructor.call(this, config);
39801 };
39802   
39803 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
39804     fieldLabel:      '',
39805     inputType:      'hidden',
39806     width:          50,
39807     allowBlank:     true,
39808     labelSeparator: '',
39809     hidden:         true,
39810     itemCls :       'x-form-item-display-none'
39811
39812
39813 });
39814
39815
39816 /*
39817  * Based on:
39818  * Ext JS Library 1.1.1
39819  * Copyright(c) 2006-2007, Ext JS, LLC.
39820  *
39821  * Originally Released Under LGPL - original licence link has changed is not relivant.
39822  *
39823  * Fork - LGPL
39824  * <script type="text/javascript">
39825  */
39826  
39827 /**
39828  * @class Roo.form.TriggerField
39829  * @extends Roo.form.TextField
39830  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
39831  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
39832  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
39833  * for which you can provide a custom implementation.  For example:
39834  * <pre><code>
39835 var trigger = new Roo.form.TriggerField();
39836 trigger.onTriggerClick = myTriggerFn;
39837 trigger.applyTo('my-field');
39838 </code></pre>
39839  *
39840  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
39841  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
39842  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
39843  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
39844  * @constructor
39845  * Create a new TriggerField.
39846  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
39847  * to the base TextField)
39848  */
39849 Roo.form.TriggerField = function(config){
39850     this.mimicing = false;
39851     Roo.form.TriggerField.superclass.constructor.call(this, config);
39852 };
39853
39854 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
39855     /**
39856      * @cfg {String} triggerClass A CSS class to apply to the trigger
39857      */
39858     /**
39859      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
39860      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
39861      */
39862     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
39863     /**
39864      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
39865      */
39866     hideTrigger:false,
39867
39868     /** @cfg {Boolean} grow @hide */
39869     /** @cfg {Number} growMin @hide */
39870     /** @cfg {Number} growMax @hide */
39871
39872     /**
39873      * @hide 
39874      * @method
39875      */
39876     autoSize: Roo.emptyFn,
39877     // private
39878     monitorTab : true,
39879     // private
39880     deferHeight : true,
39881
39882     
39883     actionMode : 'wrap',
39884     // private
39885     onResize : function(w, h){
39886         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
39887         if(typeof w == 'number'){
39888             var x = w - this.trigger.getWidth();
39889             this.el.setWidth(this.adjustWidth('input', x));
39890             this.trigger.setStyle('left', x+'px');
39891         }
39892     },
39893
39894     // private
39895     adjustSize : Roo.BoxComponent.prototype.adjustSize,
39896
39897     // private
39898     getResizeEl : function(){
39899         return this.wrap;
39900     },
39901
39902     // private
39903     getPositionEl : function(){
39904         return this.wrap;
39905     },
39906
39907     // private
39908     alignErrorIcon : function(){
39909         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
39910     },
39911
39912     // private
39913     onRender : function(ct, position){
39914         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
39915         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
39916         this.trigger = this.wrap.createChild(this.triggerConfig ||
39917                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
39918         if(this.hideTrigger){
39919             this.trigger.setDisplayed(false);
39920         }
39921         this.initTrigger();
39922         if(!this.width){
39923             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
39924         }
39925     },
39926
39927     // private
39928     initTrigger : function(){
39929         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
39930         this.trigger.addClassOnOver('x-form-trigger-over');
39931         this.trigger.addClassOnClick('x-form-trigger-click');
39932     },
39933
39934     // private
39935     onDestroy : function(){
39936         if(this.trigger){
39937             this.trigger.removeAllListeners();
39938             this.trigger.remove();
39939         }
39940         if(this.wrap){
39941             this.wrap.remove();
39942         }
39943         Roo.form.TriggerField.superclass.onDestroy.call(this);
39944     },
39945
39946     // private
39947     onFocus : function(){
39948         Roo.form.TriggerField.superclass.onFocus.call(this);
39949         if(!this.mimicing){
39950             this.wrap.addClass('x-trigger-wrap-focus');
39951             this.mimicing = true;
39952             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
39953             if(this.monitorTab){
39954                 this.el.on("keydown", this.checkTab, this);
39955             }
39956         }
39957     },
39958
39959     // private
39960     checkTab : function(e){
39961         if(e.getKey() == e.TAB){
39962             this.triggerBlur();
39963         }
39964     },
39965
39966     // private
39967     onBlur : function(){
39968         // do nothing
39969     },
39970
39971     // private
39972     mimicBlur : function(e, t){
39973         if(!this.wrap.contains(t) && this.validateBlur()){
39974             this.triggerBlur();
39975         }
39976     },
39977
39978     // private
39979     triggerBlur : function(){
39980         this.mimicing = false;
39981         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
39982         if(this.monitorTab){
39983             this.el.un("keydown", this.checkTab, this);
39984         }
39985         this.wrap.removeClass('x-trigger-wrap-focus');
39986         Roo.form.TriggerField.superclass.onBlur.call(this);
39987     },
39988
39989     // private
39990     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
39991     validateBlur : function(e, t){
39992         return true;
39993     },
39994
39995     // private
39996     onDisable : function(){
39997         Roo.form.TriggerField.superclass.onDisable.call(this);
39998         if(this.wrap){
39999             this.wrap.addClass('x-item-disabled');
40000         }
40001     },
40002
40003     // private
40004     onEnable : function(){
40005         Roo.form.TriggerField.superclass.onEnable.call(this);
40006         if(this.wrap){
40007             this.wrap.removeClass('x-item-disabled');
40008         }
40009     },
40010
40011     // private
40012     onShow : function(){
40013         var ae = this.getActionEl();
40014         
40015         if(ae){
40016             ae.dom.style.display = '';
40017             ae.dom.style.visibility = 'visible';
40018         }
40019     },
40020
40021     // private
40022     
40023     onHide : function(){
40024         var ae = this.getActionEl();
40025         ae.dom.style.display = 'none';
40026     },
40027
40028     /**
40029      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
40030      * by an implementing function.
40031      * @method
40032      * @param {EventObject} e
40033      */
40034     onTriggerClick : Roo.emptyFn
40035 });
40036
40037 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
40038 // to be extended by an implementing class.  For an example of implementing this class, see the custom
40039 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
40040 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
40041     initComponent : function(){
40042         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
40043
40044         this.triggerConfig = {
40045             tag:'span', cls:'x-form-twin-triggers', cn:[
40046             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
40047             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
40048         ]};
40049     },
40050
40051     getTrigger : function(index){
40052         return this.triggers[index];
40053     },
40054
40055     initTrigger : function(){
40056         var ts = this.trigger.select('.x-form-trigger', true);
40057         this.wrap.setStyle('overflow', 'hidden');
40058         var triggerField = this;
40059         ts.each(function(t, all, index){
40060             t.hide = function(){
40061                 var w = triggerField.wrap.getWidth();
40062                 this.dom.style.display = 'none';
40063                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
40064             };
40065             t.show = function(){
40066                 var w = triggerField.wrap.getWidth();
40067                 this.dom.style.display = '';
40068                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
40069             };
40070             var triggerIndex = 'Trigger'+(index+1);
40071
40072             if(this['hide'+triggerIndex]){
40073                 t.dom.style.display = 'none';
40074             }
40075             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
40076             t.addClassOnOver('x-form-trigger-over');
40077             t.addClassOnClick('x-form-trigger-click');
40078         }, this);
40079         this.triggers = ts.elements;
40080     },
40081
40082     onTrigger1Click : Roo.emptyFn,
40083     onTrigger2Click : Roo.emptyFn
40084 });/*
40085  * Based on:
40086  * Ext JS Library 1.1.1
40087  * Copyright(c) 2006-2007, Ext JS, LLC.
40088  *
40089  * Originally Released Under LGPL - original licence link has changed is not relivant.
40090  *
40091  * Fork - LGPL
40092  * <script type="text/javascript">
40093  */
40094  
40095 /**
40096  * @class Roo.form.TextArea
40097  * @extends Roo.form.TextField
40098  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
40099  * support for auto-sizing.
40100  * @constructor
40101  * Creates a new TextArea
40102  * @param {Object} config Configuration options
40103  */
40104 Roo.form.TextArea = function(config){
40105     Roo.form.TextArea.superclass.constructor.call(this, config);
40106     // these are provided exchanges for backwards compat
40107     // minHeight/maxHeight were replaced by growMin/growMax to be
40108     // compatible with TextField growing config values
40109     if(this.minHeight !== undefined){
40110         this.growMin = this.minHeight;
40111     }
40112     if(this.maxHeight !== undefined){
40113         this.growMax = this.maxHeight;
40114     }
40115 };
40116
40117 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
40118     /**
40119      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
40120      */
40121     growMin : 60,
40122     /**
40123      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
40124      */
40125     growMax: 1000,
40126     /**
40127      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
40128      * in the field (equivalent to setting overflow: hidden, defaults to false)
40129      */
40130     preventScrollbars: false,
40131     /**
40132      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
40133      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
40134      */
40135
40136     // private
40137     onRender : function(ct, position){
40138         if(!this.el){
40139             this.defaultAutoCreate = {
40140                 tag: "textarea",
40141                 style:"width:300px;height:60px;",
40142                 autocomplete: "new-password"
40143             };
40144         }
40145         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
40146         if(this.grow){
40147             this.textSizeEl = Roo.DomHelper.append(document.body, {
40148                 tag: "pre", cls: "x-form-grow-sizer"
40149             });
40150             if(this.preventScrollbars){
40151                 this.el.setStyle("overflow", "hidden");
40152             }
40153             this.el.setHeight(this.growMin);
40154         }
40155     },
40156
40157     onDestroy : function(){
40158         if(this.textSizeEl){
40159             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
40160         }
40161         Roo.form.TextArea.superclass.onDestroy.call(this);
40162     },
40163
40164     // private
40165     onKeyUp : function(e){
40166         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
40167             this.autoSize();
40168         }
40169     },
40170
40171     /**
40172      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
40173      * This only takes effect if grow = true, and fires the autosize event if the height changes.
40174      */
40175     autoSize : function(){
40176         if(!this.grow || !this.textSizeEl){
40177             return;
40178         }
40179         var el = this.el;
40180         var v = el.dom.value;
40181         var ts = this.textSizeEl;
40182
40183         ts.innerHTML = '';
40184         ts.appendChild(document.createTextNode(v));
40185         v = ts.innerHTML;
40186
40187         Roo.fly(ts).setWidth(this.el.getWidth());
40188         if(v.length < 1){
40189             v = "&#160;&#160;";
40190         }else{
40191             if(Roo.isIE){
40192                 v = v.replace(/\n/g, '<p>&#160;</p>');
40193             }
40194             v += "&#160;\n&#160;";
40195         }
40196         ts.innerHTML = v;
40197         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
40198         if(h != this.lastHeight){
40199             this.lastHeight = h;
40200             this.el.setHeight(h);
40201             this.fireEvent("autosize", this, h);
40202         }
40203     }
40204 });/*
40205  * Based on:
40206  * Ext JS Library 1.1.1
40207  * Copyright(c) 2006-2007, Ext JS, LLC.
40208  *
40209  * Originally Released Under LGPL - original licence link has changed is not relivant.
40210  *
40211  * Fork - LGPL
40212  * <script type="text/javascript">
40213  */
40214  
40215
40216 /**
40217  * @class Roo.form.NumberField
40218  * @extends Roo.form.TextField
40219  * Numeric text field that provides automatic keystroke filtering and numeric validation.
40220  * @constructor
40221  * Creates a new NumberField
40222  * @param {Object} config Configuration options
40223  */
40224 Roo.form.NumberField = function(config){
40225     Roo.form.NumberField.superclass.constructor.call(this, config);
40226 };
40227
40228 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
40229     /**
40230      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
40231      */
40232     fieldClass: "x-form-field x-form-num-field",
40233     /**
40234      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40235      */
40236     allowDecimals : true,
40237     /**
40238      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40239      */
40240     decimalSeparator : ".",
40241     /**
40242      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40243      */
40244     decimalPrecision : 2,
40245     /**
40246      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40247      */
40248     allowNegative : true,
40249     /**
40250      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40251      */
40252     minValue : Number.NEGATIVE_INFINITY,
40253     /**
40254      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40255      */
40256     maxValue : Number.MAX_VALUE,
40257     /**
40258      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40259      */
40260     minText : "The minimum value for this field is {0}",
40261     /**
40262      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40263      */
40264     maxText : "The maximum value for this field is {0}",
40265     /**
40266      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40267      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40268      */
40269     nanText : "{0} is not a valid number",
40270
40271     // private
40272     initEvents : function(){
40273         Roo.form.NumberField.superclass.initEvents.call(this);
40274         var allowed = "0123456789";
40275         if(this.allowDecimals){
40276             allowed += this.decimalSeparator;
40277         }
40278         if(this.allowNegative){
40279             allowed += "-";
40280         }
40281         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40282         var keyPress = function(e){
40283             var k = e.getKey();
40284             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40285                 return;
40286             }
40287             var c = e.getCharCode();
40288             if(allowed.indexOf(String.fromCharCode(c)) === -1){
40289                 e.stopEvent();
40290             }
40291         };
40292         this.el.on("keypress", keyPress, this);
40293     },
40294
40295     // private
40296     validateValue : function(value){
40297         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
40298             return false;
40299         }
40300         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
40301              return true;
40302         }
40303         var num = this.parseValue(value);
40304         if(isNaN(num)){
40305             this.markInvalid(String.format(this.nanText, value));
40306             return false;
40307         }
40308         if(num < this.minValue){
40309             this.markInvalid(String.format(this.minText, this.minValue));
40310             return false;
40311         }
40312         if(num > this.maxValue){
40313             this.markInvalid(String.format(this.maxText, this.maxValue));
40314             return false;
40315         }
40316         return true;
40317     },
40318
40319     getValue : function(){
40320         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
40321     },
40322
40323     // private
40324     parseValue : function(value){
40325         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40326         return isNaN(value) ? '' : value;
40327     },
40328
40329     // private
40330     fixPrecision : function(value){
40331         var nan = isNaN(value);
40332         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40333             return nan ? '' : value;
40334         }
40335         return parseFloat(value).toFixed(this.decimalPrecision);
40336     },
40337
40338     setValue : function(v){
40339         v = this.fixPrecision(v);
40340         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
40341     },
40342
40343     // private
40344     decimalPrecisionFcn : function(v){
40345         return Math.floor(v);
40346     },
40347
40348     beforeBlur : function(){
40349         var v = this.parseValue(this.getRawValue());
40350         if(v){
40351             this.setValue(v);
40352         }
40353     }
40354 });/*
40355  * Based on:
40356  * Ext JS Library 1.1.1
40357  * Copyright(c) 2006-2007, Ext JS, LLC.
40358  *
40359  * Originally Released Under LGPL - original licence link has changed is not relivant.
40360  *
40361  * Fork - LGPL
40362  * <script type="text/javascript">
40363  */
40364  
40365 /**
40366  * @class Roo.form.DateField
40367  * @extends Roo.form.TriggerField
40368  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
40369 * @constructor
40370 * Create a new DateField
40371 * @param {Object} config
40372  */
40373 Roo.form.DateField = function(config)
40374 {
40375     Roo.form.DateField.superclass.constructor.call(this, config);
40376     
40377       this.addEvents({
40378          
40379         /**
40380          * @event select
40381          * Fires when a date is selected
40382              * @param {Roo.form.DateField} combo This combo box
40383              * @param {Date} date The date selected
40384              */
40385         'select' : true
40386          
40387     });
40388     
40389     
40390     if(typeof this.minValue == "string") {
40391         this.minValue = this.parseDate(this.minValue);
40392     }
40393     if(typeof this.maxValue == "string") {
40394         this.maxValue = this.parseDate(this.maxValue);
40395     }
40396     this.ddMatch = null;
40397     if(this.disabledDates){
40398         var dd = this.disabledDates;
40399         var re = "(?:";
40400         for(var i = 0; i < dd.length; i++){
40401             re += dd[i];
40402             if(i != dd.length-1) {
40403                 re += "|";
40404             }
40405         }
40406         this.ddMatch = new RegExp(re + ")");
40407     }
40408 };
40409
40410 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
40411     /**
40412      * @cfg {String} format
40413      * The default date format string which can be overriden for localization support.  The format must be
40414      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
40415      */
40416     format : "m/d/y",
40417     /**
40418      * @cfg {String} altFormats
40419      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
40420      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
40421      */
40422     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
40423     /**
40424      * @cfg {Array} disabledDays
40425      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
40426      */
40427     disabledDays : null,
40428     /**
40429      * @cfg {String} disabledDaysText
40430      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
40431      */
40432     disabledDaysText : "Disabled",
40433     /**
40434      * @cfg {Array} disabledDates
40435      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
40436      * expression so they are very powerful. Some examples:
40437      * <ul>
40438      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
40439      * <li>["03/08", "09/16"] would disable those days for every year</li>
40440      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
40441      * <li>["03/../2006"] would disable every day in March 2006</li>
40442      * <li>["^03"] would disable every day in every March</li>
40443      * </ul>
40444      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
40445      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
40446      */
40447     disabledDates : null,
40448     /**
40449      * @cfg {String} disabledDatesText
40450      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
40451      */
40452     disabledDatesText : "Disabled",
40453     /**
40454      * @cfg {Date/String} minValue
40455      * The minimum allowed date. Can be either a Javascript date object or a string date in a
40456      * valid format (defaults to null).
40457      */
40458     minValue : null,
40459     /**
40460      * @cfg {Date/String} maxValue
40461      * The maximum allowed date. Can be either a Javascript date object or a string date in a
40462      * valid format (defaults to null).
40463      */
40464     maxValue : null,
40465     /**
40466      * @cfg {String} minText
40467      * The error text to display when the date in the cell is before minValue (defaults to
40468      * 'The date in this field must be after {minValue}').
40469      */
40470     minText : "The date in this field must be equal to or after {0}",
40471     /**
40472      * @cfg {String} maxText
40473      * The error text to display when the date in the cell is after maxValue (defaults to
40474      * 'The date in this field must be before {maxValue}').
40475      */
40476     maxText : "The date in this field must be equal to or before {0}",
40477     /**
40478      * @cfg {String} invalidText
40479      * The error text to display when the date in the field is invalid (defaults to
40480      * '{value} is not a valid date - it must be in the format {format}').
40481      */
40482     invalidText : "{0} is not a valid date - it must be in the format {1}",
40483     /**
40484      * @cfg {String} triggerClass
40485      * An additional CSS class used to style the trigger button.  The trigger will always get the
40486      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
40487      * which displays a calendar icon).
40488      */
40489     triggerClass : 'x-form-date-trigger',
40490     
40491
40492     /**
40493      * @cfg {Boolean} useIso
40494      * if enabled, then the date field will use a hidden field to store the 
40495      * real value as iso formated date. default (false)
40496      */ 
40497     useIso : false,
40498     /**
40499      * @cfg {String/Object} autoCreate
40500      * A DomHelper element spec, or true for a default element spec (defaults to
40501      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
40502      */ 
40503     // private
40504     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
40505     
40506     // private
40507     hiddenField: false,
40508     
40509     onRender : function(ct, position)
40510     {
40511         Roo.form.DateField.superclass.onRender.call(this, ct, position);
40512         if (this.useIso) {
40513             //this.el.dom.removeAttribute('name'); 
40514             Roo.log("Changing name?");
40515             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
40516             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
40517                     'before', true);
40518             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
40519             // prevent input submission
40520             this.hiddenName = this.name;
40521         }
40522             
40523             
40524     },
40525     
40526     // private
40527     validateValue : function(value)
40528     {
40529         value = this.formatDate(value);
40530         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
40531             Roo.log('super failed');
40532             return false;
40533         }
40534         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
40535              return true;
40536         }
40537         var svalue = value;
40538         value = this.parseDate(value);
40539         if(!value){
40540             Roo.log('parse date failed' + svalue);
40541             this.markInvalid(String.format(this.invalidText, svalue, this.format));
40542             return false;
40543         }
40544         var time = value.getTime();
40545         if(this.minValue && time < this.minValue.getTime()){
40546             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
40547             return false;
40548         }
40549         if(this.maxValue && time > this.maxValue.getTime()){
40550             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
40551             return false;
40552         }
40553         if(this.disabledDays){
40554             var day = value.getDay();
40555             for(var i = 0; i < this.disabledDays.length; i++) {
40556                 if(day === this.disabledDays[i]){
40557                     this.markInvalid(this.disabledDaysText);
40558                     return false;
40559                 }
40560             }
40561         }
40562         var fvalue = this.formatDate(value);
40563         if(this.ddMatch && this.ddMatch.test(fvalue)){
40564             this.markInvalid(String.format(this.disabledDatesText, fvalue));
40565             return false;
40566         }
40567         return true;
40568     },
40569
40570     // private
40571     // Provides logic to override the default TriggerField.validateBlur which just returns true
40572     validateBlur : function(){
40573         return !this.menu || !this.menu.isVisible();
40574     },
40575     
40576     getName: function()
40577     {
40578         // returns hidden if it's set..
40579         if (!this.rendered) {return ''};
40580         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
40581         
40582     },
40583
40584     /**
40585      * Returns the current date value of the date field.
40586      * @return {Date} The date value
40587      */
40588     getValue : function(){
40589         
40590         return  this.hiddenField ?
40591                 this.hiddenField.value :
40592                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
40593     },
40594
40595     /**
40596      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
40597      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
40598      * (the default format used is "m/d/y").
40599      * <br />Usage:
40600      * <pre><code>
40601 //All of these calls set the same date value (May 4, 2006)
40602
40603 //Pass a date object:
40604 var dt = new Date('5/4/06');
40605 dateField.setValue(dt);
40606
40607 //Pass a date string (default format):
40608 dateField.setValue('5/4/06');
40609
40610 //Pass a date string (custom format):
40611 dateField.format = 'Y-m-d';
40612 dateField.setValue('2006-5-4');
40613 </code></pre>
40614      * @param {String/Date} date The date or valid date string
40615      */
40616     setValue : function(date){
40617         if (this.hiddenField) {
40618             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
40619         }
40620         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
40621         // make sure the value field is always stored as a date..
40622         this.value = this.parseDate(date);
40623         
40624         
40625     },
40626
40627     // private
40628     parseDate : function(value){
40629         if(!value || value instanceof Date){
40630             return value;
40631         }
40632         var v = Date.parseDate(value, this.format);
40633          if (!v && this.useIso) {
40634             v = Date.parseDate(value, 'Y-m-d');
40635         }
40636         if(!v && this.altFormats){
40637             if(!this.altFormatsArray){
40638                 this.altFormatsArray = this.altFormats.split("|");
40639             }
40640             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
40641                 v = Date.parseDate(value, this.altFormatsArray[i]);
40642             }
40643         }
40644         return v;
40645     },
40646
40647     // private
40648     formatDate : function(date, fmt){
40649         return (!date || !(date instanceof Date)) ?
40650                date : date.dateFormat(fmt || this.format);
40651     },
40652
40653     // private
40654     menuListeners : {
40655         select: function(m, d){
40656             
40657             this.setValue(d);
40658             this.fireEvent('select', this, d);
40659         },
40660         show : function(){ // retain focus styling
40661             this.onFocus();
40662         },
40663         hide : function(){
40664             this.focus.defer(10, this);
40665             var ml = this.menuListeners;
40666             this.menu.un("select", ml.select,  this);
40667             this.menu.un("show", ml.show,  this);
40668             this.menu.un("hide", ml.hide,  this);
40669         }
40670     },
40671
40672     // private
40673     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
40674     onTriggerClick : function(){
40675         if(this.disabled){
40676             return;
40677         }
40678         if(this.menu == null){
40679             this.menu = new Roo.menu.DateMenu();
40680         }
40681         Roo.apply(this.menu.picker,  {
40682             showClear: this.allowBlank,
40683             minDate : this.minValue,
40684             maxDate : this.maxValue,
40685             disabledDatesRE : this.ddMatch,
40686             disabledDatesText : this.disabledDatesText,
40687             disabledDays : this.disabledDays,
40688             disabledDaysText : this.disabledDaysText,
40689             format : this.useIso ? 'Y-m-d' : this.format,
40690             minText : String.format(this.minText, this.formatDate(this.minValue)),
40691             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
40692         });
40693         this.menu.on(Roo.apply({}, this.menuListeners, {
40694             scope:this
40695         }));
40696         this.menu.picker.setValue(this.getValue() || new Date());
40697         this.menu.show(this.el, "tl-bl?");
40698     },
40699
40700     beforeBlur : function(){
40701         var v = this.parseDate(this.getRawValue());
40702         if(v){
40703             this.setValue(v);
40704         }
40705     },
40706
40707     /*@
40708      * overide
40709      * 
40710      */
40711     isDirty : function() {
40712         if(this.disabled) {
40713             return false;
40714         }
40715         
40716         if(typeof(this.startValue) === 'undefined'){
40717             return false;
40718         }
40719         
40720         return String(this.getValue()) !== String(this.startValue);
40721         
40722     },
40723     // @overide
40724     cleanLeadingSpace : function(e)
40725     {
40726        return;
40727     }
40728     
40729 });/*
40730  * Based on:
40731  * Ext JS Library 1.1.1
40732  * Copyright(c) 2006-2007, Ext JS, LLC.
40733  *
40734  * Originally Released Under LGPL - original licence link has changed is not relivant.
40735  *
40736  * Fork - LGPL
40737  * <script type="text/javascript">
40738  */
40739  
40740 /**
40741  * @class Roo.form.MonthField
40742  * @extends Roo.form.TriggerField
40743  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
40744 * @constructor
40745 * Create a new MonthField
40746 * @param {Object} config
40747  */
40748 Roo.form.MonthField = function(config){
40749     
40750     Roo.form.MonthField.superclass.constructor.call(this, config);
40751     
40752       this.addEvents({
40753          
40754         /**
40755          * @event select
40756          * Fires when a date is selected
40757              * @param {Roo.form.MonthFieeld} combo This combo box
40758              * @param {Date} date The date selected
40759              */
40760         'select' : true
40761          
40762     });
40763     
40764     
40765     if(typeof this.minValue == "string") {
40766         this.minValue = this.parseDate(this.minValue);
40767     }
40768     if(typeof this.maxValue == "string") {
40769         this.maxValue = this.parseDate(this.maxValue);
40770     }
40771     this.ddMatch = null;
40772     if(this.disabledDates){
40773         var dd = this.disabledDates;
40774         var re = "(?:";
40775         for(var i = 0; i < dd.length; i++){
40776             re += dd[i];
40777             if(i != dd.length-1) {
40778                 re += "|";
40779             }
40780         }
40781         this.ddMatch = new RegExp(re + ")");
40782     }
40783 };
40784
40785 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
40786     /**
40787      * @cfg {String} format
40788      * The default date format string which can be overriden for localization support.  The format must be
40789      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
40790      */
40791     format : "M Y",
40792     /**
40793      * @cfg {String} altFormats
40794      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
40795      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
40796      */
40797     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
40798     /**
40799      * @cfg {Array} disabledDays
40800      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
40801      */
40802     disabledDays : [0,1,2,3,4,5,6],
40803     /**
40804      * @cfg {String} disabledDaysText
40805      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
40806      */
40807     disabledDaysText : "Disabled",
40808     /**
40809      * @cfg {Array} disabledDates
40810      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
40811      * expression so they are very powerful. Some examples:
40812      * <ul>
40813      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
40814      * <li>["03/08", "09/16"] would disable those days for every year</li>
40815      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
40816      * <li>["03/../2006"] would disable every day in March 2006</li>
40817      * <li>["^03"] would disable every day in every March</li>
40818      * </ul>
40819      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
40820      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
40821      */
40822     disabledDates : null,
40823     /**
40824      * @cfg {String} disabledDatesText
40825      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
40826      */
40827     disabledDatesText : "Disabled",
40828     /**
40829      * @cfg {Date/String} minValue
40830      * The minimum allowed date. Can be either a Javascript date object or a string date in a
40831      * valid format (defaults to null).
40832      */
40833     minValue : null,
40834     /**
40835      * @cfg {Date/String} maxValue
40836      * The maximum allowed date. Can be either a Javascript date object or a string date in a
40837      * valid format (defaults to null).
40838      */
40839     maxValue : null,
40840     /**
40841      * @cfg {String} minText
40842      * The error text to display when the date in the cell is before minValue (defaults to
40843      * 'The date in this field must be after {minValue}').
40844      */
40845     minText : "The date in this field must be equal to or after {0}",
40846     /**
40847      * @cfg {String} maxTextf
40848      * The error text to display when the date in the cell is after maxValue (defaults to
40849      * 'The date in this field must be before {maxValue}').
40850      */
40851     maxText : "The date in this field must be equal to or before {0}",
40852     /**
40853      * @cfg {String} invalidText
40854      * The error text to display when the date in the field is invalid (defaults to
40855      * '{value} is not a valid date - it must be in the format {format}').
40856      */
40857     invalidText : "{0} is not a valid date - it must be in the format {1}",
40858     /**
40859      * @cfg {String} triggerClass
40860      * An additional CSS class used to style the trigger button.  The trigger will always get the
40861      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
40862      * which displays a calendar icon).
40863      */
40864     triggerClass : 'x-form-date-trigger',
40865     
40866
40867     /**
40868      * @cfg {Boolean} useIso
40869      * if enabled, then the date field will use a hidden field to store the 
40870      * real value as iso formated date. default (true)
40871      */ 
40872     useIso : true,
40873     /**
40874      * @cfg {String/Object} autoCreate
40875      * A DomHelper element spec, or true for a default element spec (defaults to
40876      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
40877      */ 
40878     // private
40879     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
40880     
40881     // private
40882     hiddenField: false,
40883     
40884     hideMonthPicker : false,
40885     
40886     onRender : function(ct, position)
40887     {
40888         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
40889         if (this.useIso) {
40890             this.el.dom.removeAttribute('name'); 
40891             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
40892                     'before', true);
40893             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
40894             // prevent input submission
40895             this.hiddenName = this.name;
40896         }
40897             
40898             
40899     },
40900     
40901     // private
40902     validateValue : function(value)
40903     {
40904         value = this.formatDate(value);
40905         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
40906             return false;
40907         }
40908         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
40909              return true;
40910         }
40911         var svalue = value;
40912         value = this.parseDate(value);
40913         if(!value){
40914             this.markInvalid(String.format(this.invalidText, svalue, this.format));
40915             return false;
40916         }
40917         var time = value.getTime();
40918         if(this.minValue && time < this.minValue.getTime()){
40919             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
40920             return false;
40921         }
40922         if(this.maxValue && time > this.maxValue.getTime()){
40923             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
40924             return false;
40925         }
40926         /*if(this.disabledDays){
40927             var day = value.getDay();
40928             for(var i = 0; i < this.disabledDays.length; i++) {
40929                 if(day === this.disabledDays[i]){
40930                     this.markInvalid(this.disabledDaysText);
40931                     return false;
40932                 }
40933             }
40934         }
40935         */
40936         var fvalue = this.formatDate(value);
40937         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
40938             this.markInvalid(String.format(this.disabledDatesText, fvalue));
40939             return false;
40940         }
40941         */
40942         return true;
40943     },
40944
40945     // private
40946     // Provides logic to override the default TriggerField.validateBlur which just returns true
40947     validateBlur : function(){
40948         return !this.menu || !this.menu.isVisible();
40949     },
40950
40951     /**
40952      * Returns the current date value of the date field.
40953      * @return {Date} The date value
40954      */
40955     getValue : function(){
40956         
40957         
40958         
40959         return  this.hiddenField ?
40960                 this.hiddenField.value :
40961                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
40962     },
40963
40964     /**
40965      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
40966      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
40967      * (the default format used is "m/d/y").
40968      * <br />Usage:
40969      * <pre><code>
40970 //All of these calls set the same date value (May 4, 2006)
40971
40972 //Pass a date object:
40973 var dt = new Date('5/4/06');
40974 monthField.setValue(dt);
40975
40976 //Pass a date string (default format):
40977 monthField.setValue('5/4/06');
40978
40979 //Pass a date string (custom format):
40980 monthField.format = 'Y-m-d';
40981 monthField.setValue('2006-5-4');
40982 </code></pre>
40983      * @param {String/Date} date The date or valid date string
40984      */
40985     setValue : function(date){
40986         Roo.log('month setValue' + date);
40987         // can only be first of month..
40988         
40989         var val = this.parseDate(date);
40990         
40991         if (this.hiddenField) {
40992             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
40993         }
40994         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
40995         this.value = this.parseDate(date);
40996     },
40997
40998     // private
40999     parseDate : function(value){
41000         if(!value || value instanceof Date){
41001             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
41002             return value;
41003         }
41004         var v = Date.parseDate(value, this.format);
41005         if (!v && this.useIso) {
41006             v = Date.parseDate(value, 'Y-m-d');
41007         }
41008         if (v) {
41009             // 
41010             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
41011         }
41012         
41013         
41014         if(!v && this.altFormats){
41015             if(!this.altFormatsArray){
41016                 this.altFormatsArray = this.altFormats.split("|");
41017             }
41018             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
41019                 v = Date.parseDate(value, this.altFormatsArray[i]);
41020             }
41021         }
41022         return v;
41023     },
41024
41025     // private
41026     formatDate : function(date, fmt){
41027         return (!date || !(date instanceof Date)) ?
41028                date : date.dateFormat(fmt || this.format);
41029     },
41030
41031     // private
41032     menuListeners : {
41033         select: function(m, d){
41034             this.setValue(d);
41035             this.fireEvent('select', this, d);
41036         },
41037         show : function(){ // retain focus styling
41038             this.onFocus();
41039         },
41040         hide : function(){
41041             this.focus.defer(10, this);
41042             var ml = this.menuListeners;
41043             this.menu.un("select", ml.select,  this);
41044             this.menu.un("show", ml.show,  this);
41045             this.menu.un("hide", ml.hide,  this);
41046         }
41047     },
41048     // private
41049     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
41050     onTriggerClick : function(){
41051         if(this.disabled){
41052             return;
41053         }
41054         if(this.menu == null){
41055             this.menu = new Roo.menu.DateMenu();
41056            
41057         }
41058         
41059         Roo.apply(this.menu.picker,  {
41060             
41061             showClear: this.allowBlank,
41062             minDate : this.minValue,
41063             maxDate : this.maxValue,
41064             disabledDatesRE : this.ddMatch,
41065             disabledDatesText : this.disabledDatesText,
41066             
41067             format : this.useIso ? 'Y-m-d' : this.format,
41068             minText : String.format(this.minText, this.formatDate(this.minValue)),
41069             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
41070             
41071         });
41072          this.menu.on(Roo.apply({}, this.menuListeners, {
41073             scope:this
41074         }));
41075        
41076         
41077         var m = this.menu;
41078         var p = m.picker;
41079         
41080         // hide month picker get's called when we called by 'before hide';
41081         
41082         var ignorehide = true;
41083         p.hideMonthPicker  = function(disableAnim){
41084             if (ignorehide) {
41085                 return;
41086             }
41087              if(this.monthPicker){
41088                 Roo.log("hideMonthPicker called");
41089                 if(disableAnim === true){
41090                     this.monthPicker.hide();
41091                 }else{
41092                     this.monthPicker.slideOut('t', {duration:.2});
41093                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
41094                     p.fireEvent("select", this, this.value);
41095                     m.hide();
41096                 }
41097             }
41098         }
41099         
41100         Roo.log('picker set value');
41101         Roo.log(this.getValue());
41102         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
41103         m.show(this.el, 'tl-bl?');
41104         ignorehide  = false;
41105         // this will trigger hideMonthPicker..
41106         
41107         
41108         // hidden the day picker
41109         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
41110         
41111         
41112         
41113       
41114         
41115         p.showMonthPicker.defer(100, p);
41116     
41117         
41118        
41119     },
41120
41121     beforeBlur : function(){
41122         var v = this.parseDate(this.getRawValue());
41123         if(v){
41124             this.setValue(v);
41125         }
41126     }
41127
41128     /** @cfg {Boolean} grow @hide */
41129     /** @cfg {Number} growMin @hide */
41130     /** @cfg {Number} growMax @hide */
41131     /**
41132      * @hide
41133      * @method autoSize
41134      */
41135 });/*
41136  * Based on:
41137  * Ext JS Library 1.1.1
41138  * Copyright(c) 2006-2007, Ext JS, LLC.
41139  *
41140  * Originally Released Under LGPL - original licence link has changed is not relivant.
41141  *
41142  * Fork - LGPL
41143  * <script type="text/javascript">
41144  */
41145  
41146
41147 /**
41148  * @class Roo.form.ComboBox
41149  * @extends Roo.form.TriggerField
41150  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
41151  * @constructor
41152  * Create a new ComboBox.
41153  * @param {Object} config Configuration options
41154  */
41155 Roo.form.ComboBox = function(config){
41156     Roo.form.ComboBox.superclass.constructor.call(this, config);
41157     this.addEvents({
41158         /**
41159          * @event expand
41160          * Fires when the dropdown list is expanded
41161              * @param {Roo.form.ComboBox} combo This combo box
41162              */
41163         'expand' : true,
41164         /**
41165          * @event collapse
41166          * Fires when the dropdown list is collapsed
41167              * @param {Roo.form.ComboBox} combo This combo box
41168              */
41169         'collapse' : true,
41170         /**
41171          * @event beforeselect
41172          * Fires before a list item is selected. Return false to cancel the selection.
41173              * @param {Roo.form.ComboBox} combo This combo box
41174              * @param {Roo.data.Record} record The data record returned from the underlying store
41175              * @param {Number} index The index of the selected item in the dropdown list
41176              */
41177         'beforeselect' : true,
41178         /**
41179          * @event select
41180          * Fires when a list item is selected
41181              * @param {Roo.form.ComboBox} combo This combo box
41182              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
41183              * @param {Number} index The index of the selected item in the dropdown list
41184              */
41185         'select' : true,
41186         /**
41187          * @event beforequery
41188          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
41189          * The event object passed has these properties:
41190              * @param {Roo.form.ComboBox} combo This combo box
41191              * @param {String} query The query
41192              * @param {Boolean} forceAll true to force "all" query
41193              * @param {Boolean} cancel true to cancel the query
41194              * @param {Object} e The query event object
41195              */
41196         'beforequery': true,
41197          /**
41198          * @event add
41199          * Fires when the 'add' icon is pressed (add a listener to enable add button)
41200              * @param {Roo.form.ComboBox} combo This combo box
41201              */
41202         'add' : true,
41203         /**
41204          * @event edit
41205          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
41206              * @param {Roo.form.ComboBox} combo This combo box
41207              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
41208              */
41209         'edit' : true
41210         
41211         
41212     });
41213     if(this.transform){
41214         this.allowDomMove = false;
41215         var s = Roo.getDom(this.transform);
41216         if(!this.hiddenName){
41217             this.hiddenName = s.name;
41218         }
41219         if(!this.store){
41220             this.mode = 'local';
41221             var d = [], opts = s.options;
41222             for(var i = 0, len = opts.length;i < len; i++){
41223                 var o = opts[i];
41224                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
41225                 if(o.selected) {
41226                     this.value = value;
41227                 }
41228                 d.push([value, o.text]);
41229             }
41230             this.store = new Roo.data.SimpleStore({
41231                 'id': 0,
41232                 fields: ['value', 'text'],
41233                 data : d
41234             });
41235             this.valueField = 'value';
41236             this.displayField = 'text';
41237         }
41238         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
41239         if(!this.lazyRender){
41240             this.target = true;
41241             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
41242             s.parentNode.removeChild(s); // remove it
41243             this.render(this.el.parentNode);
41244         }else{
41245             s.parentNode.removeChild(s); // remove it
41246         }
41247
41248     }
41249     if (this.store) {
41250         this.store = Roo.factory(this.store, Roo.data);
41251     }
41252     
41253     this.selectedIndex = -1;
41254     if(this.mode == 'local'){
41255         if(config.queryDelay === undefined){
41256             this.queryDelay = 10;
41257         }
41258         if(config.minChars === undefined){
41259             this.minChars = 0;
41260         }
41261     }
41262 };
41263
41264 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
41265     /**
41266      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
41267      */
41268     /**
41269      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
41270      * rendering into an Roo.Editor, defaults to false)
41271      */
41272     /**
41273      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
41274      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
41275      */
41276     /**
41277      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
41278      */
41279     /**
41280      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
41281      * the dropdown list (defaults to undefined, with no header element)
41282      */
41283
41284      /**
41285      * @cfg {String/Roo.Template} tpl The template to use to render the output
41286      */
41287      
41288     // private
41289     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
41290     /**
41291      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
41292      */
41293     listWidth: undefined,
41294     /**
41295      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
41296      * mode = 'remote' or 'text' if mode = 'local')
41297      */
41298     displayField: undefined,
41299     /**
41300      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
41301      * mode = 'remote' or 'value' if mode = 'local'). 
41302      * Note: use of a valueField requires the user make a selection
41303      * in order for a value to be mapped.
41304      */
41305     valueField: undefined,
41306     
41307     
41308     /**
41309      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
41310      * field's data value (defaults to the underlying DOM element's name)
41311      */
41312     hiddenName: undefined,
41313     /**
41314      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
41315      */
41316     listClass: '',
41317     /**
41318      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
41319      */
41320     selectedClass: 'x-combo-selected',
41321     /**
41322      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
41323      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
41324      * which displays a downward arrow icon).
41325      */
41326     triggerClass : 'x-form-arrow-trigger',
41327     /**
41328      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
41329      */
41330     shadow:'sides',
41331     /**
41332      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
41333      * anchor positions (defaults to 'tl-bl')
41334      */
41335     listAlign: 'tl-bl?',
41336     /**
41337      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
41338      */
41339     maxHeight: 300,
41340     /**
41341      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
41342      * query specified by the allQuery config option (defaults to 'query')
41343      */
41344     triggerAction: 'query',
41345     /**
41346      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
41347      * (defaults to 4, does not apply if editable = false)
41348      */
41349     minChars : 4,
41350     /**
41351      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
41352      * delay (typeAheadDelay) if it matches a known value (defaults to false)
41353      */
41354     typeAhead: false,
41355     /**
41356      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
41357      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
41358      */
41359     queryDelay: 500,
41360     /**
41361      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
41362      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
41363      */
41364     pageSize: 0,
41365     /**
41366      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
41367      * when editable = true (defaults to false)
41368      */
41369     selectOnFocus:false,
41370     /**
41371      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
41372      */
41373     queryParam: 'query',
41374     /**
41375      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
41376      * when mode = 'remote' (defaults to 'Loading...')
41377      */
41378     loadingText: 'Loading...',
41379     /**
41380      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
41381      */
41382     resizable: false,
41383     /**
41384      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
41385      */
41386     handleHeight : 8,
41387     /**
41388      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
41389      * traditional select (defaults to true)
41390      */
41391     editable: true,
41392     /**
41393      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
41394      */
41395     allQuery: '',
41396     /**
41397      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
41398      */
41399     mode: 'remote',
41400     /**
41401      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
41402      * listWidth has a higher value)
41403      */
41404     minListWidth : 70,
41405     /**
41406      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
41407      * allow the user to set arbitrary text into the field (defaults to false)
41408      */
41409     forceSelection:false,
41410     /**
41411      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
41412      * if typeAhead = true (defaults to 250)
41413      */
41414     typeAheadDelay : 250,
41415     /**
41416      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
41417      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
41418      */
41419     valueNotFoundText : undefined,
41420     /**
41421      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
41422      */
41423     blockFocus : false,
41424     
41425     /**
41426      * @cfg {Boolean} disableClear Disable showing of clear button.
41427      */
41428     disableClear : false,
41429     /**
41430      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
41431      */
41432     alwaysQuery : false,
41433     
41434     //private
41435     addicon : false,
41436     editicon: false,
41437     
41438     // element that contains real text value.. (when hidden is used..)
41439      
41440     // private
41441     onRender : function(ct, position)
41442     {
41443         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
41444         
41445         if(this.hiddenName){
41446             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
41447                     'before', true);
41448             this.hiddenField.value =
41449                 this.hiddenValue !== undefined ? this.hiddenValue :
41450                 this.value !== undefined ? this.value : '';
41451
41452             // prevent input submission
41453             this.el.dom.removeAttribute('name');
41454              
41455              
41456         }
41457         
41458         if(Roo.isGecko){
41459             this.el.dom.setAttribute('autocomplete', 'off');
41460         }
41461
41462         var cls = 'x-combo-list';
41463
41464         this.list = new Roo.Layer({
41465             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
41466         });
41467
41468         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
41469         this.list.setWidth(lw);
41470         this.list.swallowEvent('mousewheel');
41471         this.assetHeight = 0;
41472
41473         if(this.title){
41474             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
41475             this.assetHeight += this.header.getHeight();
41476         }
41477
41478         this.innerList = this.list.createChild({cls:cls+'-inner'});
41479         this.innerList.on('mouseover', this.onViewOver, this);
41480         this.innerList.on('mousemove', this.onViewMove, this);
41481         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
41482         
41483         if(this.allowBlank && !this.pageSize && !this.disableClear){
41484             this.footer = this.list.createChild({cls:cls+'-ft'});
41485             this.pageTb = new Roo.Toolbar(this.footer);
41486            
41487         }
41488         if(this.pageSize){
41489             this.footer = this.list.createChild({cls:cls+'-ft'});
41490             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
41491                     {pageSize: this.pageSize});
41492             
41493         }
41494         
41495         if (this.pageTb && this.allowBlank && !this.disableClear) {
41496             var _this = this;
41497             this.pageTb.add(new Roo.Toolbar.Fill(), {
41498                 cls: 'x-btn-icon x-btn-clear',
41499                 text: '&#160;',
41500                 handler: function()
41501                 {
41502                     _this.collapse();
41503                     _this.clearValue();
41504                     _this.onSelect(false, -1);
41505                 }
41506             });
41507         }
41508         if (this.footer) {
41509             this.assetHeight += this.footer.getHeight();
41510         }
41511         
41512
41513         if(!this.tpl){
41514             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
41515         }
41516
41517         this.view = new Roo.View(this.innerList, this.tpl, {
41518             singleSelect:true,
41519             store: this.store,
41520             selectedClass: this.selectedClass
41521         });
41522
41523         this.view.on('click', this.onViewClick, this);
41524
41525         this.store.on('beforeload', this.onBeforeLoad, this);
41526         this.store.on('load', this.onLoad, this);
41527         this.store.on('loadexception', this.onLoadException, this);
41528
41529         if(this.resizable){
41530             this.resizer = new Roo.Resizable(this.list,  {
41531                pinned:true, handles:'se'
41532             });
41533             this.resizer.on('resize', function(r, w, h){
41534                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
41535                 this.listWidth = w;
41536                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
41537                 this.restrictHeight();
41538             }, this);
41539             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
41540         }
41541         if(!this.editable){
41542             this.editable = true;
41543             this.setEditable(false);
41544         }  
41545         
41546         
41547         if (typeof(this.events.add.listeners) != 'undefined') {
41548             
41549             this.addicon = this.wrap.createChild(
41550                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
41551        
41552             this.addicon.on('click', function(e) {
41553                 this.fireEvent('add', this);
41554             }, this);
41555         }
41556         if (typeof(this.events.edit.listeners) != 'undefined') {
41557             
41558             this.editicon = this.wrap.createChild(
41559                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
41560             if (this.addicon) {
41561                 this.editicon.setStyle('margin-left', '40px');
41562             }
41563             this.editicon.on('click', function(e) {
41564                 
41565                 // we fire even  if inothing is selected..
41566                 this.fireEvent('edit', this, this.lastData );
41567                 
41568             }, this);
41569         }
41570         
41571         
41572         
41573     },
41574
41575     // private
41576     initEvents : function(){
41577         Roo.form.ComboBox.superclass.initEvents.call(this);
41578
41579         this.keyNav = new Roo.KeyNav(this.el, {
41580             "up" : function(e){
41581                 this.inKeyMode = true;
41582                 this.selectPrev();
41583             },
41584
41585             "down" : function(e){
41586                 if(!this.isExpanded()){
41587                     this.onTriggerClick();
41588                 }else{
41589                     this.inKeyMode = true;
41590                     this.selectNext();
41591                 }
41592             },
41593
41594             "enter" : function(e){
41595                 this.onViewClick();
41596                 //return true;
41597             },
41598
41599             "esc" : function(e){
41600                 this.collapse();
41601             },
41602
41603             "tab" : function(e){
41604                 this.onViewClick(false);
41605                 this.fireEvent("specialkey", this, e);
41606                 return true;
41607             },
41608
41609             scope : this,
41610
41611             doRelay : function(foo, bar, hname){
41612                 if(hname == 'down' || this.scope.isExpanded()){
41613                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
41614                 }
41615                 return true;
41616             },
41617
41618             forceKeyDown: true
41619         });
41620         this.queryDelay = Math.max(this.queryDelay || 10,
41621                 this.mode == 'local' ? 10 : 250);
41622         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
41623         if(this.typeAhead){
41624             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
41625         }
41626         if(this.editable !== false){
41627             this.el.on("keyup", this.onKeyUp, this);
41628         }
41629         if(this.forceSelection){
41630             this.on('blur', this.doForce, this);
41631         }
41632     },
41633
41634     onDestroy : function(){
41635         if(this.view){
41636             this.view.setStore(null);
41637             this.view.el.removeAllListeners();
41638             this.view.el.remove();
41639             this.view.purgeListeners();
41640         }
41641         if(this.list){
41642             this.list.destroy();
41643         }
41644         if(this.store){
41645             this.store.un('beforeload', this.onBeforeLoad, this);
41646             this.store.un('load', this.onLoad, this);
41647             this.store.un('loadexception', this.onLoadException, this);
41648         }
41649         Roo.form.ComboBox.superclass.onDestroy.call(this);
41650     },
41651
41652     // private
41653     fireKey : function(e){
41654         if(e.isNavKeyPress() && !this.list.isVisible()){
41655             this.fireEvent("specialkey", this, e);
41656         }
41657     },
41658
41659     // private
41660     onResize: function(w, h){
41661         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
41662         
41663         if(typeof w != 'number'){
41664             // we do not handle it!?!?
41665             return;
41666         }
41667         var tw = this.trigger.getWidth();
41668         tw += this.addicon ? this.addicon.getWidth() : 0;
41669         tw += this.editicon ? this.editicon.getWidth() : 0;
41670         var x = w - tw;
41671         this.el.setWidth( this.adjustWidth('input', x));
41672             
41673         this.trigger.setStyle('left', x+'px');
41674         
41675         if(this.list && this.listWidth === undefined){
41676             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
41677             this.list.setWidth(lw);
41678             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
41679         }
41680         
41681     
41682         
41683     },
41684
41685     /**
41686      * Allow or prevent the user from directly editing the field text.  If false is passed,
41687      * the user will only be able to select from the items defined in the dropdown list.  This method
41688      * is the runtime equivalent of setting the 'editable' config option at config time.
41689      * @param {Boolean} value True to allow the user to directly edit the field text
41690      */
41691     setEditable : function(value){
41692         if(value == this.editable){
41693             return;
41694         }
41695         this.editable = value;
41696         if(!value){
41697             this.el.dom.setAttribute('readOnly', true);
41698             this.el.on('mousedown', this.onTriggerClick,  this);
41699             this.el.addClass('x-combo-noedit');
41700         }else{
41701             this.el.dom.setAttribute('readOnly', false);
41702             this.el.un('mousedown', this.onTriggerClick,  this);
41703             this.el.removeClass('x-combo-noedit');
41704         }
41705     },
41706
41707     // private
41708     onBeforeLoad : function(){
41709         if(!this.hasFocus){
41710             return;
41711         }
41712         this.innerList.update(this.loadingText ?
41713                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
41714         this.restrictHeight();
41715         this.selectedIndex = -1;
41716     },
41717
41718     // private
41719     onLoad : function(){
41720         if(!this.hasFocus){
41721             return;
41722         }
41723         if(this.store.getCount() > 0){
41724             this.expand();
41725             this.restrictHeight();
41726             if(this.lastQuery == this.allQuery){
41727                 if(this.editable){
41728                     this.el.dom.select();
41729                 }
41730                 if(!this.selectByValue(this.value, true)){
41731                     this.select(0, true);
41732                 }
41733             }else{
41734                 this.selectNext();
41735                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
41736                     this.taTask.delay(this.typeAheadDelay);
41737                 }
41738             }
41739         }else{
41740             this.onEmptyResults();
41741         }
41742         //this.el.focus();
41743     },
41744     // private
41745     onLoadException : function()
41746     {
41747         this.collapse();
41748         Roo.log(this.store.reader.jsonData);
41749         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
41750             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
41751         }
41752         
41753         
41754     },
41755     // private
41756     onTypeAhead : function(){
41757         if(this.store.getCount() > 0){
41758             var r = this.store.getAt(0);
41759             var newValue = r.data[this.displayField];
41760             var len = newValue.length;
41761             var selStart = this.getRawValue().length;
41762             if(selStart != len){
41763                 this.setRawValue(newValue);
41764                 this.selectText(selStart, newValue.length);
41765             }
41766         }
41767     },
41768
41769     // private
41770     onSelect : function(record, index){
41771         if(this.fireEvent('beforeselect', this, record, index) !== false){
41772             this.setFromData(index > -1 ? record.data : false);
41773             this.collapse();
41774             this.fireEvent('select', this, record, index);
41775         }
41776     },
41777
41778     /**
41779      * Returns the currently selected field value or empty string if no value is set.
41780      * @return {String} value The selected value
41781      */
41782     getValue : function(){
41783         if(this.valueField){
41784             return typeof this.value != 'undefined' ? this.value : '';
41785         }
41786         return Roo.form.ComboBox.superclass.getValue.call(this);
41787     },
41788
41789     /**
41790      * Clears any text/value currently set in the field
41791      */
41792     clearValue : function(){
41793         if(this.hiddenField){
41794             this.hiddenField.value = '';
41795         }
41796         this.value = '';
41797         this.setRawValue('');
41798         this.lastSelectionText = '';
41799         
41800     },
41801
41802     /**
41803      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
41804      * will be displayed in the field.  If the value does not match the data value of an existing item,
41805      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
41806      * Otherwise the field will be blank (although the value will still be set).
41807      * @param {String} value The value to match
41808      */
41809     setValue : function(v){
41810         var text = v;
41811         if(this.valueField){
41812             var r = this.findRecord(this.valueField, v);
41813             if(r){
41814                 text = r.data[this.displayField];
41815             }else if(this.valueNotFoundText !== undefined){
41816                 text = this.valueNotFoundText;
41817             }
41818         }
41819         this.lastSelectionText = text;
41820         if(this.hiddenField){
41821             this.hiddenField.value = v;
41822         }
41823         Roo.form.ComboBox.superclass.setValue.call(this, text);
41824         this.value = v;
41825     },
41826     /**
41827      * @property {Object} the last set data for the element
41828      */
41829     
41830     lastData : false,
41831     /**
41832      * Sets the value of the field based on a object which is related to the record format for the store.
41833      * @param {Object} value the value to set as. or false on reset?
41834      */
41835     setFromData : function(o){
41836         var dv = ''; // display value
41837         var vv = ''; // value value..
41838         this.lastData = o;
41839         if (this.displayField) {
41840             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
41841         } else {
41842             // this is an error condition!!!
41843             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
41844         }
41845         
41846         if(this.valueField){
41847             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
41848         }
41849         if(this.hiddenField){
41850             this.hiddenField.value = vv;
41851             
41852             this.lastSelectionText = dv;
41853             Roo.form.ComboBox.superclass.setValue.call(this, dv);
41854             this.value = vv;
41855             return;
41856         }
41857         // no hidden field.. - we store the value in 'value', but still display
41858         // display field!!!!
41859         this.lastSelectionText = dv;
41860         Roo.form.ComboBox.superclass.setValue.call(this, dv);
41861         this.value = vv;
41862         
41863         
41864     },
41865     // private
41866     reset : function(){
41867         // overridden so that last data is reset..
41868         this.setValue(this.resetValue);
41869         this.originalValue = this.getValue();
41870         this.clearInvalid();
41871         this.lastData = false;
41872         if (this.view) {
41873             this.view.clearSelections();
41874         }
41875     },
41876     // private
41877     findRecord : function(prop, value){
41878         var record;
41879         if(this.store.getCount() > 0){
41880             this.store.each(function(r){
41881                 if(r.data[prop] == value){
41882                     record = r;
41883                     return false;
41884                 }
41885                 return true;
41886             });
41887         }
41888         return record;
41889     },
41890     
41891     getName: function()
41892     {
41893         // returns hidden if it's set..
41894         if (!this.rendered) {return ''};
41895         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
41896         
41897     },
41898     // private
41899     onViewMove : function(e, t){
41900         this.inKeyMode = false;
41901     },
41902
41903     // private
41904     onViewOver : function(e, t){
41905         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
41906             return;
41907         }
41908         var item = this.view.findItemFromChild(t);
41909         if(item){
41910             var index = this.view.indexOf(item);
41911             this.select(index, false);
41912         }
41913     },
41914
41915     // private
41916     onViewClick : function(doFocus)
41917     {
41918         var index = this.view.getSelectedIndexes()[0];
41919         var r = this.store.getAt(index);
41920         if(r){
41921             this.onSelect(r, index);
41922         }
41923         if(doFocus !== false && !this.blockFocus){
41924             this.el.focus();
41925         }
41926     },
41927
41928     // private
41929     restrictHeight : function(){
41930         this.innerList.dom.style.height = '';
41931         var inner = this.innerList.dom;
41932         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
41933         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
41934         this.list.beginUpdate();
41935         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
41936         this.list.alignTo(this.el, this.listAlign);
41937         this.list.endUpdate();
41938     },
41939
41940     // private
41941     onEmptyResults : function(){
41942         this.collapse();
41943     },
41944
41945     /**
41946      * Returns true if the dropdown list is expanded, else false.
41947      */
41948     isExpanded : function(){
41949         return this.list.isVisible();
41950     },
41951
41952     /**
41953      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
41954      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
41955      * @param {String} value The data value of the item to select
41956      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
41957      * selected item if it is not currently in view (defaults to true)
41958      * @return {Boolean} True if the value matched an item in the list, else false
41959      */
41960     selectByValue : function(v, scrollIntoView){
41961         if(v !== undefined && v !== null){
41962             var r = this.findRecord(this.valueField || this.displayField, v);
41963             if(r){
41964                 this.select(this.store.indexOf(r), scrollIntoView);
41965                 return true;
41966             }
41967         }
41968         return false;
41969     },
41970
41971     /**
41972      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
41973      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
41974      * @param {Number} index The zero-based index of the list item to select
41975      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
41976      * selected item if it is not currently in view (defaults to true)
41977      */
41978     select : function(index, scrollIntoView){
41979         this.selectedIndex = index;
41980         this.view.select(index);
41981         if(scrollIntoView !== false){
41982             var el = this.view.getNode(index);
41983             if(el){
41984                 this.innerList.scrollChildIntoView(el, false);
41985             }
41986         }
41987     },
41988
41989     // private
41990     selectNext : function(){
41991         var ct = this.store.getCount();
41992         if(ct > 0){
41993             if(this.selectedIndex == -1){
41994                 this.select(0);
41995             }else if(this.selectedIndex < ct-1){
41996                 this.select(this.selectedIndex+1);
41997             }
41998         }
41999     },
42000
42001     // private
42002     selectPrev : function(){
42003         var ct = this.store.getCount();
42004         if(ct > 0){
42005             if(this.selectedIndex == -1){
42006                 this.select(0);
42007             }else if(this.selectedIndex != 0){
42008                 this.select(this.selectedIndex-1);
42009             }
42010         }
42011     },
42012
42013     // private
42014     onKeyUp : function(e){
42015         if(this.editable !== false && !e.isSpecialKey()){
42016             this.lastKey = e.getKey();
42017             this.dqTask.delay(this.queryDelay);
42018         }
42019     },
42020
42021     // private
42022     validateBlur : function(){
42023         return !this.list || !this.list.isVisible();   
42024     },
42025
42026     // private
42027     initQuery : function(){
42028         this.doQuery(this.getRawValue());
42029     },
42030
42031     // private
42032     doForce : function(){
42033         if(this.el.dom.value.length > 0){
42034             this.el.dom.value =
42035                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
42036              
42037         }
42038     },
42039
42040     /**
42041      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
42042      * query allowing the query action to be canceled if needed.
42043      * @param {String} query The SQL query to execute
42044      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
42045      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
42046      * saved in the current store (defaults to false)
42047      */
42048     doQuery : function(q, forceAll){
42049         if(q === undefined || q === null){
42050             q = '';
42051         }
42052         var qe = {
42053             query: q,
42054             forceAll: forceAll,
42055             combo: this,
42056             cancel:false
42057         };
42058         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
42059             return false;
42060         }
42061         q = qe.query;
42062         forceAll = qe.forceAll;
42063         if(forceAll === true || (q.length >= this.minChars)){
42064             if(this.lastQuery != q || this.alwaysQuery){
42065                 this.lastQuery = q;
42066                 if(this.mode == 'local'){
42067                     this.selectedIndex = -1;
42068                     if(forceAll){
42069                         this.store.clearFilter();
42070                     }else{
42071                         this.store.filter(this.displayField, q);
42072                     }
42073                     this.onLoad();
42074                 }else{
42075                     this.store.baseParams[this.queryParam] = q;
42076                     this.store.load({
42077                         params: this.getParams(q)
42078                     });
42079                     this.expand();
42080                 }
42081             }else{
42082                 this.selectedIndex = -1;
42083                 this.onLoad();   
42084             }
42085         }
42086     },
42087
42088     // private
42089     getParams : function(q){
42090         var p = {};
42091         //p[this.queryParam] = q;
42092         if(this.pageSize){
42093             p.start = 0;
42094             p.limit = this.pageSize;
42095         }
42096         return p;
42097     },
42098
42099     /**
42100      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
42101      */
42102     collapse : function(){
42103         if(!this.isExpanded()){
42104             return;
42105         }
42106         this.list.hide();
42107         Roo.get(document).un('mousedown', this.collapseIf, this);
42108         Roo.get(document).un('mousewheel', this.collapseIf, this);
42109         if (!this.editable) {
42110             Roo.get(document).un('keydown', this.listKeyPress, this);
42111         }
42112         this.fireEvent('collapse', this);
42113     },
42114
42115     // private
42116     collapseIf : function(e){
42117         if(!e.within(this.wrap) && !e.within(this.list)){
42118             this.collapse();
42119         }
42120     },
42121
42122     /**
42123      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
42124      */
42125     expand : function(){
42126         if(this.isExpanded() || !this.hasFocus){
42127             return;
42128         }
42129         this.list.alignTo(this.el, this.listAlign);
42130         this.list.show();
42131         Roo.get(document).on('mousedown', this.collapseIf, this);
42132         Roo.get(document).on('mousewheel', this.collapseIf, this);
42133         if (!this.editable) {
42134             Roo.get(document).on('keydown', this.listKeyPress, this);
42135         }
42136         
42137         this.fireEvent('expand', this);
42138     },
42139
42140     // private
42141     // Implements the default empty TriggerField.onTriggerClick function
42142     onTriggerClick : function(){
42143         if(this.disabled){
42144             return;
42145         }
42146         if(this.isExpanded()){
42147             this.collapse();
42148             if (!this.blockFocus) {
42149                 this.el.focus();
42150             }
42151             
42152         }else {
42153             this.hasFocus = true;
42154             if(this.triggerAction == 'all') {
42155                 this.doQuery(this.allQuery, true);
42156             } else {
42157                 this.doQuery(this.getRawValue());
42158             }
42159             if (!this.blockFocus) {
42160                 this.el.focus();
42161             }
42162         }
42163     },
42164     listKeyPress : function(e)
42165     {
42166         //Roo.log('listkeypress');
42167         // scroll to first matching element based on key pres..
42168         if (e.isSpecialKey()) {
42169             return false;
42170         }
42171         var k = String.fromCharCode(e.getKey()).toUpperCase();
42172         //Roo.log(k);
42173         var match  = false;
42174         var csel = this.view.getSelectedNodes();
42175         var cselitem = false;
42176         if (csel.length) {
42177             var ix = this.view.indexOf(csel[0]);
42178             cselitem  = this.store.getAt(ix);
42179             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
42180                 cselitem = false;
42181             }
42182             
42183         }
42184         
42185         this.store.each(function(v) { 
42186             if (cselitem) {
42187                 // start at existing selection.
42188                 if (cselitem.id == v.id) {
42189                     cselitem = false;
42190                 }
42191                 return;
42192             }
42193                 
42194             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
42195                 match = this.store.indexOf(v);
42196                 return false;
42197             }
42198         }, this);
42199         
42200         if (match === false) {
42201             return true; // no more action?
42202         }
42203         // scroll to?
42204         this.view.select(match);
42205         var sn = Roo.get(this.view.getSelectedNodes()[0]);
42206         sn.scrollIntoView(sn.dom.parentNode, false);
42207     } 
42208
42209     /** 
42210     * @cfg {Boolean} grow 
42211     * @hide 
42212     */
42213     /** 
42214     * @cfg {Number} growMin 
42215     * @hide 
42216     */
42217     /** 
42218     * @cfg {Number} growMax 
42219     * @hide 
42220     */
42221     /**
42222      * @hide
42223      * @method autoSize
42224      */
42225 });/*
42226  * Copyright(c) 2010-2012, Roo J Solutions Limited
42227  *
42228  * Licence LGPL
42229  *
42230  */
42231
42232 /**
42233  * @class Roo.form.ComboBoxArray
42234  * @extends Roo.form.TextField
42235  * A facebook style adder... for lists of email / people / countries  etc...
42236  * pick multiple items from a combo box, and shows each one.
42237  *
42238  *  Fred [x]  Brian [x]  [Pick another |v]
42239  *
42240  *
42241  *  For this to work: it needs various extra information
42242  *    - normal combo problay has
42243  *      name, hiddenName
42244  *    + displayField, valueField
42245  *
42246  *    For our purpose...
42247  *
42248  *
42249  *   If we change from 'extends' to wrapping...
42250  *   
42251  *  
42252  *
42253  
42254  
42255  * @constructor
42256  * Create a new ComboBoxArray.
42257  * @param {Object} config Configuration options
42258  */
42259  
42260
42261 Roo.form.ComboBoxArray = function(config)
42262 {
42263     this.addEvents({
42264         /**
42265          * @event beforeremove
42266          * Fires before remove the value from the list
42267              * @param {Roo.form.ComboBoxArray} _self This combo box array
42268              * @param {Roo.form.ComboBoxArray.Item} item removed item
42269              */
42270         'beforeremove' : true,
42271         /**
42272          * @event remove
42273          * Fires when remove the value from the list
42274              * @param {Roo.form.ComboBoxArray} _self This combo box array
42275              * @param {Roo.form.ComboBoxArray.Item} item removed item
42276              */
42277         'remove' : true
42278         
42279         
42280     });
42281     
42282     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
42283     
42284     this.items = new Roo.util.MixedCollection(false);
42285     
42286     // construct the child combo...
42287     
42288     
42289     
42290     
42291    
42292     
42293 }
42294
42295  
42296 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
42297
42298     /**
42299      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
42300      */
42301     
42302     lastData : false,
42303     
42304     // behavies liek a hiddne field
42305     inputType:      'hidden',
42306     /**
42307      * @cfg {Number} width The width of the box that displays the selected element
42308      */ 
42309     width:          300,
42310
42311     
42312     
42313     /**
42314      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
42315      */
42316     name : false,
42317     /**
42318      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
42319      */
42320     hiddenName : false,
42321       /**
42322      * @cfg {String} seperator    The value seperator normally ',' 
42323      */
42324     seperator : ',',
42325     
42326     // private the array of items that are displayed..
42327     items  : false,
42328     // private - the hidden field el.
42329     hiddenEl : false,
42330     // private - the filed el..
42331     el : false,
42332     
42333     //validateValue : function() { return true; }, // all values are ok!
42334     //onAddClick: function() { },
42335     
42336     onRender : function(ct, position) 
42337     {
42338         
42339         // create the standard hidden element
42340         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
42341         
42342         
42343         // give fake names to child combo;
42344         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
42345         this.combo.name = this.name ? (this.name+'-subcombo') : this.name;
42346         
42347         this.combo = Roo.factory(this.combo, Roo.form);
42348         this.combo.onRender(ct, position);
42349         if (typeof(this.combo.width) != 'undefined') {
42350             this.combo.onResize(this.combo.width,0);
42351         }
42352         
42353         this.combo.initEvents();
42354         
42355         // assigned so form know we need to do this..
42356         this.store          = this.combo.store;
42357         this.valueField     = this.combo.valueField;
42358         this.displayField   = this.combo.displayField ;
42359         
42360         
42361         this.combo.wrap.addClass('x-cbarray-grp');
42362         
42363         var cbwrap = this.combo.wrap.createChild(
42364             {tag: 'div', cls: 'x-cbarray-cb'},
42365             this.combo.el.dom
42366         );
42367         
42368              
42369         this.hiddenEl = this.combo.wrap.createChild({
42370             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
42371         });
42372         this.el = this.combo.wrap.createChild({
42373             tag: 'input',  type:'hidden' , name: this.name, value : ''
42374         });
42375          //   this.el.dom.removeAttribute("name");
42376         
42377         
42378         this.outerWrap = this.combo.wrap;
42379         this.wrap = cbwrap;
42380         
42381         this.outerWrap.setWidth(this.width);
42382         this.outerWrap.dom.removeChild(this.el.dom);
42383         
42384         this.wrap.dom.appendChild(this.el.dom);
42385         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
42386         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
42387         
42388         this.combo.trigger.setStyle('position','relative');
42389         this.combo.trigger.setStyle('left', '0px');
42390         this.combo.trigger.setStyle('top', '2px');
42391         
42392         this.combo.el.setStyle('vertical-align', 'text-bottom');
42393         
42394         //this.trigger.setStyle('vertical-align', 'top');
42395         
42396         // this should use the code from combo really... on('add' ....)
42397         if (this.adder) {
42398             
42399         
42400             this.adder = this.outerWrap.createChild(
42401                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
42402             var _t = this;
42403             this.adder.on('click', function(e) {
42404                 _t.fireEvent('adderclick', this, e);
42405             }, _t);
42406         }
42407         //var _t = this;
42408         //this.adder.on('click', this.onAddClick, _t);
42409         
42410         
42411         this.combo.on('select', function(cb, rec, ix) {
42412             this.addItem(rec.data);
42413             
42414             cb.setValue('');
42415             cb.el.dom.value = '';
42416             //cb.lastData = rec.data;
42417             // add to list
42418             
42419         }, this);
42420         
42421         
42422     },
42423     
42424     
42425     getName: function()
42426     {
42427         // returns hidden if it's set..
42428         if (!this.rendered) {return ''};
42429         return  this.hiddenName ? this.hiddenName : this.name;
42430         
42431     },
42432     
42433     
42434     onResize: function(w, h){
42435         
42436         return;
42437         // not sure if this is needed..
42438         //this.combo.onResize(w,h);
42439         
42440         if(typeof w != 'number'){
42441             // we do not handle it!?!?
42442             return;
42443         }
42444         var tw = this.combo.trigger.getWidth();
42445         tw += this.addicon ? this.addicon.getWidth() : 0;
42446         tw += this.editicon ? this.editicon.getWidth() : 0;
42447         var x = w - tw;
42448         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
42449             
42450         this.combo.trigger.setStyle('left', '0px');
42451         
42452         if(this.list && this.listWidth === undefined){
42453             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
42454             this.list.setWidth(lw);
42455             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
42456         }
42457         
42458     
42459         
42460     },
42461     
42462     addItem: function(rec)
42463     {
42464         var valueField = this.combo.valueField;
42465         var displayField = this.combo.displayField;
42466         
42467         if (this.items.indexOfKey(rec[valueField]) > -1) {
42468             //console.log("GOT " + rec.data.id);
42469             return;
42470         }
42471         
42472         var x = new Roo.form.ComboBoxArray.Item({
42473             //id : rec[this.idField],
42474             data : rec,
42475             displayField : displayField ,
42476             tipField : displayField ,
42477             cb : this
42478         });
42479         // use the 
42480         this.items.add(rec[valueField],x);
42481         // add it before the element..
42482         this.updateHiddenEl();
42483         x.render(this.outerWrap, this.wrap.dom);
42484         // add the image handler..
42485     },
42486     
42487     updateHiddenEl : function()
42488     {
42489         this.validate();
42490         if (!this.hiddenEl) {
42491             return;
42492         }
42493         var ar = [];
42494         var idField = this.combo.valueField;
42495         
42496         this.items.each(function(f) {
42497             ar.push(f.data[idField]);
42498         });
42499         this.hiddenEl.dom.value = ar.join(this.seperator);
42500         this.validate();
42501     },
42502     
42503     reset : function()
42504     {
42505         this.items.clear();
42506         
42507         Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
42508            el.remove();
42509         });
42510         
42511         this.el.dom.value = '';
42512         if (this.hiddenEl) {
42513             this.hiddenEl.dom.value = '';
42514         }
42515         
42516     },
42517     getValue: function()
42518     {
42519         return this.hiddenEl ? this.hiddenEl.dom.value : '';
42520     },
42521     setValue: function(v) // not a valid action - must use addItems..
42522     {
42523         
42524         this.reset();
42525          
42526         if (this.store.isLocal && (typeof(v) == 'string')) {
42527             // then we can use the store to find the values..
42528             // comma seperated at present.. this needs to allow JSON based encoding..
42529             this.hiddenEl.value  = v;
42530             var v_ar = [];
42531             Roo.each(v.split(this.seperator), function(k) {
42532                 Roo.log("CHECK " + this.valueField + ',' + k);
42533                 var li = this.store.query(this.valueField, k);
42534                 if (!li.length) {
42535                     return;
42536                 }
42537                 var add = {};
42538                 add[this.valueField] = k;
42539                 add[this.displayField] = li.item(0).data[this.displayField];
42540                 
42541                 this.addItem(add);
42542             }, this) 
42543              
42544         }
42545         if (typeof(v) == 'object' ) {
42546             // then let's assume it's an array of objects..
42547             Roo.each(v, function(l) {
42548                 var add = l;
42549                 if (typeof(l) == 'string') {
42550                     add = {};
42551                     add[this.valueField] = l;
42552                     add[this.displayField] = l
42553                 }
42554                 this.addItem(add);
42555             }, this);
42556              
42557         }
42558         
42559         
42560     },
42561     setFromData: function(v)
42562     {
42563         // this recieves an object, if setValues is called.
42564         this.reset();
42565         this.el.dom.value = v[this.displayField];
42566         this.hiddenEl.dom.value = v[this.valueField];
42567         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
42568             return;
42569         }
42570         var kv = v[this.valueField];
42571         var dv = v[this.displayField];
42572         kv = typeof(kv) != 'string' ? '' : kv;
42573         dv = typeof(dv) != 'string' ? '' : dv;
42574         
42575         
42576         var keys = kv.split(this.seperator);
42577         var display = dv.split(this.seperator);
42578         for (var i = 0 ; i < keys.length; i++) {
42579             add = {};
42580             add[this.valueField] = keys[i];
42581             add[this.displayField] = display[i];
42582             this.addItem(add);
42583         }
42584       
42585         
42586     },
42587     
42588     /**
42589      * Validates the combox array value
42590      * @return {Boolean} True if the value is valid, else false
42591      */
42592     validate : function(){
42593         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
42594             this.clearInvalid();
42595             return true;
42596         }
42597         return false;
42598     },
42599     
42600     validateValue : function(value){
42601         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
42602         
42603     },
42604     
42605     /*@
42606      * overide
42607      * 
42608      */
42609     isDirty : function() {
42610         if(this.disabled) {
42611             return false;
42612         }
42613         
42614         try {
42615             var d = Roo.decode(String(this.originalValue));
42616         } catch (e) {
42617             return String(this.getValue()) !== String(this.originalValue);
42618         }
42619         
42620         var originalValue = [];
42621         
42622         for (var i = 0; i < d.length; i++){
42623             originalValue.push(d[i][this.valueField]);
42624         }
42625         
42626         return String(this.getValue()) !== String(originalValue.join(this.seperator));
42627         
42628     }
42629     
42630 });
42631
42632
42633
42634 /**
42635  * @class Roo.form.ComboBoxArray.Item
42636  * @extends Roo.BoxComponent
42637  * A selected item in the list
42638  *  Fred [x]  Brian [x]  [Pick another |v]
42639  * 
42640  * @constructor
42641  * Create a new item.
42642  * @param {Object} config Configuration options
42643  */
42644  
42645 Roo.form.ComboBoxArray.Item = function(config) {
42646     config.id = Roo.id();
42647     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
42648 }
42649
42650 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
42651     data : {},
42652     cb: false,
42653     displayField : false,
42654     tipField : false,
42655     
42656     
42657     defaultAutoCreate : {
42658         tag: 'div',
42659         cls: 'x-cbarray-item',
42660         cn : [ 
42661             { tag: 'div' },
42662             {
42663                 tag: 'img',
42664                 width:16,
42665                 height : 16,
42666                 src : Roo.BLANK_IMAGE_URL ,
42667                 align: 'center'
42668             }
42669         ]
42670         
42671     },
42672     
42673  
42674     onRender : function(ct, position)
42675     {
42676         Roo.form.Field.superclass.onRender.call(this, ct, position);
42677         
42678         if(!this.el){
42679             var cfg = this.getAutoCreate();
42680             this.el = ct.createChild(cfg, position);
42681         }
42682         
42683         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
42684         
42685         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
42686             this.cb.renderer(this.data) :
42687             String.format('{0}',this.data[this.displayField]);
42688         
42689             
42690         this.el.child('div').dom.setAttribute('qtip',
42691                         String.format('{0}',this.data[this.tipField])
42692         );
42693         
42694         this.el.child('img').on('click', this.remove, this);
42695         
42696     },
42697    
42698     remove : function()
42699     {
42700         if(this.cb.disabled){
42701             return;
42702         }
42703         
42704         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
42705             this.cb.items.remove(this);
42706             this.el.child('img').un('click', this.remove, this);
42707             this.el.remove();
42708             this.cb.updateHiddenEl();
42709
42710             this.cb.fireEvent('remove', this.cb, this);
42711         }
42712         
42713     }
42714 });/*
42715  * RooJS Library 1.1.1
42716  * Copyright(c) 2008-2011  Alan Knowles
42717  *
42718  * License - LGPL
42719  */
42720  
42721
42722 /**
42723  * @class Roo.form.ComboNested
42724  * @extends Roo.form.ComboBox
42725  * A combobox for that allows selection of nested items in a list,
42726  * eg.
42727  *
42728  *  Book
42729  *    -> red
42730  *    -> green
42731  *  Table
42732  *    -> square
42733  *      ->red
42734  *      ->green
42735  *    -> rectangle
42736  *      ->green
42737  *      
42738  * 
42739  * @constructor
42740  * Create a new ComboNested
42741  * @param {Object} config Configuration options
42742  */
42743 Roo.form.ComboNested = function(config){
42744     Roo.form.ComboCheck.superclass.constructor.call(this, config);
42745     // should verify some data...
42746     // like
42747     // hiddenName = required..
42748     // displayField = required
42749     // valudField == required
42750     var req= [ 'hiddenName', 'displayField', 'valueField' ];
42751     var _t = this;
42752     Roo.each(req, function(e) {
42753         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
42754             throw "Roo.form.ComboNested : missing value for: " + e;
42755         }
42756     });
42757      
42758     
42759 };
42760
42761 Roo.extend(Roo.form.ComboNested, Roo.form.ComboBox, {
42762    
42763     /*
42764      * @config {Number} max Number of columns to show
42765      */
42766     
42767     maxColumns : 3,
42768    
42769     list : null, // the outermost div..
42770     innerLists : null, // the
42771     views : null,
42772     stores : null,
42773     // private
42774     loadingChildren : false,
42775     
42776     onRender : function(ct, position)
42777     {
42778         Roo.form.ComboBox.superclass.onRender.call(this, ct, position); // skip parent call - got to above..
42779         
42780         if(this.hiddenName){
42781             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
42782                     'before', true);
42783             this.hiddenField.value =
42784                 this.hiddenValue !== undefined ? this.hiddenValue :
42785                 this.value !== undefined ? this.value : '';
42786
42787             // prevent input submission
42788             this.el.dom.removeAttribute('name');
42789              
42790              
42791         }
42792         
42793         if(Roo.isGecko){
42794             this.el.dom.setAttribute('autocomplete', 'off');
42795         }
42796
42797         var cls = 'x-combo-list';
42798
42799         this.list = new Roo.Layer({
42800             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
42801         });
42802
42803         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
42804         this.list.setWidth(lw);
42805         this.list.swallowEvent('mousewheel');
42806         this.assetHeight = 0;
42807
42808         if(this.title){
42809             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
42810             this.assetHeight += this.header.getHeight();
42811         }
42812         this.innerLists = [];
42813         this.views = [];
42814         this.stores = [];
42815         for (var i =0 ; i < this.maxColumns; i++) {
42816             this.onRenderList( cls, i);
42817         }
42818         
42819         // always needs footer, as we are going to have an 'OK' button.
42820         this.footer = this.list.createChild({cls:cls+'-ft'});
42821         this.pageTb = new Roo.Toolbar(this.footer);  
42822         var _this = this;
42823         this.pageTb.add(  {
42824             
42825             text: 'Done',
42826             handler: function()
42827             {
42828                 _this.collapse();
42829             }
42830         });
42831         
42832         if ( this.allowBlank && !this.disableClear) {
42833             
42834             this.pageTb.add(new Roo.Toolbar.Fill(), {
42835                 cls: 'x-btn-icon x-btn-clear',
42836                 text: '&#160;',
42837                 handler: function()
42838                 {
42839                     _this.collapse();
42840                     _this.clearValue();
42841                     _this.onSelect(false, -1);
42842                 }
42843             });
42844         }
42845         if (this.footer) {
42846             this.assetHeight += this.footer.getHeight();
42847         }
42848         
42849     },
42850     onRenderList : function (  cls, i)
42851     {
42852         
42853         var lw = Math.floor(
42854                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
42855         );
42856         
42857         this.list.setWidth(lw); // default to '1'
42858
42859         var il = this.innerLists[i] = this.list.createChild({cls:cls+'-inner'});
42860         //il.on('mouseover', this.onViewOver, this, { list:  i });
42861         //il.on('mousemove', this.onViewMove, this, { list:  i });
42862         il.setWidth(lw);
42863         il.setStyle({ 'overflow-x' : 'hidden'});
42864
42865         if(!this.tpl){
42866             this.tpl = new Roo.Template({
42867                 html :  '<div class="'+cls+'-item '+cls+'-item-{cn:this.isEmpty}">{' + this.displayField + '}</div>',
42868                 isEmpty: function (value, allValues) {
42869                     //Roo.log(value);
42870                     var dl = typeof(value.data) != 'undefined' ? value.data.length : value.length; ///json is a nested response..
42871                     return dl ? 'has-children' : 'no-children'
42872                 }
42873             });
42874         }
42875         
42876         var store  = this.store;
42877         if (i > 0) {
42878             store  = new Roo.data.SimpleStore({
42879                 //fields : this.store.reader.meta.fields,
42880                 reader : this.store.reader,
42881                 data : [ ]
42882             });
42883         }
42884         this.stores[i]  = store;
42885                   
42886         var view = this.views[i] = new Roo.View(
42887             il,
42888             this.tpl,
42889             {
42890                 singleSelect:true,
42891                 store: store,
42892                 selectedClass: this.selectedClass
42893             }
42894         );
42895         view.getEl().setWidth(lw);
42896         view.getEl().setStyle({
42897             position: i < 1 ? 'relative' : 'absolute',
42898             top: 0,
42899             left: (i * lw ) + 'px',
42900             display : i > 0 ? 'none' : 'block'
42901         });
42902         view.on('selectionchange', this.onSelectChange.createDelegate(this, {list : i }, true));
42903         view.on('dblclick', this.onDoubleClick.createDelegate(this, {list : i }, true));
42904         //view.on('click', this.onViewClick, this, { list : i });
42905
42906         store.on('beforeload', this.onBeforeLoad, this);
42907         store.on('load',  this.onLoad, this, { list  : i});
42908         store.on('loadexception', this.onLoadException, this);
42909
42910         // hide the other vies..
42911         
42912         
42913         
42914     },
42915       
42916     restrictHeight : function()
42917     {
42918         var mh = 0;
42919         Roo.each(this.innerLists, function(il,i) {
42920             var el = this.views[i].getEl();
42921             el.dom.style.height = '';
42922             var inner = el.dom;
42923             var h = Math.max(il.clientHeight, il.offsetHeight, il.scrollHeight);
42924             // only adjust heights on other ones..
42925             mh = Math.max(h, mh);
42926             if (i < 1) {
42927                 
42928                 el.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
42929                 il.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
42930                
42931             }
42932             
42933             
42934         }, this);
42935         
42936         this.list.beginUpdate();
42937         this.list.setHeight(mh+this.list.getFrameWidth('tb')+this.assetHeight);
42938         this.list.alignTo(this.el, this.listAlign);
42939         this.list.endUpdate();
42940         
42941     },
42942      
42943     
42944     // -- store handlers..
42945     // private
42946     onBeforeLoad : function()
42947     {
42948         if(!this.hasFocus){
42949             return;
42950         }
42951         this.innerLists[0].update(this.loadingText ?
42952                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
42953         this.restrictHeight();
42954         this.selectedIndex = -1;
42955     },
42956     // private
42957     onLoad : function(a,b,c,d)
42958     {
42959         if (!this.loadingChildren) {
42960             // then we are loading the top level. - hide the children
42961             for (var i = 1;i < this.views.length; i++) {
42962                 this.views[i].getEl().setStyle({ display : 'none' });
42963             }
42964             var lw = Math.floor(
42965                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
42966             );
42967         
42968              this.list.setWidth(lw); // default to '1'
42969
42970             
42971         }
42972         if(!this.hasFocus){
42973             return;
42974         }
42975         
42976         if(this.store.getCount() > 0) {
42977             this.expand();
42978             this.restrictHeight();   
42979         } else {
42980             this.onEmptyResults();
42981         }
42982         
42983         if (!this.loadingChildren) {
42984             this.selectActive();
42985         }
42986         /*
42987         this.stores[1].loadData([]);
42988         this.stores[2].loadData([]);
42989         this.views
42990         */    
42991     
42992         //this.el.focus();
42993     },
42994     
42995     
42996     // private
42997     onLoadException : function()
42998     {
42999         this.collapse();
43000         Roo.log(this.store.reader.jsonData);
43001         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
43002             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
43003         }
43004         
43005         
43006     },
43007     // no cleaning of leading spaces on blur here.
43008     cleanLeadingSpace : function(e) { },
43009     
43010
43011     onSelectChange : function (view, sels, opts )
43012     {
43013         var ix = view.getSelectedIndexes();
43014          
43015         if (opts.list > this.maxColumns - 2) {
43016             if (view.store.getCount()<  1) {
43017                 this.views[opts.list ].getEl().setStyle({ display :   'none' });
43018
43019             } else  {
43020                 if (ix.length) {
43021                     // used to clear ?? but if we are loading unselected 
43022                     this.setFromData(view.store.getAt(ix[0]).data);
43023                 }
43024                 
43025             }
43026             
43027             return;
43028         }
43029         
43030         if (!ix.length) {
43031             // this get's fired when trigger opens..
43032            // this.setFromData({});
43033             var str = this.stores[opts.list+1];
43034             str.data.clear(); // removeall wihtout the fire events..
43035             return;
43036         }
43037         
43038         var rec = view.store.getAt(ix[0]);
43039          
43040         this.setFromData(rec.data);
43041         this.fireEvent('select', this, rec, ix[0]);
43042         
43043         var lw = Math.floor(
43044              (
43045                 (this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')
43046              ) / this.maxColumns
43047         );
43048         this.loadingChildren = true;
43049         this.stores[opts.list+1].loadDataFromChildren( rec );
43050         this.loadingChildren = false;
43051         var dl = this.stores[opts.list+1]. getTotalCount();
43052         
43053         this.views[opts.list+1].getEl().setHeight( this.innerLists[0].getHeight());
43054         
43055         this.views[opts.list+1].getEl().setStyle({ display : dl ? 'block' : 'none' });
43056         for (var i = opts.list+2; i < this.views.length;i++) {
43057             this.views[i].getEl().setStyle({ display : 'none' });
43058         }
43059         
43060         this.innerLists[opts.list+1].setHeight( this.innerLists[0].getHeight());
43061         this.list.setWidth(lw * (opts.list + (dl ? 2 : 1)));
43062         
43063         if (this.isLoading) {
43064            // this.selectActive(opts.list);
43065         }
43066          
43067     },
43068     
43069     
43070     
43071     
43072     onDoubleClick : function()
43073     {
43074         this.collapse(); //??
43075     },
43076     
43077      
43078     
43079     
43080     
43081     // private
43082     recordToStack : function(store, prop, value, stack)
43083     {
43084         var cstore = new Roo.data.SimpleStore({
43085             //fields : this.store.reader.meta.fields, // we need array reader.. for
43086             reader : this.store.reader,
43087             data : [ ]
43088         });
43089         var _this = this;
43090         var record  = false;
43091         var srec = false;
43092         if(store.getCount() < 1){
43093             return false;
43094         }
43095         store.each(function(r){
43096             if(r.data[prop] == value){
43097                 record = r;
43098             srec = r;
43099                 return false;
43100             }
43101             if (r.data.cn && r.data.cn.length) {
43102                 cstore.loadDataFromChildren( r);
43103                 var cret = _this.recordToStack(cstore, prop, value, stack);
43104                 if (cret !== false) {
43105                     record = cret;
43106                     srec = r;
43107                     return false;
43108                 }
43109             }
43110              
43111             return true;
43112         });
43113         if (record == false) {
43114             return false
43115         }
43116         stack.unshift(srec);
43117         return record;
43118     },
43119     
43120     /*
43121      * find the stack of stores that match our value.
43122      *
43123      * 
43124      */
43125     
43126     selectActive : function ()
43127     {
43128         // if store is not loaded, then we will need to wait for that to happen first.
43129         var stack = [];
43130         this.recordToStack(this.store, this.valueField, this.getValue(), stack);
43131         for (var i = 0; i < stack.length; i++ ) {
43132             this.views[i].select(stack[i].store.indexOf(stack[i]), false, false );
43133         }
43134         
43135     }
43136         
43137          
43138     
43139     
43140     
43141     
43142 });/*
43143  * Based on:
43144  * Ext JS Library 1.1.1
43145  * Copyright(c) 2006-2007, Ext JS, LLC.
43146  *
43147  * Originally Released Under LGPL - original licence link has changed is not relivant.
43148  *
43149  * Fork - LGPL
43150  * <script type="text/javascript">
43151  */
43152 /**
43153  * @class Roo.form.Checkbox
43154  * @extends Roo.form.Field
43155  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
43156  * @constructor
43157  * Creates a new Checkbox
43158  * @param {Object} config Configuration options
43159  */
43160 Roo.form.Checkbox = function(config){
43161     Roo.form.Checkbox.superclass.constructor.call(this, config);
43162     this.addEvents({
43163         /**
43164          * @event check
43165          * Fires when the checkbox is checked or unchecked.
43166              * @param {Roo.form.Checkbox} this This checkbox
43167              * @param {Boolean} checked The new checked value
43168              */
43169         check : true
43170     });
43171 };
43172
43173 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
43174     /**
43175      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
43176      */
43177     focusClass : undefined,
43178     /**
43179      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
43180      */
43181     fieldClass: "x-form-field",
43182     /**
43183      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
43184      */
43185     checked: false,
43186     /**
43187      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
43188      * {tag: "input", type: "checkbox", autocomplete: "off"})
43189      */
43190     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
43191     /**
43192      * @cfg {String} boxLabel The text that appears beside the checkbox
43193      */
43194     boxLabel : "",
43195     /**
43196      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
43197      */  
43198     inputValue : '1',
43199     /**
43200      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
43201      */
43202      valueOff: '0', // value when not checked..
43203
43204     actionMode : 'viewEl', 
43205     //
43206     // private
43207     itemCls : 'x-menu-check-item x-form-item',
43208     groupClass : 'x-menu-group-item',
43209     inputType : 'hidden',
43210     
43211     
43212     inSetChecked: false, // check that we are not calling self...
43213     
43214     inputElement: false, // real input element?
43215     basedOn: false, // ????
43216     
43217     isFormField: true, // not sure where this is needed!!!!
43218
43219     onResize : function(){
43220         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
43221         if(!this.boxLabel){
43222             this.el.alignTo(this.wrap, 'c-c');
43223         }
43224     },
43225
43226     initEvents : function(){
43227         Roo.form.Checkbox.superclass.initEvents.call(this);
43228         this.el.on("click", this.onClick,  this);
43229         this.el.on("change", this.onClick,  this);
43230     },
43231
43232
43233     getResizeEl : function(){
43234         return this.wrap;
43235     },
43236
43237     getPositionEl : function(){
43238         return this.wrap;
43239     },
43240
43241     // private
43242     onRender : function(ct, position){
43243         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
43244         /*
43245         if(this.inputValue !== undefined){
43246             this.el.dom.value = this.inputValue;
43247         }
43248         */
43249         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
43250         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
43251         var viewEl = this.wrap.createChild({ 
43252             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
43253         this.viewEl = viewEl;   
43254         this.wrap.on('click', this.onClick,  this); 
43255         
43256         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
43257         this.el.on('propertychange', this.setFromHidden,  this);  //ie
43258         
43259         
43260         
43261         if(this.boxLabel){
43262             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
43263         //    viewEl.on('click', this.onClick,  this); 
43264         }
43265         //if(this.checked){
43266             this.setChecked(this.checked);
43267         //}else{
43268             //this.checked = this.el.dom;
43269         //}
43270
43271     },
43272
43273     // private
43274     initValue : Roo.emptyFn,
43275
43276     /**
43277      * Returns the checked state of the checkbox.
43278      * @return {Boolean} True if checked, else false
43279      */
43280     getValue : function(){
43281         if(this.el){
43282             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
43283         }
43284         return this.valueOff;
43285         
43286     },
43287
43288         // private
43289     onClick : function(){ 
43290         if (this.disabled) {
43291             return;
43292         }
43293         this.setChecked(!this.checked);
43294
43295         //if(this.el.dom.checked != this.checked){
43296         //    this.setValue(this.el.dom.checked);
43297        // }
43298     },
43299
43300     /**
43301      * Sets the checked state of the checkbox.
43302      * On is always based on a string comparison between inputValue and the param.
43303      * @param {Boolean/String} value - the value to set 
43304      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
43305      */
43306     setValue : function(v,suppressEvent){
43307         
43308         
43309         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
43310         //if(this.el && this.el.dom){
43311         //    this.el.dom.checked = this.checked;
43312         //    this.el.dom.defaultChecked = this.checked;
43313         //}
43314         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
43315         //this.fireEvent("check", this, this.checked);
43316     },
43317     // private..
43318     setChecked : function(state,suppressEvent)
43319     {
43320         if (this.inSetChecked) {
43321             this.checked = state;
43322             return;
43323         }
43324         
43325     
43326         if(this.wrap){
43327             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
43328         }
43329         this.checked = state;
43330         if(suppressEvent !== true){
43331             this.fireEvent('check', this, state);
43332         }
43333         this.inSetChecked = true;
43334         this.el.dom.value = state ? this.inputValue : this.valueOff;
43335         this.inSetChecked = false;
43336         
43337     },
43338     // handle setting of hidden value by some other method!!?!?
43339     setFromHidden: function()
43340     {
43341         if(!this.el){
43342             return;
43343         }
43344         //console.log("SET FROM HIDDEN");
43345         //alert('setFrom hidden');
43346         this.setValue(this.el.dom.value);
43347     },
43348     
43349     onDestroy : function()
43350     {
43351         if(this.viewEl){
43352             Roo.get(this.viewEl).remove();
43353         }
43354          
43355         Roo.form.Checkbox.superclass.onDestroy.call(this);
43356     },
43357     
43358     setBoxLabel : function(str)
43359     {
43360         this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
43361     }
43362
43363 });/*
43364  * Based on:
43365  * Ext JS Library 1.1.1
43366  * Copyright(c) 2006-2007, Ext JS, LLC.
43367  *
43368  * Originally Released Under LGPL - original licence link has changed is not relivant.
43369  *
43370  * Fork - LGPL
43371  * <script type="text/javascript">
43372  */
43373  
43374 /**
43375  * @class Roo.form.Radio
43376  * @extends Roo.form.Checkbox
43377  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
43378  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
43379  * @constructor
43380  * Creates a new Radio
43381  * @param {Object} config Configuration options
43382  */
43383 Roo.form.Radio = function(){
43384     Roo.form.Radio.superclass.constructor.apply(this, arguments);
43385 };
43386 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
43387     inputType: 'radio',
43388
43389     /**
43390      * If this radio is part of a group, it will return the selected value
43391      * @return {String}
43392      */
43393     getGroupValue : function(){
43394         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
43395     },
43396     
43397     
43398     onRender : function(ct, position){
43399         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
43400         
43401         if(this.inputValue !== undefined){
43402             this.el.dom.value = this.inputValue;
43403         }
43404          
43405         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
43406         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
43407         //var viewEl = this.wrap.createChild({ 
43408         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
43409         //this.viewEl = viewEl;   
43410         //this.wrap.on('click', this.onClick,  this); 
43411         
43412         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
43413         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
43414         
43415         
43416         
43417         if(this.boxLabel){
43418             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
43419         //    viewEl.on('click', this.onClick,  this); 
43420         }
43421          if(this.checked){
43422             this.el.dom.checked =   'checked' ;
43423         }
43424          
43425     } 
43426     
43427     
43428 });//<script type="text/javascript">
43429
43430 /*
43431  * Based  Ext JS Library 1.1.1
43432  * Copyright(c) 2006-2007, Ext JS, LLC.
43433  * LGPL
43434  *
43435  */
43436  
43437 /**
43438  * @class Roo.HtmlEditorCore
43439  * @extends Roo.Component
43440  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
43441  *
43442  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
43443  */
43444
43445 Roo.HtmlEditorCore = function(config){
43446     
43447     
43448     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
43449     
43450     
43451     this.addEvents({
43452         /**
43453          * @event initialize
43454          * Fires when the editor is fully initialized (including the iframe)
43455          * @param {Roo.HtmlEditorCore} this
43456          */
43457         initialize: true,
43458         /**
43459          * @event activate
43460          * Fires when the editor is first receives the focus. Any insertion must wait
43461          * until after this event.
43462          * @param {Roo.HtmlEditorCore} this
43463          */
43464         activate: true,
43465          /**
43466          * @event beforesync
43467          * Fires before the textarea is updated with content from the editor iframe. Return false
43468          * to cancel the sync.
43469          * @param {Roo.HtmlEditorCore} this
43470          * @param {String} html
43471          */
43472         beforesync: true,
43473          /**
43474          * @event beforepush
43475          * Fires before the iframe editor is updated with content from the textarea. Return false
43476          * to cancel the push.
43477          * @param {Roo.HtmlEditorCore} this
43478          * @param {String} html
43479          */
43480         beforepush: true,
43481          /**
43482          * @event sync
43483          * Fires when the textarea is updated with content from the editor iframe.
43484          * @param {Roo.HtmlEditorCore} this
43485          * @param {String} html
43486          */
43487         sync: true,
43488          /**
43489          * @event push
43490          * Fires when the iframe editor is updated with content from the textarea.
43491          * @param {Roo.HtmlEditorCore} this
43492          * @param {String} html
43493          */
43494         push: true,
43495         
43496         /**
43497          * @event editorevent
43498          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
43499          * @param {Roo.HtmlEditorCore} this
43500          */
43501         editorevent: true
43502         
43503     });
43504     
43505     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
43506     
43507     // defaults : white / black...
43508     this.applyBlacklists();
43509     
43510     
43511     
43512 };
43513
43514
43515 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
43516
43517
43518      /**
43519      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
43520      */
43521     
43522     owner : false,
43523     
43524      /**
43525      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
43526      *                        Roo.resizable.
43527      */
43528     resizable : false,
43529      /**
43530      * @cfg {Number} height (in pixels)
43531      */   
43532     height: 300,
43533    /**
43534      * @cfg {Number} width (in pixels)
43535      */   
43536     width: 500,
43537     
43538     /**
43539      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
43540      * 
43541      */
43542     stylesheets: false,
43543     
43544     // id of frame..
43545     frameId: false,
43546     
43547     // private properties
43548     validationEvent : false,
43549     deferHeight: true,
43550     initialized : false,
43551     activated : false,
43552     sourceEditMode : false,
43553     onFocus : Roo.emptyFn,
43554     iframePad:3,
43555     hideMode:'offsets',
43556     
43557     clearUp: true,
43558     
43559     // blacklist + whitelisted elements..
43560     black: false,
43561     white: false,
43562      
43563     bodyCls : '',
43564
43565     /**
43566      * Protected method that will not generally be called directly. It
43567      * is called when the editor initializes the iframe with HTML contents. Override this method if you
43568      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
43569      */
43570     getDocMarkup : function(){
43571         // body styles..
43572         var st = '';
43573         
43574         // inherit styels from page...?? 
43575         if (this.stylesheets === false) {
43576             
43577             Roo.get(document.head).select('style').each(function(node) {
43578                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
43579             });
43580             
43581             Roo.get(document.head).select('link').each(function(node) { 
43582                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
43583             });
43584             
43585         } else if (!this.stylesheets.length) {
43586                 // simple..
43587                 st = '<style type="text/css">' +
43588                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
43589                    '</style>';
43590         } else { 
43591             st = '<style type="text/css">' +
43592                     this.stylesheets +
43593                 '</style>';
43594         }
43595         
43596         st +=  '<style type="text/css">' +
43597             'IMG { cursor: pointer } ' +
43598         '</style>';
43599
43600         var cls = 'roo-htmleditor-body';
43601         
43602         if(this.bodyCls.length){
43603             cls += ' ' + this.bodyCls;
43604         }
43605         
43606         return '<html><head>' + st  +
43607             //<style type="text/css">' +
43608             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
43609             //'</style>' +
43610             ' </head><body class="' +  cls + '"></body></html>';
43611     },
43612
43613     // private
43614     onRender : function(ct, position)
43615     {
43616         var _t = this;
43617         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
43618         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
43619         
43620         
43621         this.el.dom.style.border = '0 none';
43622         this.el.dom.setAttribute('tabIndex', -1);
43623         this.el.addClass('x-hidden hide');
43624         
43625         
43626         
43627         if(Roo.isIE){ // fix IE 1px bogus margin
43628             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
43629         }
43630        
43631         
43632         this.frameId = Roo.id();
43633         
43634          
43635         
43636         var iframe = this.owner.wrap.createChild({
43637             tag: 'iframe',
43638             cls: 'form-control', // bootstrap..
43639             id: this.frameId,
43640             name: this.frameId,
43641             frameBorder : 'no',
43642             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
43643         }, this.el
43644         );
43645         
43646         
43647         this.iframe = iframe.dom;
43648
43649          this.assignDocWin();
43650         
43651         this.doc.designMode = 'on';
43652        
43653         this.doc.open();
43654         this.doc.write(this.getDocMarkup());
43655         this.doc.close();
43656
43657         
43658         var task = { // must defer to wait for browser to be ready
43659             run : function(){
43660                 //console.log("run task?" + this.doc.readyState);
43661                 this.assignDocWin();
43662                 if(this.doc.body || this.doc.readyState == 'complete'){
43663                     try {
43664                         this.doc.designMode="on";
43665                     } catch (e) {
43666                         return;
43667                     }
43668                     Roo.TaskMgr.stop(task);
43669                     this.initEditor.defer(10, this);
43670                 }
43671             },
43672             interval : 10,
43673             duration: 10000,
43674             scope: this
43675         };
43676         Roo.TaskMgr.start(task);
43677
43678     },
43679
43680     // private
43681     onResize : function(w, h)
43682     {
43683          Roo.log('resize: ' +w + ',' + h );
43684         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
43685         if(!this.iframe){
43686             return;
43687         }
43688         if(typeof w == 'number'){
43689             
43690             this.iframe.style.width = w + 'px';
43691         }
43692         if(typeof h == 'number'){
43693             
43694             this.iframe.style.height = h + 'px';
43695             if(this.doc){
43696                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
43697             }
43698         }
43699         
43700     },
43701
43702     /**
43703      * Toggles the editor between standard and source edit mode.
43704      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
43705      */
43706     toggleSourceEdit : function(sourceEditMode){
43707         
43708         this.sourceEditMode = sourceEditMode === true;
43709         
43710         if(this.sourceEditMode){
43711  
43712             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
43713             
43714         }else{
43715             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
43716             //this.iframe.className = '';
43717             this.deferFocus();
43718         }
43719         //this.setSize(this.owner.wrap.getSize());
43720         //this.fireEvent('editmodechange', this, this.sourceEditMode);
43721     },
43722
43723     
43724   
43725
43726     /**
43727      * Protected method that will not generally be called directly. If you need/want
43728      * custom HTML cleanup, this is the method you should override.
43729      * @param {String} html The HTML to be cleaned
43730      * return {String} The cleaned HTML
43731      */
43732     cleanHtml : function(html){
43733         html = String(html);
43734         if(html.length > 5){
43735             if(Roo.isSafari){ // strip safari nonsense
43736                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
43737             }
43738         }
43739         if(html == '&nbsp;'){
43740             html = '';
43741         }
43742         return html;
43743     },
43744
43745     /**
43746      * HTML Editor -> Textarea
43747      * Protected method that will not generally be called directly. Syncs the contents
43748      * of the editor iframe with the textarea.
43749      */
43750     syncValue : function(){
43751         if(this.initialized){
43752             var bd = (this.doc.body || this.doc.documentElement);
43753             //this.cleanUpPaste(); -- this is done else where and causes havoc..
43754             var html = bd.innerHTML;
43755             if(Roo.isSafari){
43756                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
43757                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
43758                 if(m && m[1]){
43759                     html = '<div style="'+m[0]+'">' + html + '</div>';
43760                 }
43761             }
43762             html = this.cleanHtml(html);
43763             // fix up the special chars.. normaly like back quotes in word...
43764             // however we do not want to do this with chinese..
43765             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
43766                 
43767                 var cc = match.charCodeAt();
43768
43769                 // Get the character value, handling surrogate pairs
43770                 if (match.length == 2) {
43771                     // It's a surrogate pair, calculate the Unicode code point
43772                     var high = match.charCodeAt(0) - 0xD800;
43773                     var low  = match.charCodeAt(1) - 0xDC00;
43774                     cc = (high * 0x400) + low + 0x10000;
43775                 }  else if (
43776                     (cc >= 0x4E00 && cc < 0xA000 ) ||
43777                     (cc >= 0x3400 && cc < 0x4E00 ) ||
43778                     (cc >= 0xf900 && cc < 0xfb00 )
43779                 ) {
43780                         return match;
43781                 }  
43782          
43783                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
43784                 return "&#" + cc + ";";
43785                 
43786                 
43787             });
43788             
43789             
43790              
43791             if(this.owner.fireEvent('beforesync', this, html) !== false){
43792                 this.el.dom.value = html;
43793                 this.owner.fireEvent('sync', this, html);
43794             }
43795         }
43796     },
43797
43798     /**
43799      * Protected method that will not generally be called directly. Pushes the value of the textarea
43800      * into the iframe editor.
43801      */
43802     pushValue : function(){
43803         if(this.initialized){
43804             var v = this.el.dom.value.trim();
43805             
43806 //            if(v.length < 1){
43807 //                v = '&#160;';
43808 //            }
43809             
43810             if(this.owner.fireEvent('beforepush', this, v) !== false){
43811                 var d = (this.doc.body || this.doc.documentElement);
43812                 d.innerHTML = v;
43813                 this.cleanUpPaste();
43814                 this.el.dom.value = d.innerHTML;
43815                 this.owner.fireEvent('push', this, v);
43816             }
43817         }
43818     },
43819
43820     // private
43821     deferFocus : function(){
43822         this.focus.defer(10, this);
43823     },
43824
43825     // doc'ed in Field
43826     focus : function(){
43827         if(this.win && !this.sourceEditMode){
43828             this.win.focus();
43829         }else{
43830             this.el.focus();
43831         }
43832     },
43833     
43834     assignDocWin: function()
43835     {
43836         var iframe = this.iframe;
43837         
43838          if(Roo.isIE){
43839             this.doc = iframe.contentWindow.document;
43840             this.win = iframe.contentWindow;
43841         } else {
43842 //            if (!Roo.get(this.frameId)) {
43843 //                return;
43844 //            }
43845 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
43846 //            this.win = Roo.get(this.frameId).dom.contentWindow;
43847             
43848             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
43849                 return;
43850             }
43851             
43852             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
43853             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
43854         }
43855     },
43856     
43857     // private
43858     initEditor : function(){
43859         //console.log("INIT EDITOR");
43860         this.assignDocWin();
43861         
43862         
43863         
43864         this.doc.designMode="on";
43865         this.doc.open();
43866         this.doc.write(this.getDocMarkup());
43867         this.doc.close();
43868         
43869         var dbody = (this.doc.body || this.doc.documentElement);
43870         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
43871         // this copies styles from the containing element into thsi one..
43872         // not sure why we need all of this..
43873         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
43874         
43875         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
43876         //ss['background-attachment'] = 'fixed'; // w3c
43877         dbody.bgProperties = 'fixed'; // ie
43878         //Roo.DomHelper.applyStyles(dbody, ss);
43879         Roo.EventManager.on(this.doc, {
43880             //'mousedown': this.onEditorEvent,
43881             'mouseup': this.onEditorEvent,
43882             'dblclick': this.onEditorEvent,
43883             'click': this.onEditorEvent,
43884             'keyup': this.onEditorEvent,
43885             buffer:100,
43886             scope: this
43887         });
43888         if(Roo.isGecko){
43889             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
43890         }
43891         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
43892             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
43893         }
43894         this.initialized = true;
43895
43896         this.owner.fireEvent('initialize', this);
43897         this.pushValue();
43898     },
43899
43900     // private
43901     onDestroy : function(){
43902         
43903         
43904         
43905         if(this.rendered){
43906             
43907             //for (var i =0; i < this.toolbars.length;i++) {
43908             //    // fixme - ask toolbars for heights?
43909             //    this.toolbars[i].onDestroy();
43910            // }
43911             
43912             //this.wrap.dom.innerHTML = '';
43913             //this.wrap.remove();
43914         }
43915     },
43916
43917     // private
43918     onFirstFocus : function(){
43919         
43920         this.assignDocWin();
43921         
43922         
43923         this.activated = true;
43924          
43925     
43926         if(Roo.isGecko){ // prevent silly gecko errors
43927             this.win.focus();
43928             var s = this.win.getSelection();
43929             if(!s.focusNode || s.focusNode.nodeType != 3){
43930                 var r = s.getRangeAt(0);
43931                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
43932                 r.collapse(true);
43933                 this.deferFocus();
43934             }
43935             try{
43936                 this.execCmd('useCSS', true);
43937                 this.execCmd('styleWithCSS', false);
43938             }catch(e){}
43939         }
43940         this.owner.fireEvent('activate', this);
43941     },
43942
43943     // private
43944     adjustFont: function(btn){
43945         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
43946         //if(Roo.isSafari){ // safari
43947         //    adjust *= 2;
43948        // }
43949         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
43950         if(Roo.isSafari){ // safari
43951             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
43952             v =  (v < 10) ? 10 : v;
43953             v =  (v > 48) ? 48 : v;
43954             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
43955             
43956         }
43957         
43958         
43959         v = Math.max(1, v+adjust);
43960         
43961         this.execCmd('FontSize', v  );
43962     },
43963
43964     onEditorEvent : function(e)
43965     {
43966         this.owner.fireEvent('editorevent', this, e);
43967       //  this.updateToolbar();
43968         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
43969     },
43970
43971     insertTag : function(tg)
43972     {
43973         // could be a bit smarter... -> wrap the current selected tRoo..
43974         if (tg.toLowerCase() == 'span' ||
43975             tg.toLowerCase() == 'code' ||
43976             tg.toLowerCase() == 'sup' ||
43977             tg.toLowerCase() == 'sub' 
43978             ) {
43979             
43980             range = this.createRange(this.getSelection());
43981             var wrappingNode = this.doc.createElement(tg.toLowerCase());
43982             wrappingNode.appendChild(range.extractContents());
43983             range.insertNode(wrappingNode);
43984
43985             return;
43986             
43987             
43988             
43989         }
43990         this.execCmd("formatblock",   tg);
43991         
43992     },
43993     
43994     insertText : function(txt)
43995     {
43996         
43997         
43998         var range = this.createRange();
43999         range.deleteContents();
44000                //alert(Sender.getAttribute('label'));
44001                
44002         range.insertNode(this.doc.createTextNode(txt));
44003     } ,
44004     
44005      
44006
44007     /**
44008      * Executes a Midas editor command on the editor document and performs necessary focus and
44009      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
44010      * @param {String} cmd The Midas command
44011      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
44012      */
44013     relayCmd : function(cmd, value){
44014         this.win.focus();
44015         this.execCmd(cmd, value);
44016         this.owner.fireEvent('editorevent', this);
44017         //this.updateToolbar();
44018         this.owner.deferFocus();
44019     },
44020
44021     /**
44022      * Executes a Midas editor command directly on the editor document.
44023      * For visual commands, you should use {@link #relayCmd} instead.
44024      * <b>This should only be called after the editor is initialized.</b>
44025      * @param {String} cmd The Midas command
44026      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
44027      */
44028     execCmd : function(cmd, value){
44029         this.doc.execCommand(cmd, false, value === undefined ? null : value);
44030         this.syncValue();
44031     },
44032  
44033  
44034    
44035     /**
44036      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
44037      * to insert tRoo.
44038      * @param {String} text | dom node.. 
44039      */
44040     insertAtCursor : function(text)
44041     {
44042         
44043         if(!this.activated){
44044             return;
44045         }
44046         /*
44047         if(Roo.isIE){
44048             this.win.focus();
44049             var r = this.doc.selection.createRange();
44050             if(r){
44051                 r.collapse(true);
44052                 r.pasteHTML(text);
44053                 this.syncValue();
44054                 this.deferFocus();
44055             
44056             }
44057             return;
44058         }
44059         */
44060         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
44061             this.win.focus();
44062             
44063             
44064             // from jquery ui (MIT licenced)
44065             var range, node;
44066             var win = this.win;
44067             
44068             if (win.getSelection && win.getSelection().getRangeAt) {
44069                 range = win.getSelection().getRangeAt(0);
44070                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
44071                 range.insertNode(node);
44072             } else if (win.document.selection && win.document.selection.createRange) {
44073                 // no firefox support
44074                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
44075                 win.document.selection.createRange().pasteHTML(txt);
44076             } else {
44077                 // no firefox support
44078                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
44079                 this.execCmd('InsertHTML', txt);
44080             } 
44081             
44082             this.syncValue();
44083             
44084             this.deferFocus();
44085         }
44086     },
44087  // private
44088     mozKeyPress : function(e){
44089         if(e.ctrlKey){
44090             var c = e.getCharCode(), cmd;
44091           
44092             if(c > 0){
44093                 c = String.fromCharCode(c).toLowerCase();
44094                 switch(c){
44095                     case 'b':
44096                         cmd = 'bold';
44097                         break;
44098                     case 'i':
44099                         cmd = 'italic';
44100                         break;
44101                     
44102                     case 'u':
44103                         cmd = 'underline';
44104                         break;
44105                     
44106                     case 'v':
44107                         this.cleanUpPaste.defer(100, this);
44108                         return;
44109                         
44110                 }
44111                 if(cmd){
44112                     this.win.focus();
44113                     this.execCmd(cmd);
44114                     this.deferFocus();
44115                     e.preventDefault();
44116                 }
44117                 
44118             }
44119         }
44120     },
44121
44122     // private
44123     fixKeys : function(){ // load time branching for fastest keydown performance
44124         if(Roo.isIE){
44125             return function(e){
44126                 var k = e.getKey(), r;
44127                 if(k == e.TAB){
44128                     e.stopEvent();
44129                     r = this.doc.selection.createRange();
44130                     if(r){
44131                         r.collapse(true);
44132                         r.pasteHTML('&#160;&#160;&#160;&#160;');
44133                         this.deferFocus();
44134                     }
44135                     return;
44136                 }
44137                 
44138                 if(k == e.ENTER){
44139                     r = this.doc.selection.createRange();
44140                     if(r){
44141                         var target = r.parentElement();
44142                         if(!target || target.tagName.toLowerCase() != 'li'){
44143                             e.stopEvent();
44144                             r.pasteHTML('<br />');
44145                             r.collapse(false);
44146                             r.select();
44147                         }
44148                     }
44149                 }
44150                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
44151                     this.cleanUpPaste.defer(100, this);
44152                     return;
44153                 }
44154                 
44155                 
44156             };
44157         }else if(Roo.isOpera){
44158             return function(e){
44159                 var k = e.getKey();
44160                 if(k == e.TAB){
44161                     e.stopEvent();
44162                     this.win.focus();
44163                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
44164                     this.deferFocus();
44165                 }
44166                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
44167                     this.cleanUpPaste.defer(100, this);
44168                     return;
44169                 }
44170                 
44171             };
44172         }else if(Roo.isSafari){
44173             return function(e){
44174                 var k = e.getKey();
44175                 
44176                 if(k == e.TAB){
44177                     e.stopEvent();
44178                     this.execCmd('InsertText','\t');
44179                     this.deferFocus();
44180                     return;
44181                 }
44182                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
44183                     this.cleanUpPaste.defer(100, this);
44184                     return;
44185                 }
44186                 
44187              };
44188         }
44189     }(),
44190     
44191     getAllAncestors: function()
44192     {
44193         var p = this.getSelectedNode();
44194         var a = [];
44195         if (!p) {
44196             a.push(p); // push blank onto stack..
44197             p = this.getParentElement();
44198         }
44199         
44200         
44201         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
44202             a.push(p);
44203             p = p.parentNode;
44204         }
44205         a.push(this.doc.body);
44206         return a;
44207     },
44208     lastSel : false,
44209     lastSelNode : false,
44210     
44211     
44212     getSelection : function() 
44213     {
44214         this.assignDocWin();
44215         return Roo.isIE ? this.doc.selection : this.win.getSelection();
44216     },
44217     
44218     getSelectedNode: function() 
44219     {
44220         // this may only work on Gecko!!!
44221         
44222         // should we cache this!!!!
44223         
44224         
44225         
44226          
44227         var range = this.createRange(this.getSelection()).cloneRange();
44228         
44229         if (Roo.isIE) {
44230             var parent = range.parentElement();
44231             while (true) {
44232                 var testRange = range.duplicate();
44233                 testRange.moveToElementText(parent);
44234                 if (testRange.inRange(range)) {
44235                     break;
44236                 }
44237                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
44238                     break;
44239                 }
44240                 parent = parent.parentElement;
44241             }
44242             return parent;
44243         }
44244         
44245         // is ancestor a text element.
44246         var ac =  range.commonAncestorContainer;
44247         if (ac.nodeType == 3) {
44248             ac = ac.parentNode;
44249         }
44250         
44251         var ar = ac.childNodes;
44252          
44253         var nodes = [];
44254         var other_nodes = [];
44255         var has_other_nodes = false;
44256         for (var i=0;i<ar.length;i++) {
44257             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
44258                 continue;
44259             }
44260             // fullly contained node.
44261             
44262             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
44263                 nodes.push(ar[i]);
44264                 continue;
44265             }
44266             
44267             // probably selected..
44268             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
44269                 other_nodes.push(ar[i]);
44270                 continue;
44271             }
44272             // outer..
44273             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
44274                 continue;
44275             }
44276             
44277             
44278             has_other_nodes = true;
44279         }
44280         if (!nodes.length && other_nodes.length) {
44281             nodes= other_nodes;
44282         }
44283         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
44284             return false;
44285         }
44286         
44287         return nodes[0];
44288     },
44289     createRange: function(sel)
44290     {
44291         // this has strange effects when using with 
44292         // top toolbar - not sure if it's a great idea.
44293         //this.editor.contentWindow.focus();
44294         if (typeof sel != "undefined") {
44295             try {
44296                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
44297             } catch(e) {
44298                 return this.doc.createRange();
44299             }
44300         } else {
44301             return this.doc.createRange();
44302         }
44303     },
44304     getParentElement: function()
44305     {
44306         
44307         this.assignDocWin();
44308         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
44309         
44310         var range = this.createRange(sel);
44311          
44312         try {
44313             var p = range.commonAncestorContainer;
44314             while (p.nodeType == 3) { // text node
44315                 p = p.parentNode;
44316             }
44317             return p;
44318         } catch (e) {
44319             return null;
44320         }
44321     
44322     },
44323     /***
44324      *
44325      * Range intersection.. the hard stuff...
44326      *  '-1' = before
44327      *  '0' = hits..
44328      *  '1' = after.
44329      *         [ -- selected range --- ]
44330      *   [fail]                        [fail]
44331      *
44332      *    basically..
44333      *      if end is before start or  hits it. fail.
44334      *      if start is after end or hits it fail.
44335      *
44336      *   if either hits (but other is outside. - then it's not 
44337      *   
44338      *    
44339      **/
44340     
44341     
44342     // @see http://www.thismuchiknow.co.uk/?p=64.
44343     rangeIntersectsNode : function(range, node)
44344     {
44345         var nodeRange = node.ownerDocument.createRange();
44346         try {
44347             nodeRange.selectNode(node);
44348         } catch (e) {
44349             nodeRange.selectNodeContents(node);
44350         }
44351     
44352         var rangeStartRange = range.cloneRange();
44353         rangeStartRange.collapse(true);
44354     
44355         var rangeEndRange = range.cloneRange();
44356         rangeEndRange.collapse(false);
44357     
44358         var nodeStartRange = nodeRange.cloneRange();
44359         nodeStartRange.collapse(true);
44360     
44361         var nodeEndRange = nodeRange.cloneRange();
44362         nodeEndRange.collapse(false);
44363     
44364         return rangeStartRange.compareBoundaryPoints(
44365                  Range.START_TO_START, nodeEndRange) == -1 &&
44366                rangeEndRange.compareBoundaryPoints(
44367                  Range.START_TO_START, nodeStartRange) == 1;
44368         
44369          
44370     },
44371     rangeCompareNode : function(range, node)
44372     {
44373         var nodeRange = node.ownerDocument.createRange();
44374         try {
44375             nodeRange.selectNode(node);
44376         } catch (e) {
44377             nodeRange.selectNodeContents(node);
44378         }
44379         
44380         
44381         range.collapse(true);
44382     
44383         nodeRange.collapse(true);
44384      
44385         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
44386         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
44387          
44388         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
44389         
44390         var nodeIsBefore   =  ss == 1;
44391         var nodeIsAfter    = ee == -1;
44392         
44393         if (nodeIsBefore && nodeIsAfter) {
44394             return 0; // outer
44395         }
44396         if (!nodeIsBefore && nodeIsAfter) {
44397             return 1; //right trailed.
44398         }
44399         
44400         if (nodeIsBefore && !nodeIsAfter) {
44401             return 2;  // left trailed.
44402         }
44403         // fully contined.
44404         return 3;
44405     },
44406
44407     // private? - in a new class?
44408     cleanUpPaste :  function()
44409     {
44410         // cleans up the whole document..
44411         Roo.log('cleanuppaste');
44412         
44413         this.cleanUpChildren(this.doc.body);
44414         var clean = this.cleanWordChars(this.doc.body.innerHTML);
44415         if (clean != this.doc.body.innerHTML) {
44416             this.doc.body.innerHTML = clean;
44417         }
44418         
44419     },
44420     
44421     cleanWordChars : function(input) {// change the chars to hex code
44422         var he = Roo.HtmlEditorCore;
44423         
44424         var output = input;
44425         Roo.each(he.swapCodes, function(sw) { 
44426             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
44427             
44428             output = output.replace(swapper, sw[1]);
44429         });
44430         
44431         return output;
44432     },
44433     
44434     
44435     cleanUpChildren : function (n)
44436     {
44437         if (!n.childNodes.length) {
44438             return;
44439         }
44440         for (var i = n.childNodes.length-1; i > -1 ; i--) {
44441            this.cleanUpChild(n.childNodes[i]);
44442         }
44443     },
44444     
44445     
44446         
44447     
44448     cleanUpChild : function (node)
44449     {
44450         var ed = this;
44451         //console.log(node);
44452         if (node.nodeName == "#text") {
44453             // clean up silly Windows -- stuff?
44454             return; 
44455         }
44456         if (node.nodeName == "#comment") {
44457             node.parentNode.removeChild(node);
44458             // clean up silly Windows -- stuff?
44459             return; 
44460         }
44461         var lcname = node.tagName.toLowerCase();
44462         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
44463         // whitelist of tags..
44464         
44465         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
44466             // remove node.
44467             node.parentNode.removeChild(node);
44468             return;
44469             
44470         }
44471         
44472         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
44473         
44474         // spans with no attributes - just remove them..
44475         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
44476             remove_keep_children = true;
44477         }
44478         
44479         // remove <a name=....> as rendering on yahoo mailer is borked with this.
44480         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
44481         
44482         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
44483         //    remove_keep_children = true;
44484         //}
44485         
44486         if (remove_keep_children) {
44487             this.cleanUpChildren(node);
44488             // inserts everything just before this node...
44489             while (node.childNodes.length) {
44490                 var cn = node.childNodes[0];
44491                 node.removeChild(cn);
44492                 node.parentNode.insertBefore(cn, node);
44493             }
44494             node.parentNode.removeChild(node);
44495             return;
44496         }
44497         
44498         if (!node.attributes || !node.attributes.length) {
44499             
44500           
44501             
44502             
44503             this.cleanUpChildren(node);
44504             return;
44505         }
44506         
44507         function cleanAttr(n,v)
44508         {
44509             
44510             if (v.match(/^\./) || v.match(/^\//)) {
44511                 return;
44512             }
44513             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
44514                 return;
44515             }
44516             if (v.match(/^#/)) {
44517                 return;
44518             }
44519 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
44520             node.removeAttribute(n);
44521             
44522         }
44523         
44524         var cwhite = this.cwhite;
44525         var cblack = this.cblack;
44526             
44527         function cleanStyle(n,v)
44528         {
44529             if (v.match(/expression/)) { //XSS?? should we even bother..
44530                 node.removeAttribute(n);
44531                 return;
44532             }
44533             
44534             var parts = v.split(/;/);
44535             var clean = [];
44536             
44537             Roo.each(parts, function(p) {
44538                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
44539                 if (!p.length) {
44540                     return true;
44541                 }
44542                 var l = p.split(':').shift().replace(/\s+/g,'');
44543                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
44544                 
44545                 if ( cwhite.length && cblack.indexOf(l) > -1) {
44546 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
44547                     //node.removeAttribute(n);
44548                     return true;
44549                 }
44550                 //Roo.log()
44551                 // only allow 'c whitelisted system attributes'
44552                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
44553 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
44554                     //node.removeAttribute(n);
44555                     return true;
44556                 }
44557                 
44558                 
44559                  
44560                 
44561                 clean.push(p);
44562                 return true;
44563             });
44564             if (clean.length) { 
44565                 node.setAttribute(n, clean.join(';'));
44566             } else {
44567                 node.removeAttribute(n);
44568             }
44569             
44570         }
44571         
44572         
44573         for (var i = node.attributes.length-1; i > -1 ; i--) {
44574             var a = node.attributes[i];
44575             //console.log(a);
44576             
44577             if (a.name.toLowerCase().substr(0,2)=='on')  {
44578                 node.removeAttribute(a.name);
44579                 continue;
44580             }
44581             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
44582                 node.removeAttribute(a.name);
44583                 continue;
44584             }
44585             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
44586                 cleanAttr(a.name,a.value); // fixme..
44587                 continue;
44588             }
44589             if (a.name == 'style') {
44590                 cleanStyle(a.name,a.value);
44591                 continue;
44592             }
44593             /// clean up MS crap..
44594             // tecnically this should be a list of valid class'es..
44595             
44596             
44597             if (a.name == 'class') {
44598                 if (a.value.match(/^Mso/)) {
44599                     node.removeAttribute('class');
44600                 }
44601                 
44602                 if (a.value.match(/^body$/)) {
44603                     node.removeAttribute('class');
44604                 }
44605                 continue;
44606             }
44607             
44608             // style cleanup!?
44609             // class cleanup?
44610             
44611         }
44612         
44613         
44614         this.cleanUpChildren(node);
44615         
44616         
44617     },
44618     
44619     /**
44620      * Clean up MS wordisms...
44621      */
44622     cleanWord : function(node)
44623     {
44624         if (!node) {
44625             this.cleanWord(this.doc.body);
44626             return;
44627         }
44628         
44629         if(
44630                 node.nodeName == 'SPAN' &&
44631                 !node.hasAttributes() &&
44632                 node.childNodes.length == 1 &&
44633                 node.firstChild.nodeName == "#text"  
44634         ) {
44635             var textNode = node.firstChild;
44636             node.removeChild(textNode);
44637             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
44638                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
44639             }
44640             node.parentNode.insertBefore(textNode, node);
44641             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
44642                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
44643             }
44644             node.parentNode.removeChild(node);
44645         }
44646         
44647         if (node.nodeName == "#text") {
44648             // clean up silly Windows -- stuff?
44649             return; 
44650         }
44651         if (node.nodeName == "#comment") {
44652             node.parentNode.removeChild(node);
44653             // clean up silly Windows -- stuff?
44654             return; 
44655         }
44656         
44657         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
44658             node.parentNode.removeChild(node);
44659             return;
44660         }
44661         //Roo.log(node.tagName);
44662         // remove - but keep children..
44663         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
44664             //Roo.log('-- removed');
44665             while (node.childNodes.length) {
44666                 var cn = node.childNodes[0];
44667                 node.removeChild(cn);
44668                 node.parentNode.insertBefore(cn, node);
44669                 // move node to parent - and clean it..
44670                 this.cleanWord(cn);
44671             }
44672             node.parentNode.removeChild(node);
44673             /// no need to iterate chidlren = it's got none..
44674             //this.iterateChildren(node, this.cleanWord);
44675             return;
44676         }
44677         // clean styles
44678         if (node.className.length) {
44679             
44680             var cn = node.className.split(/\W+/);
44681             var cna = [];
44682             Roo.each(cn, function(cls) {
44683                 if (cls.match(/Mso[a-zA-Z]+/)) {
44684                     return;
44685                 }
44686                 cna.push(cls);
44687             });
44688             node.className = cna.length ? cna.join(' ') : '';
44689             if (!cna.length) {
44690                 node.removeAttribute("class");
44691             }
44692         }
44693         
44694         if (node.hasAttribute("lang")) {
44695             node.removeAttribute("lang");
44696         }
44697         
44698         if (node.hasAttribute("style")) {
44699             
44700             var styles = node.getAttribute("style").split(";");
44701             var nstyle = [];
44702             Roo.each(styles, function(s) {
44703                 if (!s.match(/:/)) {
44704                     return;
44705                 }
44706                 var kv = s.split(":");
44707                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
44708                     return;
44709                 }
44710                 // what ever is left... we allow.
44711                 nstyle.push(s);
44712             });
44713             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
44714             if (!nstyle.length) {
44715                 node.removeAttribute('style');
44716             }
44717         }
44718         this.iterateChildren(node, this.cleanWord);
44719         
44720         
44721         
44722     },
44723     /**
44724      * iterateChildren of a Node, calling fn each time, using this as the scole..
44725      * @param {DomNode} node node to iterate children of.
44726      * @param {Function} fn method of this class to call on each item.
44727      */
44728     iterateChildren : function(node, fn)
44729     {
44730         if (!node.childNodes.length) {
44731                 return;
44732         }
44733         for (var i = node.childNodes.length-1; i > -1 ; i--) {
44734            fn.call(this, node.childNodes[i])
44735         }
44736     },
44737     
44738     
44739     /**
44740      * cleanTableWidths.
44741      *
44742      * Quite often pasting from word etc.. results in tables with column and widths.
44743      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
44744      *
44745      */
44746     cleanTableWidths : function(node)
44747     {
44748          
44749          
44750         if (!node) {
44751             this.cleanTableWidths(this.doc.body);
44752             return;
44753         }
44754         
44755         // ignore list...
44756         if (node.nodeName == "#text" || node.nodeName == "#comment") {
44757             return; 
44758         }
44759         Roo.log(node.tagName);
44760         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
44761             this.iterateChildren(node, this.cleanTableWidths);
44762             return;
44763         }
44764         if (node.hasAttribute('width')) {
44765             node.removeAttribute('width');
44766         }
44767         
44768          
44769         if (node.hasAttribute("style")) {
44770             // pretty basic...
44771             
44772             var styles = node.getAttribute("style").split(";");
44773             var nstyle = [];
44774             Roo.each(styles, function(s) {
44775                 if (!s.match(/:/)) {
44776                     return;
44777                 }
44778                 var kv = s.split(":");
44779                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
44780                     return;
44781                 }
44782                 // what ever is left... we allow.
44783                 nstyle.push(s);
44784             });
44785             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
44786             if (!nstyle.length) {
44787                 node.removeAttribute('style');
44788             }
44789         }
44790         
44791         this.iterateChildren(node, this.cleanTableWidths);
44792         
44793         
44794     },
44795     
44796     
44797     
44798     
44799     domToHTML : function(currentElement, depth, nopadtext) {
44800         
44801         depth = depth || 0;
44802         nopadtext = nopadtext || false;
44803     
44804         if (!currentElement) {
44805             return this.domToHTML(this.doc.body);
44806         }
44807         
44808         //Roo.log(currentElement);
44809         var j;
44810         var allText = false;
44811         var nodeName = currentElement.nodeName;
44812         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
44813         
44814         if  (nodeName == '#text') {
44815             
44816             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
44817         }
44818         
44819         
44820         var ret = '';
44821         if (nodeName != 'BODY') {
44822              
44823             var i = 0;
44824             // Prints the node tagName, such as <A>, <IMG>, etc
44825             if (tagName) {
44826                 var attr = [];
44827                 for(i = 0; i < currentElement.attributes.length;i++) {
44828                     // quoting?
44829                     var aname = currentElement.attributes.item(i).name;
44830                     if (!currentElement.attributes.item(i).value.length) {
44831                         continue;
44832                     }
44833                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
44834                 }
44835                 
44836                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
44837             } 
44838             else {
44839                 
44840                 // eack
44841             }
44842         } else {
44843             tagName = false;
44844         }
44845         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
44846             return ret;
44847         }
44848         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
44849             nopadtext = true;
44850         }
44851         
44852         
44853         // Traverse the tree
44854         i = 0;
44855         var currentElementChild = currentElement.childNodes.item(i);
44856         var allText = true;
44857         var innerHTML  = '';
44858         lastnode = '';
44859         while (currentElementChild) {
44860             // Formatting code (indent the tree so it looks nice on the screen)
44861             var nopad = nopadtext;
44862             if (lastnode == 'SPAN') {
44863                 nopad  = true;
44864             }
44865             // text
44866             if  (currentElementChild.nodeName == '#text') {
44867                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
44868                 toadd = nopadtext ? toadd : toadd.trim();
44869                 if (!nopad && toadd.length > 80) {
44870                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
44871                 }
44872                 innerHTML  += toadd;
44873                 
44874                 i++;
44875                 currentElementChild = currentElement.childNodes.item(i);
44876                 lastNode = '';
44877                 continue;
44878             }
44879             allText = false;
44880             
44881             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
44882                 
44883             // Recursively traverse the tree structure of the child node
44884             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
44885             lastnode = currentElementChild.nodeName;
44886             i++;
44887             currentElementChild=currentElement.childNodes.item(i);
44888         }
44889         
44890         ret += innerHTML;
44891         
44892         if (!allText) {
44893                 // The remaining code is mostly for formatting the tree
44894             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
44895         }
44896         
44897         
44898         if (tagName) {
44899             ret+= "</"+tagName+">";
44900         }
44901         return ret;
44902         
44903     },
44904         
44905     applyBlacklists : function()
44906     {
44907         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
44908         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
44909         
44910         this.white = [];
44911         this.black = [];
44912         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
44913             if (b.indexOf(tag) > -1) {
44914                 return;
44915             }
44916             this.white.push(tag);
44917             
44918         }, this);
44919         
44920         Roo.each(w, function(tag) {
44921             if (b.indexOf(tag) > -1) {
44922                 return;
44923             }
44924             if (this.white.indexOf(tag) > -1) {
44925                 return;
44926             }
44927             this.white.push(tag);
44928             
44929         }, this);
44930         
44931         
44932         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
44933             if (w.indexOf(tag) > -1) {
44934                 return;
44935             }
44936             this.black.push(tag);
44937             
44938         }, this);
44939         
44940         Roo.each(b, function(tag) {
44941             if (w.indexOf(tag) > -1) {
44942                 return;
44943             }
44944             if (this.black.indexOf(tag) > -1) {
44945                 return;
44946             }
44947             this.black.push(tag);
44948             
44949         }, this);
44950         
44951         
44952         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
44953         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
44954         
44955         this.cwhite = [];
44956         this.cblack = [];
44957         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
44958             if (b.indexOf(tag) > -1) {
44959                 return;
44960             }
44961             this.cwhite.push(tag);
44962             
44963         }, this);
44964         
44965         Roo.each(w, function(tag) {
44966             if (b.indexOf(tag) > -1) {
44967                 return;
44968             }
44969             if (this.cwhite.indexOf(tag) > -1) {
44970                 return;
44971             }
44972             this.cwhite.push(tag);
44973             
44974         }, this);
44975         
44976         
44977         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
44978             if (w.indexOf(tag) > -1) {
44979                 return;
44980             }
44981             this.cblack.push(tag);
44982             
44983         }, this);
44984         
44985         Roo.each(b, function(tag) {
44986             if (w.indexOf(tag) > -1) {
44987                 return;
44988             }
44989             if (this.cblack.indexOf(tag) > -1) {
44990                 return;
44991             }
44992             this.cblack.push(tag);
44993             
44994         }, this);
44995     },
44996     
44997     setStylesheets : function(stylesheets)
44998     {
44999         if(typeof(stylesheets) == 'string'){
45000             Roo.get(this.iframe.contentDocument.head).createChild({
45001                 tag : 'link',
45002                 rel : 'stylesheet',
45003                 type : 'text/css',
45004                 href : stylesheets
45005             });
45006             
45007             return;
45008         }
45009         var _this = this;
45010      
45011         Roo.each(stylesheets, function(s) {
45012             if(!s.length){
45013                 return;
45014             }
45015             
45016             Roo.get(_this.iframe.contentDocument.head).createChild({
45017                 tag : 'link',
45018                 rel : 'stylesheet',
45019                 type : 'text/css',
45020                 href : s
45021             });
45022         });
45023
45024         
45025     },
45026     
45027     removeStylesheets : function()
45028     {
45029         var _this = this;
45030         
45031         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
45032             s.remove();
45033         });
45034     },
45035     
45036     setStyle : function(style)
45037     {
45038         Roo.get(this.iframe.contentDocument.head).createChild({
45039             tag : 'style',
45040             type : 'text/css',
45041             html : style
45042         });
45043
45044         return;
45045     }
45046     
45047     // hide stuff that is not compatible
45048     /**
45049      * @event blur
45050      * @hide
45051      */
45052     /**
45053      * @event change
45054      * @hide
45055      */
45056     /**
45057      * @event focus
45058      * @hide
45059      */
45060     /**
45061      * @event specialkey
45062      * @hide
45063      */
45064     /**
45065      * @cfg {String} fieldClass @hide
45066      */
45067     /**
45068      * @cfg {String} focusClass @hide
45069      */
45070     /**
45071      * @cfg {String} autoCreate @hide
45072      */
45073     /**
45074      * @cfg {String} inputType @hide
45075      */
45076     /**
45077      * @cfg {String} invalidClass @hide
45078      */
45079     /**
45080      * @cfg {String} invalidText @hide
45081      */
45082     /**
45083      * @cfg {String} msgFx @hide
45084      */
45085     /**
45086      * @cfg {String} validateOnBlur @hide
45087      */
45088 });
45089
45090 Roo.HtmlEditorCore.white = [
45091         'area', 'br', 'img', 'input', 'hr', 'wbr',
45092         
45093        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
45094        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
45095        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
45096        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
45097        'table',   'ul',         'xmp', 
45098        
45099        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
45100       'thead',   'tr', 
45101      
45102       'dir', 'menu', 'ol', 'ul', 'dl',
45103        
45104       'embed',  'object'
45105 ];
45106
45107
45108 Roo.HtmlEditorCore.black = [
45109     //    'embed',  'object', // enable - backend responsiblity to clean thiese
45110         'applet', // 
45111         'base',   'basefont', 'bgsound', 'blink',  'body', 
45112         'frame',  'frameset', 'head',    'html',   'ilayer', 
45113         'iframe', 'layer',  'link',     'meta',    'object',   
45114         'script', 'style' ,'title',  'xml' // clean later..
45115 ];
45116 Roo.HtmlEditorCore.clean = [
45117     'script', 'style', 'title', 'xml'
45118 ];
45119 Roo.HtmlEditorCore.remove = [
45120     'font'
45121 ];
45122 // attributes..
45123
45124 Roo.HtmlEditorCore.ablack = [
45125     'on'
45126 ];
45127     
45128 Roo.HtmlEditorCore.aclean = [ 
45129     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
45130 ];
45131
45132 // protocols..
45133 Roo.HtmlEditorCore.pwhite= [
45134         'http',  'https',  'mailto'
45135 ];
45136
45137 // white listed style attributes.
45138 Roo.HtmlEditorCore.cwhite= [
45139       //  'text-align', /// default is to allow most things..
45140       
45141          
45142 //        'font-size'//??
45143 ];
45144
45145 // black listed style attributes.
45146 Roo.HtmlEditorCore.cblack= [
45147       //  'font-size' -- this can be set by the project 
45148 ];
45149
45150
45151 Roo.HtmlEditorCore.swapCodes   =[ 
45152     [    8211, "--" ], 
45153     [    8212, "--" ], 
45154     [    8216,  "'" ],  
45155     [    8217, "'" ],  
45156     [    8220, '"' ],  
45157     [    8221, '"' ],  
45158     [    8226, "*" ],  
45159     [    8230, "..." ]
45160 ]; 
45161
45162     //<script type="text/javascript">
45163
45164 /*
45165  * Ext JS Library 1.1.1
45166  * Copyright(c) 2006-2007, Ext JS, LLC.
45167  * Licence LGPL
45168  * 
45169  */
45170  
45171  
45172 Roo.form.HtmlEditor = function(config){
45173     
45174     
45175     
45176     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
45177     
45178     if (!this.toolbars) {
45179         this.toolbars = [];
45180     }
45181     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
45182     
45183     
45184 };
45185
45186 /**
45187  * @class Roo.form.HtmlEditor
45188  * @extends Roo.form.Field
45189  * Provides a lightweight HTML Editor component.
45190  *
45191  * This has been tested on Fireforx / Chrome.. IE may not be so great..
45192  * 
45193  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
45194  * supported by this editor.</b><br/><br/>
45195  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
45196  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
45197  */
45198 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
45199     /**
45200      * @cfg {Boolean} clearUp
45201      */
45202     clearUp : true,
45203       /**
45204      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
45205      */
45206     toolbars : false,
45207    
45208      /**
45209      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
45210      *                        Roo.resizable.
45211      */
45212     resizable : false,
45213      /**
45214      * @cfg {Number} height (in pixels)
45215      */   
45216     height: 300,
45217    /**
45218      * @cfg {Number} width (in pixels)
45219      */   
45220     width: 500,
45221     
45222     /**
45223      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
45224      * 
45225      */
45226     stylesheets: false,
45227     
45228     
45229      /**
45230      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
45231      * 
45232      */
45233     cblack: false,
45234     /**
45235      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
45236      * 
45237      */
45238     cwhite: false,
45239     
45240      /**
45241      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
45242      * 
45243      */
45244     black: false,
45245     /**
45246      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
45247      * 
45248      */
45249     white: false,
45250     
45251     // id of frame..
45252     frameId: false,
45253     
45254     // private properties
45255     validationEvent : false,
45256     deferHeight: true,
45257     initialized : false,
45258     activated : false,
45259     
45260     onFocus : Roo.emptyFn,
45261     iframePad:3,
45262     hideMode:'offsets',
45263     
45264     actionMode : 'container', // defaults to hiding it...
45265     
45266     defaultAutoCreate : { // modified by initCompnoent..
45267         tag: "textarea",
45268         style:"width:500px;height:300px;",
45269         autocomplete: "new-password"
45270     },
45271
45272     // private
45273     initComponent : function(){
45274         this.addEvents({
45275             /**
45276              * @event initialize
45277              * Fires when the editor is fully initialized (including the iframe)
45278              * @param {HtmlEditor} this
45279              */
45280             initialize: true,
45281             /**
45282              * @event activate
45283              * Fires when the editor is first receives the focus. Any insertion must wait
45284              * until after this event.
45285              * @param {HtmlEditor} this
45286              */
45287             activate: true,
45288              /**
45289              * @event beforesync
45290              * Fires before the textarea is updated with content from the editor iframe. Return false
45291              * to cancel the sync.
45292              * @param {HtmlEditor} this
45293              * @param {String} html
45294              */
45295             beforesync: true,
45296              /**
45297              * @event beforepush
45298              * Fires before the iframe editor is updated with content from the textarea. Return false
45299              * to cancel the push.
45300              * @param {HtmlEditor} this
45301              * @param {String} html
45302              */
45303             beforepush: true,
45304              /**
45305              * @event sync
45306              * Fires when the textarea is updated with content from the editor iframe.
45307              * @param {HtmlEditor} this
45308              * @param {String} html
45309              */
45310             sync: true,
45311              /**
45312              * @event push
45313              * Fires when the iframe editor is updated with content from the textarea.
45314              * @param {HtmlEditor} this
45315              * @param {String} html
45316              */
45317             push: true,
45318              /**
45319              * @event editmodechange
45320              * Fires when the editor switches edit modes
45321              * @param {HtmlEditor} this
45322              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
45323              */
45324             editmodechange: true,
45325             /**
45326              * @event editorevent
45327              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
45328              * @param {HtmlEditor} this
45329              */
45330             editorevent: true,
45331             /**
45332              * @event firstfocus
45333              * Fires when on first focus - needed by toolbars..
45334              * @param {HtmlEditor} this
45335              */
45336             firstfocus: true,
45337             /**
45338              * @event autosave
45339              * Auto save the htmlEditor value as a file into Events
45340              * @param {HtmlEditor} this
45341              */
45342             autosave: true,
45343             /**
45344              * @event savedpreview
45345              * preview the saved version of htmlEditor
45346              * @param {HtmlEditor} this
45347              */
45348             savedpreview: true,
45349             
45350             /**
45351             * @event stylesheetsclick
45352             * Fires when press the Sytlesheets button
45353             * @param {Roo.HtmlEditorCore} this
45354             */
45355             stylesheetsclick: true
45356         });
45357         this.defaultAutoCreate =  {
45358             tag: "textarea",
45359             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
45360             autocomplete: "new-password"
45361         };
45362     },
45363
45364     /**
45365      * Protected method that will not generally be called directly. It
45366      * is called when the editor creates its toolbar. Override this method if you need to
45367      * add custom toolbar buttons.
45368      * @param {HtmlEditor} editor
45369      */
45370     createToolbar : function(editor){
45371         Roo.log("create toolbars");
45372         if (!editor.toolbars || !editor.toolbars.length) {
45373             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
45374         }
45375         
45376         for (var i =0 ; i < editor.toolbars.length;i++) {
45377             editor.toolbars[i] = Roo.factory(
45378                     typeof(editor.toolbars[i]) == 'string' ?
45379                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
45380                 Roo.form.HtmlEditor);
45381             editor.toolbars[i].init(editor);
45382         }
45383          
45384         
45385     },
45386
45387      
45388     // private
45389     onRender : function(ct, position)
45390     {
45391         var _t = this;
45392         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
45393         
45394         this.wrap = this.el.wrap({
45395             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
45396         });
45397         
45398         this.editorcore.onRender(ct, position);
45399          
45400         if (this.resizable) {
45401             this.resizeEl = new Roo.Resizable(this.wrap, {
45402                 pinned : true,
45403                 wrap: true,
45404                 dynamic : true,
45405                 minHeight : this.height,
45406                 height: this.height,
45407                 handles : this.resizable,
45408                 width: this.width,
45409                 listeners : {
45410                     resize : function(r, w, h) {
45411                         _t.onResize(w,h); // -something
45412                     }
45413                 }
45414             });
45415             
45416         }
45417         this.createToolbar(this);
45418        
45419         
45420         if(!this.width){
45421             this.setSize(this.wrap.getSize());
45422         }
45423         if (this.resizeEl) {
45424             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
45425             // should trigger onReize..
45426         }
45427         
45428         this.keyNav = new Roo.KeyNav(this.el, {
45429             
45430             "tab" : function(e){
45431                 e.preventDefault();
45432                 
45433                 var value = this.getValue();
45434                 
45435                 var start = this.el.dom.selectionStart;
45436                 var end = this.el.dom.selectionEnd;
45437                 
45438                 if(!e.shiftKey){
45439                     
45440                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
45441                     this.el.dom.setSelectionRange(end + 1, end + 1);
45442                     return;
45443                 }
45444                 
45445                 var f = value.substring(0, start).split("\t");
45446                 
45447                 if(f.pop().length != 0){
45448                     return;
45449                 }
45450                 
45451                 this.setValue(f.join("\t") + value.substring(end));
45452                 this.el.dom.setSelectionRange(start - 1, start - 1);
45453                 
45454             },
45455             
45456             "home" : function(e){
45457                 e.preventDefault();
45458                 
45459                 var curr = this.el.dom.selectionStart;
45460                 var lines = this.getValue().split("\n");
45461                 
45462                 if(!lines.length){
45463                     return;
45464                 }
45465                 
45466                 if(e.ctrlKey){
45467                     this.el.dom.setSelectionRange(0, 0);
45468                     return;
45469                 }
45470                 
45471                 var pos = 0;
45472                 
45473                 for (var i = 0; i < lines.length;i++) {
45474                     pos += lines[i].length;
45475                     
45476                     if(i != 0){
45477                         pos += 1;
45478                     }
45479                     
45480                     if(pos < curr){
45481                         continue;
45482                     }
45483                     
45484                     pos -= lines[i].length;
45485                     
45486                     break;
45487                 }
45488                 
45489                 if(!e.shiftKey){
45490                     this.el.dom.setSelectionRange(pos, pos);
45491                     return;
45492                 }
45493                 
45494                 this.el.dom.selectionStart = pos;
45495                 this.el.dom.selectionEnd = curr;
45496             },
45497             
45498             "end" : function(e){
45499                 e.preventDefault();
45500                 
45501                 var curr = this.el.dom.selectionStart;
45502                 var lines = this.getValue().split("\n");
45503                 
45504                 if(!lines.length){
45505                     return;
45506                 }
45507                 
45508                 if(e.ctrlKey){
45509                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
45510                     return;
45511                 }
45512                 
45513                 var pos = 0;
45514                 
45515                 for (var i = 0; i < lines.length;i++) {
45516                     
45517                     pos += lines[i].length;
45518                     
45519                     if(i != 0){
45520                         pos += 1;
45521                     }
45522                     
45523                     if(pos < curr){
45524                         continue;
45525                     }
45526                     
45527                     break;
45528                 }
45529                 
45530                 if(!e.shiftKey){
45531                     this.el.dom.setSelectionRange(pos, pos);
45532                     return;
45533                 }
45534                 
45535                 this.el.dom.selectionStart = curr;
45536                 this.el.dom.selectionEnd = pos;
45537             },
45538
45539             scope : this,
45540
45541             doRelay : function(foo, bar, hname){
45542                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
45543             },
45544
45545             forceKeyDown: true
45546         });
45547         
45548 //        if(this.autosave && this.w){
45549 //            this.autoSaveFn = setInterval(this.autosave, 1000);
45550 //        }
45551     },
45552
45553     // private
45554     onResize : function(w, h)
45555     {
45556         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
45557         var ew = false;
45558         var eh = false;
45559         
45560         if(this.el ){
45561             if(typeof w == 'number'){
45562                 var aw = w - this.wrap.getFrameWidth('lr');
45563                 this.el.setWidth(this.adjustWidth('textarea', aw));
45564                 ew = aw;
45565             }
45566             if(typeof h == 'number'){
45567                 var tbh = 0;
45568                 for (var i =0; i < this.toolbars.length;i++) {
45569                     // fixme - ask toolbars for heights?
45570                     tbh += this.toolbars[i].tb.el.getHeight();
45571                     if (this.toolbars[i].footer) {
45572                         tbh += this.toolbars[i].footer.el.getHeight();
45573                     }
45574                 }
45575                 
45576                 
45577                 
45578                 
45579                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
45580                 ah -= 5; // knock a few pixes off for look..
45581 //                Roo.log(ah);
45582                 this.el.setHeight(this.adjustWidth('textarea', ah));
45583                 var eh = ah;
45584             }
45585         }
45586         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
45587         this.editorcore.onResize(ew,eh);
45588         
45589     },
45590
45591     /**
45592      * Toggles the editor between standard and source edit mode.
45593      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
45594      */
45595     toggleSourceEdit : function(sourceEditMode)
45596     {
45597         this.editorcore.toggleSourceEdit(sourceEditMode);
45598         
45599         if(this.editorcore.sourceEditMode){
45600             Roo.log('editor - showing textarea');
45601             
45602 //            Roo.log('in');
45603 //            Roo.log(this.syncValue());
45604             this.editorcore.syncValue();
45605             this.el.removeClass('x-hidden');
45606             this.el.dom.removeAttribute('tabIndex');
45607             this.el.focus();
45608             
45609             for (var i = 0; i < this.toolbars.length; i++) {
45610                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
45611                     this.toolbars[i].tb.hide();
45612                     this.toolbars[i].footer.hide();
45613                 }
45614             }
45615             
45616         }else{
45617             Roo.log('editor - hiding textarea');
45618 //            Roo.log('out')
45619 //            Roo.log(this.pushValue()); 
45620             this.editorcore.pushValue();
45621             
45622             this.el.addClass('x-hidden');
45623             this.el.dom.setAttribute('tabIndex', -1);
45624             
45625             for (var i = 0; i < this.toolbars.length; i++) {
45626                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
45627                     this.toolbars[i].tb.show();
45628                     this.toolbars[i].footer.show();
45629                 }
45630             }
45631             
45632             //this.deferFocus();
45633         }
45634         
45635         this.setSize(this.wrap.getSize());
45636         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
45637         
45638         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
45639     },
45640  
45641     // private (for BoxComponent)
45642     adjustSize : Roo.BoxComponent.prototype.adjustSize,
45643
45644     // private (for BoxComponent)
45645     getResizeEl : function(){
45646         return this.wrap;
45647     },
45648
45649     // private (for BoxComponent)
45650     getPositionEl : function(){
45651         return this.wrap;
45652     },
45653
45654     // private
45655     initEvents : function(){
45656         this.originalValue = this.getValue();
45657     },
45658
45659     /**
45660      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
45661      * @method
45662      */
45663     markInvalid : Roo.emptyFn,
45664     /**
45665      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
45666      * @method
45667      */
45668     clearInvalid : Roo.emptyFn,
45669
45670     setValue : function(v){
45671         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
45672         this.editorcore.pushValue();
45673     },
45674
45675      
45676     // private
45677     deferFocus : function(){
45678         this.focus.defer(10, this);
45679     },
45680
45681     // doc'ed in Field
45682     focus : function(){
45683         this.editorcore.focus();
45684         
45685     },
45686       
45687
45688     // private
45689     onDestroy : function(){
45690         
45691         
45692         
45693         if(this.rendered){
45694             
45695             for (var i =0; i < this.toolbars.length;i++) {
45696                 // fixme - ask toolbars for heights?
45697                 this.toolbars[i].onDestroy();
45698             }
45699             
45700             this.wrap.dom.innerHTML = '';
45701             this.wrap.remove();
45702         }
45703     },
45704
45705     // private
45706     onFirstFocus : function(){
45707         //Roo.log("onFirstFocus");
45708         this.editorcore.onFirstFocus();
45709          for (var i =0; i < this.toolbars.length;i++) {
45710             this.toolbars[i].onFirstFocus();
45711         }
45712         
45713     },
45714     
45715     // private
45716     syncValue : function()
45717     {
45718         this.editorcore.syncValue();
45719     },
45720     
45721     pushValue : function()
45722     {
45723         this.editorcore.pushValue();
45724     },
45725     
45726     setStylesheets : function(stylesheets)
45727     {
45728         this.editorcore.setStylesheets(stylesheets);
45729     },
45730     
45731     removeStylesheets : function()
45732     {
45733         this.editorcore.removeStylesheets();
45734     }
45735      
45736     
45737     // hide stuff that is not compatible
45738     /**
45739      * @event blur
45740      * @hide
45741      */
45742     /**
45743      * @event change
45744      * @hide
45745      */
45746     /**
45747      * @event focus
45748      * @hide
45749      */
45750     /**
45751      * @event specialkey
45752      * @hide
45753      */
45754     /**
45755      * @cfg {String} fieldClass @hide
45756      */
45757     /**
45758      * @cfg {String} focusClass @hide
45759      */
45760     /**
45761      * @cfg {String} autoCreate @hide
45762      */
45763     /**
45764      * @cfg {String} inputType @hide
45765      */
45766     /**
45767      * @cfg {String} invalidClass @hide
45768      */
45769     /**
45770      * @cfg {String} invalidText @hide
45771      */
45772     /**
45773      * @cfg {String} msgFx @hide
45774      */
45775     /**
45776      * @cfg {String} validateOnBlur @hide
45777      */
45778 });
45779  
45780     // <script type="text/javascript">
45781 /*
45782  * Based on
45783  * Ext JS Library 1.1.1
45784  * Copyright(c) 2006-2007, Ext JS, LLC.
45785  *  
45786  
45787  */
45788
45789 /**
45790  * @class Roo.form.HtmlEditorToolbar1
45791  * Basic Toolbar
45792  * 
45793  * Usage:
45794  *
45795  new Roo.form.HtmlEditor({
45796     ....
45797     toolbars : [
45798         new Roo.form.HtmlEditorToolbar1({
45799             disable : { fonts: 1 , format: 1, ..., ... , ...],
45800             btns : [ .... ]
45801         })
45802     }
45803      
45804  * 
45805  * @cfg {Object} disable List of elements to disable..
45806  * @cfg {Array} btns List of additional buttons.
45807  * 
45808  * 
45809  * NEEDS Extra CSS? 
45810  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
45811  */
45812  
45813 Roo.form.HtmlEditor.ToolbarStandard = function(config)
45814 {
45815     
45816     Roo.apply(this, config);
45817     
45818     // default disabled, based on 'good practice'..
45819     this.disable = this.disable || {};
45820     Roo.applyIf(this.disable, {
45821         fontSize : true,
45822         colors : true,
45823         specialElements : true
45824     });
45825     
45826     
45827     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
45828     // dont call parent... till later.
45829 }
45830
45831 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
45832     
45833     tb: false,
45834     
45835     rendered: false,
45836     
45837     editor : false,
45838     editorcore : false,
45839     /**
45840      * @cfg {Object} disable  List of toolbar elements to disable
45841          
45842      */
45843     disable : false,
45844     
45845     
45846      /**
45847      * @cfg {String} createLinkText The default text for the create link prompt
45848      */
45849     createLinkText : 'Please enter the URL for the link:',
45850     /**
45851      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
45852      */
45853     defaultLinkValue : 'http:/'+'/',
45854    
45855     
45856       /**
45857      * @cfg {Array} fontFamilies An array of available font families
45858      */
45859     fontFamilies : [
45860         'Arial',
45861         'Courier New',
45862         'Tahoma',
45863         'Times New Roman',
45864         'Verdana'
45865     ],
45866     
45867     specialChars : [
45868            "&#169;",
45869           "&#174;",     
45870           "&#8482;",    
45871           "&#163;" ,    
45872          // "&#8212;",    
45873           "&#8230;",    
45874           "&#247;" ,    
45875         //  "&#225;" ,     ?? a acute?
45876            "&#8364;"    , //Euro
45877        //   "&#8220;"    ,
45878         //  "&#8221;"    ,
45879         //  "&#8226;"    ,
45880           "&#176;"  //   , // degrees
45881
45882          // "&#233;"     , // e ecute
45883          // "&#250;"     , // u ecute?
45884     ],
45885     
45886     specialElements : [
45887         {
45888             text: "Insert Table",
45889             xtype: 'MenuItem',
45890             xns : Roo.Menu,
45891             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
45892                 
45893         },
45894         {    
45895             text: "Insert Image",
45896             xtype: 'MenuItem',
45897             xns : Roo.Menu,
45898             ihtml : '<img src="about:blank"/>'
45899             
45900         }
45901         
45902          
45903     ],
45904     
45905     
45906     inputElements : [ 
45907             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
45908             "input:submit", "input:button", "select", "textarea", "label" ],
45909     formats : [
45910         ["p"] ,  
45911         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
45912         ["pre"],[ "code"], 
45913         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
45914         ['div'],['span'],
45915         ['sup'],['sub']
45916     ],
45917     
45918     cleanStyles : [
45919         "font-size"
45920     ],
45921      /**
45922      * @cfg {String} defaultFont default font to use.
45923      */
45924     defaultFont: 'tahoma',
45925    
45926     fontSelect : false,
45927     
45928     
45929     formatCombo : false,
45930     
45931     init : function(editor)
45932     {
45933         this.editor = editor;
45934         this.editorcore = editor.editorcore ? editor.editorcore : editor;
45935         var editorcore = this.editorcore;
45936         
45937         var _t = this;
45938         
45939         var fid = editorcore.frameId;
45940         var etb = this;
45941         function btn(id, toggle, handler){
45942             var xid = fid + '-'+ id ;
45943             return {
45944                 id : xid,
45945                 cmd : id,
45946                 cls : 'x-btn-icon x-edit-'+id,
45947                 enableToggle:toggle !== false,
45948                 scope: _t, // was editor...
45949                 handler:handler||_t.relayBtnCmd,
45950                 clickEvent:'mousedown',
45951                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
45952                 tabIndex:-1
45953             };
45954         }
45955         
45956         
45957         
45958         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
45959         this.tb = tb;
45960          // stop form submits
45961         tb.el.on('click', function(e){
45962             e.preventDefault(); // what does this do?
45963         });
45964
45965         if(!this.disable.font) { // && !Roo.isSafari){
45966             /* why no safari for fonts 
45967             editor.fontSelect = tb.el.createChild({
45968                 tag:'select',
45969                 tabIndex: -1,
45970                 cls:'x-font-select',
45971                 html: this.createFontOptions()
45972             });
45973             
45974             editor.fontSelect.on('change', function(){
45975                 var font = editor.fontSelect.dom.value;
45976                 editor.relayCmd('fontname', font);
45977                 editor.deferFocus();
45978             }, editor);
45979             
45980             tb.add(
45981                 editor.fontSelect.dom,
45982                 '-'
45983             );
45984             */
45985             
45986         };
45987         if(!this.disable.formats){
45988             this.formatCombo = new Roo.form.ComboBox({
45989                 store: new Roo.data.SimpleStore({
45990                     id : 'tag',
45991                     fields: ['tag'],
45992                     data : this.formats // from states.js
45993                 }),
45994                 blockFocus : true,
45995                 name : '',
45996                 //autoCreate : {tag: "div",  size: "20"},
45997                 displayField:'tag',
45998                 typeAhead: false,
45999                 mode: 'local',
46000                 editable : false,
46001                 triggerAction: 'all',
46002                 emptyText:'Add tag',
46003                 selectOnFocus:true,
46004                 width:135,
46005                 listeners : {
46006                     'select': function(c, r, i) {
46007                         editorcore.insertTag(r.get('tag'));
46008                         editor.focus();
46009                     }
46010                 }
46011
46012             });
46013             tb.addField(this.formatCombo);
46014             
46015         }
46016         
46017         if(!this.disable.format){
46018             tb.add(
46019                 btn('bold'),
46020                 btn('italic'),
46021                 btn('underline'),
46022                 btn('strikethrough')
46023             );
46024         };
46025         if(!this.disable.fontSize){
46026             tb.add(
46027                 '-',
46028                 
46029                 
46030                 btn('increasefontsize', false, editorcore.adjustFont),
46031                 btn('decreasefontsize', false, editorcore.adjustFont)
46032             );
46033         };
46034         
46035         
46036         if(!this.disable.colors){
46037             tb.add(
46038                 '-', {
46039                     id:editorcore.frameId +'-forecolor',
46040                     cls:'x-btn-icon x-edit-forecolor',
46041                     clickEvent:'mousedown',
46042                     tooltip: this.buttonTips['forecolor'] || undefined,
46043                     tabIndex:-1,
46044                     menu : new Roo.menu.ColorMenu({
46045                         allowReselect: true,
46046                         focus: Roo.emptyFn,
46047                         value:'000000',
46048                         plain:true,
46049                         selectHandler: function(cp, color){
46050                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
46051                             editor.deferFocus();
46052                         },
46053                         scope: editorcore,
46054                         clickEvent:'mousedown'
46055                     })
46056                 }, {
46057                     id:editorcore.frameId +'backcolor',
46058                     cls:'x-btn-icon x-edit-backcolor',
46059                     clickEvent:'mousedown',
46060                     tooltip: this.buttonTips['backcolor'] || undefined,
46061                     tabIndex:-1,
46062                     menu : new Roo.menu.ColorMenu({
46063                         focus: Roo.emptyFn,
46064                         value:'FFFFFF',
46065                         plain:true,
46066                         allowReselect: true,
46067                         selectHandler: function(cp, color){
46068                             if(Roo.isGecko){
46069                                 editorcore.execCmd('useCSS', false);
46070                                 editorcore.execCmd('hilitecolor', color);
46071                                 editorcore.execCmd('useCSS', true);
46072                                 editor.deferFocus();
46073                             }else{
46074                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
46075                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
46076                                 editor.deferFocus();
46077                             }
46078                         },
46079                         scope:editorcore,
46080                         clickEvent:'mousedown'
46081                     })
46082                 }
46083             );
46084         };
46085         // now add all the items...
46086         
46087
46088         if(!this.disable.alignments){
46089             tb.add(
46090                 '-',
46091                 btn('justifyleft'),
46092                 btn('justifycenter'),
46093                 btn('justifyright')
46094             );
46095         };
46096
46097         //if(!Roo.isSafari){
46098             if(!this.disable.links){
46099                 tb.add(
46100                     '-',
46101                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
46102                 );
46103             };
46104
46105             if(!this.disable.lists){
46106                 tb.add(
46107                     '-',
46108                     btn('insertorderedlist'),
46109                     btn('insertunorderedlist')
46110                 );
46111             }
46112             if(!this.disable.sourceEdit){
46113                 tb.add(
46114                     '-',
46115                     btn('sourceedit', true, function(btn){
46116                         this.toggleSourceEdit(btn.pressed);
46117                     })
46118                 );
46119             }
46120         //}
46121         
46122         var smenu = { };
46123         // special menu.. - needs to be tidied up..
46124         if (!this.disable.special) {
46125             smenu = {
46126                 text: "&#169;",
46127                 cls: 'x-edit-none',
46128                 
46129                 menu : {
46130                     items : []
46131                 }
46132             };
46133             for (var i =0; i < this.specialChars.length; i++) {
46134                 smenu.menu.items.push({
46135                     
46136                     html: this.specialChars[i],
46137                     handler: function(a,b) {
46138                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
46139                         //editor.insertAtCursor(a.html);
46140                         
46141                     },
46142                     tabIndex:-1
46143                 });
46144             }
46145             
46146             
46147             tb.add(smenu);
46148             
46149             
46150         }
46151         
46152         var cmenu = { };
46153         if (!this.disable.cleanStyles) {
46154             cmenu = {
46155                 cls: 'x-btn-icon x-btn-clear',
46156                 
46157                 menu : {
46158                     items : []
46159                 }
46160             };
46161             for (var i =0; i < this.cleanStyles.length; i++) {
46162                 cmenu.menu.items.push({
46163                     actiontype : this.cleanStyles[i],
46164                     html: 'Remove ' + this.cleanStyles[i],
46165                     handler: function(a,b) {
46166 //                        Roo.log(a);
46167 //                        Roo.log(b);
46168                         var c = Roo.get(editorcore.doc.body);
46169                         c.select('[style]').each(function(s) {
46170                             s.dom.style.removeProperty(a.actiontype);
46171                         });
46172                         editorcore.syncValue();
46173                     },
46174                     tabIndex:-1
46175                 });
46176             }
46177              cmenu.menu.items.push({
46178                 actiontype : 'tablewidths',
46179                 html: 'Remove Table Widths',
46180                 handler: function(a,b) {
46181                     editorcore.cleanTableWidths();
46182                     editorcore.syncValue();
46183                 },
46184                 tabIndex:-1
46185             });
46186             cmenu.menu.items.push({
46187                 actiontype : 'word',
46188                 html: 'Remove MS Word Formating',
46189                 handler: function(a,b) {
46190                     editorcore.cleanWord();
46191                     editorcore.syncValue();
46192                 },
46193                 tabIndex:-1
46194             });
46195             
46196             cmenu.menu.items.push({
46197                 actiontype : 'all',
46198                 html: 'Remove All Styles',
46199                 handler: function(a,b) {
46200                     
46201                     var c = Roo.get(editorcore.doc.body);
46202                     c.select('[style]').each(function(s) {
46203                         s.dom.removeAttribute('style');
46204                     });
46205                     editorcore.syncValue();
46206                 },
46207                 tabIndex:-1
46208             });
46209             
46210             cmenu.menu.items.push({
46211                 actiontype : 'all',
46212                 html: 'Remove All CSS Classes',
46213                 handler: function(a,b) {
46214                     
46215                     var c = Roo.get(editorcore.doc.body);
46216                     c.select('[class]').each(function(s) {
46217                         s.dom.removeAttribute('class');
46218                     });
46219                     editorcore.cleanWord();
46220                     editorcore.syncValue();
46221                 },
46222                 tabIndex:-1
46223             });
46224             
46225              cmenu.menu.items.push({
46226                 actiontype : 'tidy',
46227                 html: 'Tidy HTML Source',
46228                 handler: function(a,b) {
46229                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
46230                     editorcore.syncValue();
46231                 },
46232                 tabIndex:-1
46233             });
46234             
46235             
46236             tb.add(cmenu);
46237         }
46238          
46239         if (!this.disable.specialElements) {
46240             var semenu = {
46241                 text: "Other;",
46242                 cls: 'x-edit-none',
46243                 menu : {
46244                     items : []
46245                 }
46246             };
46247             for (var i =0; i < this.specialElements.length; i++) {
46248                 semenu.menu.items.push(
46249                     Roo.apply({ 
46250                         handler: function(a,b) {
46251                             editor.insertAtCursor(this.ihtml);
46252                         }
46253                     }, this.specialElements[i])
46254                 );
46255                     
46256             }
46257             
46258             tb.add(semenu);
46259             
46260             
46261         }
46262          
46263         
46264         if (this.btns) {
46265             for(var i =0; i< this.btns.length;i++) {
46266                 var b = Roo.factory(this.btns[i],Roo.form);
46267                 b.cls =  'x-edit-none';
46268                 
46269                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
46270                     b.cls += ' x-init-enable';
46271                 }
46272                 
46273                 b.scope = editorcore;
46274                 tb.add(b);
46275             }
46276         
46277         }
46278         
46279         
46280         
46281         // disable everything...
46282         
46283         this.tb.items.each(function(item){
46284             
46285            if(
46286                 item.id != editorcore.frameId+ '-sourceedit' && 
46287                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
46288             ){
46289                 
46290                 item.disable();
46291             }
46292         });
46293         this.rendered = true;
46294         
46295         // the all the btns;
46296         editor.on('editorevent', this.updateToolbar, this);
46297         // other toolbars need to implement this..
46298         //editor.on('editmodechange', this.updateToolbar, this);
46299     },
46300     
46301     
46302     relayBtnCmd : function(btn) {
46303         this.editorcore.relayCmd(btn.cmd);
46304     },
46305     // private used internally
46306     createLink : function(){
46307         Roo.log("create link?");
46308         var url = prompt(this.createLinkText, this.defaultLinkValue);
46309         if(url && url != 'http:/'+'/'){
46310             this.editorcore.relayCmd('createlink', url);
46311         }
46312     },
46313
46314     
46315     /**
46316      * Protected method that will not generally be called directly. It triggers
46317      * a toolbar update by reading the markup state of the current selection in the editor.
46318      */
46319     updateToolbar: function(){
46320
46321         if(!this.editorcore.activated){
46322             this.editor.onFirstFocus();
46323             return;
46324         }
46325
46326         var btns = this.tb.items.map, 
46327             doc = this.editorcore.doc,
46328             frameId = this.editorcore.frameId;
46329
46330         if(!this.disable.font && !Roo.isSafari){
46331             /*
46332             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
46333             if(name != this.fontSelect.dom.value){
46334                 this.fontSelect.dom.value = name;
46335             }
46336             */
46337         }
46338         if(!this.disable.format){
46339             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
46340             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
46341             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
46342             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
46343         }
46344         if(!this.disable.alignments){
46345             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
46346             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
46347             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
46348         }
46349         if(!Roo.isSafari && !this.disable.lists){
46350             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
46351             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
46352         }
46353         
46354         var ans = this.editorcore.getAllAncestors();
46355         if (this.formatCombo) {
46356             
46357             
46358             var store = this.formatCombo.store;
46359             this.formatCombo.setValue("");
46360             for (var i =0; i < ans.length;i++) {
46361                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
46362                     // select it..
46363                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
46364                     break;
46365                 }
46366             }
46367         }
46368         
46369         
46370         
46371         // hides menus... - so this cant be on a menu...
46372         Roo.menu.MenuMgr.hideAll();
46373
46374         //this.editorsyncValue();
46375     },
46376    
46377     
46378     createFontOptions : function(){
46379         var buf = [], fs = this.fontFamilies, ff, lc;
46380         
46381         
46382         
46383         for(var i = 0, len = fs.length; i< len; i++){
46384             ff = fs[i];
46385             lc = ff.toLowerCase();
46386             buf.push(
46387                 '<option value="',lc,'" style="font-family:',ff,';"',
46388                     (this.defaultFont == lc ? ' selected="true">' : '>'),
46389                     ff,
46390                 '</option>'
46391             );
46392         }
46393         return buf.join('');
46394     },
46395     
46396     toggleSourceEdit : function(sourceEditMode){
46397         
46398         Roo.log("toolbar toogle");
46399         if(sourceEditMode === undefined){
46400             sourceEditMode = !this.sourceEditMode;
46401         }
46402         this.sourceEditMode = sourceEditMode === true;
46403         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
46404         // just toggle the button?
46405         if(btn.pressed !== this.sourceEditMode){
46406             btn.toggle(this.sourceEditMode);
46407             return;
46408         }
46409         
46410         if(sourceEditMode){
46411             Roo.log("disabling buttons");
46412             this.tb.items.each(function(item){
46413                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
46414                     item.disable();
46415                 }
46416             });
46417           
46418         }else{
46419             Roo.log("enabling buttons");
46420             if(this.editorcore.initialized){
46421                 this.tb.items.each(function(item){
46422                     item.enable();
46423                 });
46424             }
46425             
46426         }
46427         Roo.log("calling toggole on editor");
46428         // tell the editor that it's been pressed..
46429         this.editor.toggleSourceEdit(sourceEditMode);
46430        
46431     },
46432      /**
46433      * Object collection of toolbar tooltips for the buttons in the editor. The key
46434      * is the command id associated with that button and the value is a valid QuickTips object.
46435      * For example:
46436 <pre><code>
46437 {
46438     bold : {
46439         title: 'Bold (Ctrl+B)',
46440         text: 'Make the selected text bold.',
46441         cls: 'x-html-editor-tip'
46442     },
46443     italic : {
46444         title: 'Italic (Ctrl+I)',
46445         text: 'Make the selected text italic.',
46446         cls: 'x-html-editor-tip'
46447     },
46448     ...
46449 </code></pre>
46450     * @type Object
46451      */
46452     buttonTips : {
46453         bold : {
46454             title: 'Bold (Ctrl+B)',
46455             text: 'Make the selected text bold.',
46456             cls: 'x-html-editor-tip'
46457         },
46458         italic : {
46459             title: 'Italic (Ctrl+I)',
46460             text: 'Make the selected text italic.',
46461             cls: 'x-html-editor-tip'
46462         },
46463         underline : {
46464             title: 'Underline (Ctrl+U)',
46465             text: 'Underline the selected text.',
46466             cls: 'x-html-editor-tip'
46467         },
46468         strikethrough : {
46469             title: 'Strikethrough',
46470             text: 'Strikethrough the selected text.',
46471             cls: 'x-html-editor-tip'
46472         },
46473         increasefontsize : {
46474             title: 'Grow Text',
46475             text: 'Increase the font size.',
46476             cls: 'x-html-editor-tip'
46477         },
46478         decreasefontsize : {
46479             title: 'Shrink Text',
46480             text: 'Decrease the font size.',
46481             cls: 'x-html-editor-tip'
46482         },
46483         backcolor : {
46484             title: 'Text Highlight Color',
46485             text: 'Change the background color of the selected text.',
46486             cls: 'x-html-editor-tip'
46487         },
46488         forecolor : {
46489             title: 'Font Color',
46490             text: 'Change the color of the selected text.',
46491             cls: 'x-html-editor-tip'
46492         },
46493         justifyleft : {
46494             title: 'Align Text Left',
46495             text: 'Align text to the left.',
46496             cls: 'x-html-editor-tip'
46497         },
46498         justifycenter : {
46499             title: 'Center Text',
46500             text: 'Center text in the editor.',
46501             cls: 'x-html-editor-tip'
46502         },
46503         justifyright : {
46504             title: 'Align Text Right',
46505             text: 'Align text to the right.',
46506             cls: 'x-html-editor-tip'
46507         },
46508         insertunorderedlist : {
46509             title: 'Bullet List',
46510             text: 'Start a bulleted list.',
46511             cls: 'x-html-editor-tip'
46512         },
46513         insertorderedlist : {
46514             title: 'Numbered List',
46515             text: 'Start a numbered list.',
46516             cls: 'x-html-editor-tip'
46517         },
46518         createlink : {
46519             title: 'Hyperlink',
46520             text: 'Make the selected text a hyperlink.',
46521             cls: 'x-html-editor-tip'
46522         },
46523         sourceedit : {
46524             title: 'Source Edit',
46525             text: 'Switch to source editing mode.',
46526             cls: 'x-html-editor-tip'
46527         }
46528     },
46529     // private
46530     onDestroy : function(){
46531         if(this.rendered){
46532             
46533             this.tb.items.each(function(item){
46534                 if(item.menu){
46535                     item.menu.removeAll();
46536                     if(item.menu.el){
46537                         item.menu.el.destroy();
46538                     }
46539                 }
46540                 item.destroy();
46541             });
46542              
46543         }
46544     },
46545     onFirstFocus: function() {
46546         this.tb.items.each(function(item){
46547            item.enable();
46548         });
46549     }
46550 });
46551
46552
46553
46554
46555 // <script type="text/javascript">
46556 /*
46557  * Based on
46558  * Ext JS Library 1.1.1
46559  * Copyright(c) 2006-2007, Ext JS, LLC.
46560  *  
46561  
46562  */
46563
46564  
46565 /**
46566  * @class Roo.form.HtmlEditor.ToolbarContext
46567  * Context Toolbar
46568  * 
46569  * Usage:
46570  *
46571  new Roo.form.HtmlEditor({
46572     ....
46573     toolbars : [
46574         { xtype: 'ToolbarStandard', styles : {} }
46575         { xtype: 'ToolbarContext', disable : {} }
46576     ]
46577 })
46578
46579      
46580  * 
46581  * @config : {Object} disable List of elements to disable.. (not done yet.)
46582  * @config : {Object} styles  Map of styles available.
46583  * 
46584  */
46585
46586 Roo.form.HtmlEditor.ToolbarContext = function(config)
46587 {
46588     
46589     Roo.apply(this, config);
46590     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
46591     // dont call parent... till later.
46592     this.styles = this.styles || {};
46593 }
46594
46595  
46596
46597 Roo.form.HtmlEditor.ToolbarContext.types = {
46598     'IMG' : {
46599         width : {
46600             title: "Width",
46601             width: 40
46602         },
46603         height:  {
46604             title: "Height",
46605             width: 40
46606         },
46607         align: {
46608             title: "Align",
46609             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
46610             width : 80
46611             
46612         },
46613         border: {
46614             title: "Border",
46615             width: 40
46616         },
46617         alt: {
46618             title: "Alt",
46619             width: 120
46620         },
46621         src : {
46622             title: "Src",
46623             width: 220
46624         }
46625         
46626     },
46627     'A' : {
46628         name : {
46629             title: "Name",
46630             width: 50
46631         },
46632         target:  {
46633             title: "Target",
46634             width: 120
46635         },
46636         href:  {
46637             title: "Href",
46638             width: 220
46639         } // border?
46640         
46641     },
46642     'TABLE' : {
46643         rows : {
46644             title: "Rows",
46645             width: 20
46646         },
46647         cols : {
46648             title: "Cols",
46649             width: 20
46650         },
46651         width : {
46652             title: "Width",
46653             width: 40
46654         },
46655         height : {
46656             title: "Height",
46657             width: 40
46658         },
46659         border : {
46660             title: "Border",
46661             width: 20
46662         }
46663     },
46664     'TD' : {
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"],[ "center"],[ "right"],[ "justify"],[ "char"]],
46676             width: 80
46677         },
46678         valign: {
46679             title: "Valign",
46680             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
46681             width: 80
46682         },
46683         colspan: {
46684             title: "Colspan",
46685             width: 20
46686             
46687         },
46688          'font-family'  : {
46689             title : "Font",
46690             style : 'fontFamily',
46691             displayField: 'display',
46692             optname : 'font-family',
46693             width: 140
46694         }
46695     },
46696     'INPUT' : {
46697         name : {
46698             title: "name",
46699             width: 120
46700         },
46701         value : {
46702             title: "Value",
46703             width: 120
46704         },
46705         width : {
46706             title: "Width",
46707             width: 40
46708         }
46709     },
46710     'LABEL' : {
46711         'for' : {
46712             title: "For",
46713             width: 120
46714         }
46715     },
46716     'TEXTAREA' : {
46717           name : {
46718             title: "name",
46719             width: 120
46720         },
46721         rows : {
46722             title: "Rows",
46723             width: 20
46724         },
46725         cols : {
46726             title: "Cols",
46727             width: 20
46728         }
46729     },
46730     'SELECT' : {
46731         name : {
46732             title: "name",
46733             width: 120
46734         },
46735         selectoptions : {
46736             title: "Options",
46737             width: 200
46738         }
46739     },
46740     
46741     // should we really allow this??
46742     // should this just be 
46743     'BODY' : {
46744         title : {
46745             title: "Title",
46746             width: 200,
46747             disabled : true
46748         }
46749     },
46750     'SPAN' : {
46751         'font-family'  : {
46752             title : "Font",
46753             style : 'fontFamily',
46754             displayField: 'display',
46755             optname : 'font-family',
46756             width: 140
46757         }
46758     },
46759     'DIV' : {
46760         'font-family'  : {
46761             title : "Font",
46762             style : 'fontFamily',
46763             displayField: 'display',
46764             optname : 'font-family',
46765             width: 140
46766         }
46767     },
46768      'P' : {
46769         'font-family'  : {
46770             title : "Font",
46771             style : 'fontFamily',
46772             displayField: 'display',
46773             optname : 'font-family',
46774             width: 140
46775         }
46776     },
46777     
46778     '*' : {
46779         // empty..
46780     }
46781
46782 };
46783
46784 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
46785 Roo.form.HtmlEditor.ToolbarContext.stores = false;
46786
46787 Roo.form.HtmlEditor.ToolbarContext.options = {
46788         'font-family'  : [ 
46789                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
46790                 [ 'Courier New', 'Courier New'],
46791                 [ 'Tahoma', 'Tahoma'],
46792                 [ 'Times New Roman,serif', 'Times'],
46793                 [ 'Verdana','Verdana' ]
46794         ]
46795 };
46796
46797 // fixme - these need to be configurable..
46798  
46799
46800 //Roo.form.HtmlEditor.ToolbarContext.types
46801
46802
46803 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
46804     
46805     tb: false,
46806     
46807     rendered: false,
46808     
46809     editor : false,
46810     editorcore : false,
46811     /**
46812      * @cfg {Object} disable  List of toolbar elements to disable
46813          
46814      */
46815     disable : false,
46816     /**
46817      * @cfg {Object} styles List of styles 
46818      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
46819      *
46820      * These must be defined in the page, so they get rendered correctly..
46821      * .headline { }
46822      * TD.underline { }
46823      * 
46824      */
46825     styles : false,
46826     
46827     options: false,
46828     
46829     toolbars : false,
46830     
46831     init : function(editor)
46832     {
46833         this.editor = editor;
46834         this.editorcore = editor.editorcore ? editor.editorcore : editor;
46835         var editorcore = this.editorcore;
46836         
46837         var fid = editorcore.frameId;
46838         var etb = this;
46839         function btn(id, toggle, handler){
46840             var xid = fid + '-'+ id ;
46841             return {
46842                 id : xid,
46843                 cmd : id,
46844                 cls : 'x-btn-icon x-edit-'+id,
46845                 enableToggle:toggle !== false,
46846                 scope: editorcore, // was editor...
46847                 handler:handler||editorcore.relayBtnCmd,
46848                 clickEvent:'mousedown',
46849                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
46850                 tabIndex:-1
46851             };
46852         }
46853         // create a new element.
46854         var wdiv = editor.wrap.createChild({
46855                 tag: 'div'
46856             }, editor.wrap.dom.firstChild.nextSibling, true);
46857         
46858         // can we do this more than once??
46859         
46860          // stop form submits
46861       
46862  
46863         // disable everything...
46864         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
46865         this.toolbars = {};
46866            
46867         for (var i in  ty) {
46868           
46869             this.toolbars[i] = this.buildToolbar(ty[i],i);
46870         }
46871         this.tb = this.toolbars.BODY;
46872         this.tb.el.show();
46873         this.buildFooter();
46874         this.footer.show();
46875         editor.on('hide', function( ) { this.footer.hide() }, this);
46876         editor.on('show', function( ) { this.footer.show() }, this);
46877         
46878          
46879         this.rendered = true;
46880         
46881         // the all the btns;
46882         editor.on('editorevent', this.updateToolbar, this);
46883         // other toolbars need to implement this..
46884         //editor.on('editmodechange', this.updateToolbar, this);
46885     },
46886     
46887     
46888     
46889     /**
46890      * Protected method that will not generally be called directly. It triggers
46891      * a toolbar update by reading the markup state of the current selection in the editor.
46892      *
46893      * Note you can force an update by calling on('editorevent', scope, false)
46894      */
46895     updateToolbar: function(editor,ev,sel){
46896
46897         //Roo.log(ev);
46898         // capture mouse up - this is handy for selecting images..
46899         // perhaps should go somewhere else...
46900         if(!this.editorcore.activated){
46901              this.editor.onFirstFocus();
46902             return;
46903         }
46904         
46905         
46906         
46907         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
46908         // selectNode - might want to handle IE?
46909         if (ev &&
46910             (ev.type == 'mouseup' || ev.type == 'click' ) &&
46911             ev.target && ev.target.tagName == 'IMG') {
46912             // they have click on an image...
46913             // let's see if we can change the selection...
46914             sel = ev.target;
46915          
46916               var nodeRange = sel.ownerDocument.createRange();
46917             try {
46918                 nodeRange.selectNode(sel);
46919             } catch (e) {
46920                 nodeRange.selectNodeContents(sel);
46921             }
46922             //nodeRange.collapse(true);
46923             var s = this.editorcore.win.getSelection();
46924             s.removeAllRanges();
46925             s.addRange(nodeRange);
46926         }  
46927         
46928       
46929         var updateFooter = sel ? false : true;
46930         
46931         
46932         var ans = this.editorcore.getAllAncestors();
46933         
46934         // pick
46935         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
46936         
46937         if (!sel) { 
46938             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
46939             sel = sel ? sel : this.editorcore.doc.body;
46940             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
46941             
46942         }
46943         // pick a menu that exists..
46944         var tn = sel.tagName.toUpperCase();
46945         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
46946         
46947         tn = sel.tagName.toUpperCase();
46948         
46949         var lastSel = this.tb.selectedNode;
46950         
46951         this.tb.selectedNode = sel;
46952         
46953         // if current menu does not match..
46954         
46955         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode) || ev === false) {
46956                 
46957             this.tb.el.hide();
46958             ///console.log("show: " + tn);
46959             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
46960             this.tb.el.show();
46961             // update name
46962             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
46963             
46964             
46965             // update attributes
46966             if (this.tb.fields) {
46967                 this.tb.fields.each(function(e) {
46968                     if (e.stylename) {
46969                         e.setValue(sel.style[e.stylename]);
46970                         return;
46971                     } 
46972                    e.setValue(sel.getAttribute(e.attrname));
46973                 });
46974             }
46975             
46976             var hasStyles = false;
46977             for(var i in this.styles) {
46978                 hasStyles = true;
46979                 break;
46980             }
46981             
46982             // update styles
46983             if (hasStyles) { 
46984                 var st = this.tb.fields.item(0);
46985                 
46986                 st.store.removeAll();
46987                
46988                 
46989                 var cn = sel.className.split(/\s+/);
46990                 
46991                 var avs = [];
46992                 if (this.styles['*']) {
46993                     
46994                     Roo.each(this.styles['*'], function(v) {
46995                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
46996                     });
46997                 }
46998                 if (this.styles[tn]) { 
46999                     Roo.each(this.styles[tn], function(v) {
47000                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
47001                     });
47002                 }
47003                 
47004                 st.store.loadData(avs);
47005                 st.collapse();
47006                 st.setValue(cn);
47007             }
47008             // flag our selected Node.
47009             this.tb.selectedNode = sel;
47010            
47011            
47012             Roo.menu.MenuMgr.hideAll();
47013
47014         }
47015         
47016         if (!updateFooter) {
47017             //this.footDisp.dom.innerHTML = ''; 
47018             return;
47019         }
47020         // update the footer
47021         //
47022         var html = '';
47023         
47024         this.footerEls = ans.reverse();
47025         Roo.each(this.footerEls, function(a,i) {
47026             if (!a) { return; }
47027             html += html.length ? ' &gt; '  :  '';
47028             
47029             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
47030             
47031         });
47032        
47033         // 
47034         var sz = this.footDisp.up('td').getSize();
47035         this.footDisp.dom.style.width = (sz.width -10) + 'px';
47036         this.footDisp.dom.style.marginLeft = '5px';
47037         
47038         this.footDisp.dom.style.overflow = 'hidden';
47039         
47040         this.footDisp.dom.innerHTML = html;
47041             
47042         //this.editorsyncValue();
47043     },
47044      
47045     
47046    
47047        
47048     // private
47049     onDestroy : function(){
47050         if(this.rendered){
47051             
47052             this.tb.items.each(function(item){
47053                 if(item.menu){
47054                     item.menu.removeAll();
47055                     if(item.menu.el){
47056                         item.menu.el.destroy();
47057                     }
47058                 }
47059                 item.destroy();
47060             });
47061              
47062         }
47063     },
47064     onFirstFocus: function() {
47065         // need to do this for all the toolbars..
47066         this.tb.items.each(function(item){
47067            item.enable();
47068         });
47069     },
47070     buildToolbar: function(tlist, nm)
47071     {
47072         var editor = this.editor;
47073         var editorcore = this.editorcore;
47074          // create a new element.
47075         var wdiv = editor.wrap.createChild({
47076                 tag: 'div'
47077             }, editor.wrap.dom.firstChild.nextSibling, true);
47078         
47079        
47080         var tb = new Roo.Toolbar(wdiv);
47081         // add the name..
47082         
47083         tb.add(nm+ ":&nbsp;");
47084         
47085         var styles = [];
47086         for(var i in this.styles) {
47087             styles.push(i);
47088         }
47089         
47090         // styles...
47091         if (styles && styles.length) {
47092             
47093             // this needs a multi-select checkbox...
47094             tb.addField( new Roo.form.ComboBox({
47095                 store: new Roo.data.SimpleStore({
47096                     id : 'val',
47097                     fields: ['val', 'selected'],
47098                     data : [] 
47099                 }),
47100                 name : '-roo-edit-className',
47101                 attrname : 'className',
47102                 displayField: 'val',
47103                 typeAhead: false,
47104                 mode: 'local',
47105                 editable : false,
47106                 triggerAction: 'all',
47107                 emptyText:'Select Style',
47108                 selectOnFocus:true,
47109                 width: 130,
47110                 listeners : {
47111                     'select': function(c, r, i) {
47112                         // initial support only for on class per el..
47113                         tb.selectedNode.className =  r ? r.get('val') : '';
47114                         editorcore.syncValue();
47115                     }
47116                 }
47117     
47118             }));
47119         }
47120         
47121         var tbc = Roo.form.HtmlEditor.ToolbarContext;
47122         var tbops = tbc.options;
47123         
47124         for (var i in tlist) {
47125             
47126             var item = tlist[i];
47127             tb.add(item.title + ":&nbsp;");
47128             
47129             
47130             //optname == used so you can configure the options available..
47131             var opts = item.opts ? item.opts : false;
47132             if (item.optname) {
47133                 opts = tbops[item.optname];
47134            
47135             }
47136             
47137             if (opts) {
47138                 // opts == pulldown..
47139                 tb.addField( new Roo.form.ComboBox({
47140                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
47141                         id : 'val',
47142                         fields: ['val', 'display'],
47143                         data : opts  
47144                     }),
47145                     name : '-roo-edit-' + i,
47146                     attrname : i,
47147                     stylename : item.style ? item.style : false,
47148                     displayField: item.displayField ? item.displayField : 'val',
47149                     valueField :  'val',
47150                     typeAhead: false,
47151                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
47152                     editable : false,
47153                     triggerAction: 'all',
47154                     emptyText:'Select',
47155                     selectOnFocus:true,
47156                     width: item.width ? item.width  : 130,
47157                     listeners : {
47158                         'select': function(c, r, i) {
47159                             if (c.stylename) {
47160                                 tb.selectedNode.style[c.stylename] =  r.get('val');
47161                                 return;
47162                             }
47163                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
47164                         }
47165                     }
47166
47167                 }));
47168                 continue;
47169                     
47170                  
47171                 
47172                 tb.addField( new Roo.form.TextField({
47173                     name: i,
47174                     width: 100,
47175                     //allowBlank:false,
47176                     value: ''
47177                 }));
47178                 continue;
47179             }
47180             tb.addField( new Roo.form.TextField({
47181                 name: '-roo-edit-' + i,
47182                 attrname : i,
47183                 
47184                 width: item.width,
47185                 //allowBlank:true,
47186                 value: '',
47187                 listeners: {
47188                     'change' : function(f, nv, ov) {
47189                         tb.selectedNode.setAttribute(f.attrname, nv);
47190                         editorcore.syncValue();
47191                     }
47192                 }
47193             }));
47194              
47195         }
47196         
47197         var _this = this;
47198         
47199         if(nm == 'BODY'){
47200             tb.addSeparator();
47201         
47202             tb.addButton( {
47203                 text: 'Stylesheets',
47204
47205                 listeners : {
47206                     click : function ()
47207                     {
47208                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
47209                     }
47210                 }
47211             });
47212         }
47213         
47214         tb.addFill();
47215         tb.addButton( {
47216             text: 'Remove Tag',
47217     
47218             listeners : {
47219                 click : function ()
47220                 {
47221                     // remove
47222                     // undo does not work.
47223                      
47224                     var sn = tb.selectedNode;
47225                     
47226                     var pn = sn.parentNode;
47227                     
47228                     var stn =  sn.childNodes[0];
47229                     var en = sn.childNodes[sn.childNodes.length - 1 ];
47230                     while (sn.childNodes.length) {
47231                         var node = sn.childNodes[0];
47232                         sn.removeChild(node);
47233                         //Roo.log(node);
47234                         pn.insertBefore(node, sn);
47235                         
47236                     }
47237                     pn.removeChild(sn);
47238                     var range = editorcore.createRange();
47239         
47240                     range.setStart(stn,0);
47241                     range.setEnd(en,0); //????
47242                     //range.selectNode(sel);
47243                     
47244                     
47245                     var selection = editorcore.getSelection();
47246                     selection.removeAllRanges();
47247                     selection.addRange(range);
47248                     
47249                     
47250                     
47251                     //_this.updateToolbar(null, null, pn);
47252                     _this.updateToolbar(null, null, null);
47253                     _this.footDisp.dom.innerHTML = ''; 
47254                 }
47255             }
47256             
47257                     
47258                 
47259             
47260         });
47261         
47262         
47263         tb.el.on('click', function(e){
47264             e.preventDefault(); // what does this do?
47265         });
47266         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
47267         tb.el.hide();
47268         tb.name = nm;
47269         // dont need to disable them... as they will get hidden
47270         return tb;
47271          
47272         
47273     },
47274     buildFooter : function()
47275     {
47276         
47277         var fel = this.editor.wrap.createChild();
47278         this.footer = new Roo.Toolbar(fel);
47279         // toolbar has scrolly on left / right?
47280         var footDisp= new Roo.Toolbar.Fill();
47281         var _t = this;
47282         this.footer.add(
47283             {
47284                 text : '&lt;',
47285                 xtype: 'Button',
47286                 handler : function() {
47287                     _t.footDisp.scrollTo('left',0,true)
47288                 }
47289             }
47290         );
47291         this.footer.add( footDisp );
47292         this.footer.add( 
47293             {
47294                 text : '&gt;',
47295                 xtype: 'Button',
47296                 handler : function() {
47297                     // no animation..
47298                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
47299                 }
47300             }
47301         );
47302         var fel = Roo.get(footDisp.el);
47303         fel.addClass('x-editor-context');
47304         this.footDispWrap = fel; 
47305         this.footDispWrap.overflow  = 'hidden';
47306         
47307         this.footDisp = fel.createChild();
47308         this.footDispWrap.on('click', this.onContextClick, this)
47309         
47310         
47311     },
47312     onContextClick : function (ev,dom)
47313     {
47314         ev.preventDefault();
47315         var  cn = dom.className;
47316         //Roo.log(cn);
47317         if (!cn.match(/x-ed-loc-/)) {
47318             return;
47319         }
47320         var n = cn.split('-').pop();
47321         var ans = this.footerEls;
47322         var sel = ans[n];
47323         
47324          // pick
47325         var range = this.editorcore.createRange();
47326         
47327         range.selectNodeContents(sel);
47328         //range.selectNode(sel);
47329         
47330         
47331         var selection = this.editorcore.getSelection();
47332         selection.removeAllRanges();
47333         selection.addRange(range);
47334         
47335         
47336         
47337         this.updateToolbar(null, null, sel);
47338         
47339         
47340     }
47341     
47342     
47343     
47344     
47345     
47346 });
47347
47348
47349
47350
47351
47352 /*
47353  * Based on:
47354  * Ext JS Library 1.1.1
47355  * Copyright(c) 2006-2007, Ext JS, LLC.
47356  *
47357  * Originally Released Under LGPL - original licence link has changed is not relivant.
47358  *
47359  * Fork - LGPL
47360  * <script type="text/javascript">
47361  */
47362  
47363 /**
47364  * @class Roo.form.BasicForm
47365  * @extends Roo.util.Observable
47366  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
47367  * @constructor
47368  * @param {String/HTMLElement/Roo.Element} el The form element or its id
47369  * @param {Object} config Configuration options
47370  */
47371 Roo.form.BasicForm = function(el, config){
47372     this.allItems = [];
47373     this.childForms = [];
47374     Roo.apply(this, config);
47375     /*
47376      * The Roo.form.Field items in this form.
47377      * @type MixedCollection
47378      */
47379      
47380      
47381     this.items = new Roo.util.MixedCollection(false, function(o){
47382         return o.id || (o.id = Roo.id());
47383     });
47384     this.addEvents({
47385         /**
47386          * @event beforeaction
47387          * Fires before any action is performed. Return false to cancel the action.
47388          * @param {Form} this
47389          * @param {Action} action The action to be performed
47390          */
47391         beforeaction: true,
47392         /**
47393          * @event actionfailed
47394          * Fires when an action fails.
47395          * @param {Form} this
47396          * @param {Action} action The action that failed
47397          */
47398         actionfailed : true,
47399         /**
47400          * @event actioncomplete
47401          * Fires when an action is completed.
47402          * @param {Form} this
47403          * @param {Action} action The action that completed
47404          */
47405         actioncomplete : true
47406     });
47407     if(el){
47408         this.initEl(el);
47409     }
47410     Roo.form.BasicForm.superclass.constructor.call(this);
47411     
47412     Roo.form.BasicForm.popover.apply();
47413 };
47414
47415 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
47416     /**
47417      * @cfg {String} method
47418      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
47419      */
47420     /**
47421      * @cfg {DataReader} reader
47422      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
47423      * This is optional as there is built-in support for processing JSON.
47424      */
47425     /**
47426      * @cfg {DataReader} errorReader
47427      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
47428      * This is completely optional as there is built-in support for processing JSON.
47429      */
47430     /**
47431      * @cfg {String} url
47432      * The URL to use for form actions if one isn't supplied in the action options.
47433      */
47434     /**
47435      * @cfg {Boolean} fileUpload
47436      * Set to true if this form is a file upload.
47437      */
47438      
47439     /**
47440      * @cfg {Object} baseParams
47441      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
47442      */
47443      /**
47444      
47445     /**
47446      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
47447      */
47448     timeout: 30,
47449
47450     // private
47451     activeAction : null,
47452
47453     /**
47454      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
47455      * or setValues() data instead of when the form was first created.
47456      */
47457     trackResetOnLoad : false,
47458     
47459     
47460     /**
47461      * childForms - used for multi-tab forms
47462      * @type {Array}
47463      */
47464     childForms : false,
47465     
47466     /**
47467      * allItems - full list of fields.
47468      * @type {Array}
47469      */
47470     allItems : false,
47471     
47472     /**
47473      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
47474      * element by passing it or its id or mask the form itself by passing in true.
47475      * @type Mixed
47476      */
47477     waitMsgTarget : false,
47478     
47479     /**
47480      * @type Boolean
47481      */
47482     disableMask : false,
47483     
47484     /**
47485      * @cfg {Boolean} errorMask (true|false) default false
47486      */
47487     errorMask : false,
47488     
47489     /**
47490      * @cfg {Number} maskOffset Default 100
47491      */
47492     maskOffset : 100,
47493
47494     // private
47495     initEl : function(el){
47496         this.el = Roo.get(el);
47497         this.id = this.el.id || Roo.id();
47498         this.el.on('submit', this.onSubmit, this);
47499         this.el.addClass('x-form');
47500     },
47501
47502     // private
47503     onSubmit : function(e){
47504         e.stopEvent();
47505     },
47506
47507     /**
47508      * Returns true if client-side validation on the form is successful.
47509      * @return Boolean
47510      */
47511     isValid : function(){
47512         var valid = true;
47513         var target = false;
47514         this.items.each(function(f){
47515             if(f.validate()){
47516                 return;
47517             }
47518             
47519             valid = false;
47520                 
47521             if(!target && f.el.isVisible(true)){
47522                 target = f;
47523             }
47524         });
47525         
47526         if(this.errorMask && !valid){
47527             Roo.form.BasicForm.popover.mask(this, target);
47528         }
47529         
47530         return valid;
47531     },
47532
47533     /**
47534      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
47535      * @return Boolean
47536      */
47537     isDirty : function(){
47538         var dirty = false;
47539         this.items.each(function(f){
47540            if(f.isDirty()){
47541                dirty = true;
47542                return false;
47543            }
47544         });
47545         return dirty;
47546     },
47547     
47548     /**
47549      * Returns true if any fields in this form have changed since their original load. (New version)
47550      * @return Boolean
47551      */
47552     
47553     hasChanged : function()
47554     {
47555         var dirty = false;
47556         this.items.each(function(f){
47557            if(f.hasChanged()){
47558                dirty = true;
47559                return false;
47560            }
47561         });
47562         return dirty;
47563         
47564     },
47565     /**
47566      * Resets all hasChanged to 'false' -
47567      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
47568      * So hasChanged storage is only to be used for this purpose
47569      * @return Boolean
47570      */
47571     resetHasChanged : function()
47572     {
47573         this.items.each(function(f){
47574            f.resetHasChanged();
47575         });
47576         
47577     },
47578     
47579     
47580     /**
47581      * Performs a predefined action (submit or load) or custom actions you define on this form.
47582      * @param {String} actionName The name of the action type
47583      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
47584      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
47585      * accept other config options):
47586      * <pre>
47587 Property          Type             Description
47588 ----------------  ---------------  ----------------------------------------------------------------------------------
47589 url               String           The url for the action (defaults to the form's url)
47590 method            String           The form method to use (defaults to the form's method, or POST if not defined)
47591 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
47592 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
47593                                    validate the form on the client (defaults to false)
47594      * </pre>
47595      * @return {BasicForm} this
47596      */
47597     doAction : function(action, options){
47598         if(typeof action == 'string'){
47599             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
47600         }
47601         if(this.fireEvent('beforeaction', this, action) !== false){
47602             this.beforeAction(action);
47603             action.run.defer(100, action);
47604         }
47605         return this;
47606     },
47607
47608     /**
47609      * Shortcut to do a submit action.
47610      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
47611      * @return {BasicForm} this
47612      */
47613     submit : function(options){
47614         this.doAction('submit', options);
47615         return this;
47616     },
47617
47618     /**
47619      * Shortcut to do a load action.
47620      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
47621      * @return {BasicForm} this
47622      */
47623     load : function(options){
47624         this.doAction('load', options);
47625         return this;
47626     },
47627
47628     /**
47629      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
47630      * @param {Record} record The record to edit
47631      * @return {BasicForm} this
47632      */
47633     updateRecord : function(record){
47634         record.beginEdit();
47635         var fs = record.fields;
47636         fs.each(function(f){
47637             var field = this.findField(f.name);
47638             if(field){
47639                 record.set(f.name, field.getValue());
47640             }
47641         }, this);
47642         record.endEdit();
47643         return this;
47644     },
47645
47646     /**
47647      * Loads an Roo.data.Record into this form.
47648      * @param {Record} record The record to load
47649      * @return {BasicForm} this
47650      */
47651     loadRecord : function(record){
47652         this.setValues(record.data);
47653         return this;
47654     },
47655
47656     // private
47657     beforeAction : function(action){
47658         var o = action.options;
47659         
47660         if(!this.disableMask) {
47661             if(this.waitMsgTarget === true){
47662                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
47663             }else if(this.waitMsgTarget){
47664                 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
47665                 this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
47666             }else {
47667                 Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
47668             }
47669         }
47670         
47671          
47672     },
47673
47674     // private
47675     afterAction : function(action, success){
47676         this.activeAction = null;
47677         var o = action.options;
47678         
47679         if(!this.disableMask) {
47680             if(this.waitMsgTarget === true){
47681                 this.el.unmask();
47682             }else if(this.waitMsgTarget){
47683                 this.waitMsgTarget.unmask();
47684             }else{
47685                 Roo.MessageBox.updateProgress(1);
47686                 Roo.MessageBox.hide();
47687             }
47688         }
47689         
47690         if(success){
47691             if(o.reset){
47692                 this.reset();
47693             }
47694             Roo.callback(o.success, o.scope, [this, action]);
47695             this.fireEvent('actioncomplete', this, action);
47696             
47697         }else{
47698             
47699             // failure condition..
47700             // we have a scenario where updates need confirming.
47701             // eg. if a locking scenario exists..
47702             // we look for { errors : { needs_confirm : true }} in the response.
47703             if (
47704                 (typeof(action.result) != 'undefined')  &&
47705                 (typeof(action.result.errors) != 'undefined')  &&
47706                 (typeof(action.result.errors.needs_confirm) != 'undefined')
47707            ){
47708                 var _t = this;
47709                 Roo.MessageBox.confirm(
47710                     "Change requires confirmation",
47711                     action.result.errorMsg,
47712                     function(r) {
47713                         if (r != 'yes') {
47714                             return;
47715                         }
47716                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
47717                     }
47718                     
47719                 );
47720                 
47721                 
47722                 
47723                 return;
47724             }
47725             
47726             Roo.callback(o.failure, o.scope, [this, action]);
47727             // show an error message if no failed handler is set..
47728             if (!this.hasListener('actionfailed')) {
47729                 Roo.MessageBox.alert("Error",
47730                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
47731                         action.result.errorMsg :
47732                         "Saving Failed, please check your entries or try again"
47733                 );
47734             }
47735             
47736             this.fireEvent('actionfailed', this, action);
47737         }
47738         
47739     },
47740
47741     /**
47742      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
47743      * @param {String} id The value to search for
47744      * @return Field
47745      */
47746     findField : function(id){
47747         var field = this.items.get(id);
47748         if(!field){
47749             this.items.each(function(f){
47750                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
47751                     field = f;
47752                     return false;
47753                 }
47754             });
47755         }
47756         return field || null;
47757     },
47758
47759     /**
47760      * Add a secondary form to this one, 
47761      * Used to provide tabbed forms. One form is primary, with hidden values 
47762      * which mirror the elements from the other forms.
47763      * 
47764      * @param {Roo.form.Form} form to add.
47765      * 
47766      */
47767     addForm : function(form)
47768     {
47769        
47770         if (this.childForms.indexOf(form) > -1) {
47771             // already added..
47772             return;
47773         }
47774         this.childForms.push(form);
47775         var n = '';
47776         Roo.each(form.allItems, function (fe) {
47777             
47778             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
47779             if (this.findField(n)) { // already added..
47780                 return;
47781             }
47782             var add = new Roo.form.Hidden({
47783                 name : n
47784             });
47785             add.render(this.el);
47786             
47787             this.add( add );
47788         }, this);
47789         
47790     },
47791     /**
47792      * Mark fields in this form invalid in bulk.
47793      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
47794      * @return {BasicForm} this
47795      */
47796     markInvalid : function(errors){
47797         if(errors instanceof Array){
47798             for(var i = 0, len = errors.length; i < len; i++){
47799                 var fieldError = errors[i];
47800                 var f = this.findField(fieldError.id);
47801                 if(f){
47802                     f.markInvalid(fieldError.msg);
47803                 }
47804             }
47805         }else{
47806             var field, id;
47807             for(id in errors){
47808                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
47809                     field.markInvalid(errors[id]);
47810                 }
47811             }
47812         }
47813         Roo.each(this.childForms || [], function (f) {
47814             f.markInvalid(errors);
47815         });
47816         
47817         return this;
47818     },
47819
47820     /**
47821      * Set values for fields in this form in bulk.
47822      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
47823      * @return {BasicForm} this
47824      */
47825     setValues : function(values){
47826         if(values instanceof Array){ // array of objects
47827             for(var i = 0, len = values.length; i < len; i++){
47828                 var v = values[i];
47829                 var f = this.findField(v.id);
47830                 if(f){
47831                     f.setValue(v.value);
47832                     if(this.trackResetOnLoad){
47833                         f.originalValue = f.getValue();
47834                     }
47835                 }
47836             }
47837         }else{ // object hash
47838             var field, id;
47839             for(id in values){
47840                 if(typeof values[id] != 'function' && (field = this.findField(id))){
47841                     
47842                     if (field.setFromData && 
47843                         field.valueField && 
47844                         field.displayField &&
47845                         // combos' with local stores can 
47846                         // be queried via setValue()
47847                         // to set their value..
47848                         (field.store && !field.store.isLocal)
47849                         ) {
47850                         // it's a combo
47851                         var sd = { };
47852                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
47853                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
47854                         field.setFromData(sd);
47855                         
47856                     } else {
47857                         field.setValue(values[id]);
47858                     }
47859                     
47860                     
47861                     if(this.trackResetOnLoad){
47862                         field.originalValue = field.getValue();
47863                     }
47864                 }
47865             }
47866         }
47867         this.resetHasChanged();
47868         
47869         
47870         Roo.each(this.childForms || [], function (f) {
47871             f.setValues(values);
47872             f.resetHasChanged();
47873         });
47874                 
47875         return this;
47876     },
47877  
47878     /**
47879      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
47880      * they are returned as an array.
47881      * @param {Boolean} asString
47882      * @return {Object}
47883      */
47884     getValues : function(asString){
47885         if (this.childForms) {
47886             // copy values from the child forms
47887             Roo.each(this.childForms, function (f) {
47888                 this.setValues(f.getValues());
47889             }, this);
47890         }
47891         
47892         // use formdata
47893         if (typeof(FormData) != 'undefined' && asString !== true) {
47894             // this relies on a 'recent' version of chrome apparently...
47895             try {
47896                 var fd = (new FormData(this.el.dom)).entries();
47897                 var ret = {};
47898                 var ent = fd.next();
47899                 while (!ent.done) {
47900                     ret[ent.value[0]] = ent.value[1]; // not sure how this will handle duplicates..
47901                     ent = fd.next();
47902                 };
47903                 return ret;
47904             } catch(e) {
47905                 
47906             }
47907             
47908         }
47909         
47910         
47911         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
47912         if(asString === true){
47913             return fs;
47914         }
47915         return Roo.urlDecode(fs);
47916     },
47917     
47918     /**
47919      * Returns the fields in this form as an object with key/value pairs. 
47920      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
47921      * @return {Object}
47922      */
47923     getFieldValues : function(with_hidden)
47924     {
47925         if (this.childForms) {
47926             // copy values from the child forms
47927             // should this call getFieldValues - probably not as we do not currently copy
47928             // hidden fields when we generate..
47929             Roo.each(this.childForms, function (f) {
47930                 this.setValues(f.getValues());
47931             }, this);
47932         }
47933         
47934         var ret = {};
47935         this.items.each(function(f){
47936             if (!f.getName()) {
47937                 return;
47938             }
47939             var v = f.getValue();
47940             if (f.inputType =='radio') {
47941                 if (typeof(ret[f.getName()]) == 'undefined') {
47942                     ret[f.getName()] = ''; // empty..
47943                 }
47944                 
47945                 if (!f.el.dom.checked) {
47946                     return;
47947                     
47948                 }
47949                 v = f.el.dom.value;
47950                 
47951             }
47952             
47953             // not sure if this supported any more..
47954             if ((typeof(v) == 'object') && f.getRawValue) {
47955                 v = f.getRawValue() ; // dates..
47956             }
47957             // combo boxes where name != hiddenName...
47958             if (f.name != f.getName()) {
47959                 ret[f.name] = f.getRawValue();
47960             }
47961             ret[f.getName()] = v;
47962         });
47963         
47964         return ret;
47965     },
47966
47967     /**
47968      * Clears all invalid messages in this form.
47969      * @return {BasicForm} this
47970      */
47971     clearInvalid : function(){
47972         this.items.each(function(f){
47973            f.clearInvalid();
47974         });
47975         
47976         Roo.each(this.childForms || [], function (f) {
47977             f.clearInvalid();
47978         });
47979         
47980         
47981         return this;
47982     },
47983
47984     /**
47985      * Resets this form.
47986      * @return {BasicForm} this
47987      */
47988     reset : function(){
47989         this.items.each(function(f){
47990             f.reset();
47991         });
47992         
47993         Roo.each(this.childForms || [], function (f) {
47994             f.reset();
47995         });
47996         this.resetHasChanged();
47997         
47998         return this;
47999     },
48000
48001     /**
48002      * Add Roo.form components to this form.
48003      * @param {Field} field1
48004      * @param {Field} field2 (optional)
48005      * @param {Field} etc (optional)
48006      * @return {BasicForm} this
48007      */
48008     add : function(){
48009         this.items.addAll(Array.prototype.slice.call(arguments, 0));
48010         return this;
48011     },
48012
48013
48014     /**
48015      * Removes a field from the items collection (does NOT remove its markup).
48016      * @param {Field} field
48017      * @return {BasicForm} this
48018      */
48019     remove : function(field){
48020         this.items.remove(field);
48021         return this;
48022     },
48023
48024     /**
48025      * Looks at the fields in this form, checks them for an id attribute,
48026      * and calls applyTo on the existing dom element with that id.
48027      * @return {BasicForm} this
48028      */
48029     render : function(){
48030         this.items.each(function(f){
48031             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
48032                 f.applyTo(f.id);
48033             }
48034         });
48035         return this;
48036     },
48037
48038     /**
48039      * Calls {@link Ext#apply} for all fields in this form with the passed object.
48040      * @param {Object} values
48041      * @return {BasicForm} this
48042      */
48043     applyToFields : function(o){
48044         this.items.each(function(f){
48045            Roo.apply(f, o);
48046         });
48047         return this;
48048     },
48049
48050     /**
48051      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
48052      * @param {Object} values
48053      * @return {BasicForm} this
48054      */
48055     applyIfToFields : function(o){
48056         this.items.each(function(f){
48057            Roo.applyIf(f, o);
48058         });
48059         return this;
48060     }
48061 });
48062
48063 // back compat
48064 Roo.BasicForm = Roo.form.BasicForm;
48065
48066 Roo.apply(Roo.form.BasicForm, {
48067     
48068     popover : {
48069         
48070         padding : 5,
48071         
48072         isApplied : false,
48073         
48074         isMasked : false,
48075         
48076         form : false,
48077         
48078         target : false,
48079         
48080         intervalID : false,
48081         
48082         maskEl : false,
48083         
48084         apply : function()
48085         {
48086             if(this.isApplied){
48087                 return;
48088             }
48089             
48090             this.maskEl = {
48091                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
48092                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
48093                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
48094                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
48095             };
48096             
48097             this.maskEl.top.enableDisplayMode("block");
48098             this.maskEl.left.enableDisplayMode("block");
48099             this.maskEl.bottom.enableDisplayMode("block");
48100             this.maskEl.right.enableDisplayMode("block");
48101             
48102             Roo.get(document.body).on('click', function(){
48103                 this.unmask();
48104             }, this);
48105             
48106             Roo.get(document.body).on('touchstart', function(){
48107                 this.unmask();
48108             }, this);
48109             
48110             this.isApplied = true
48111         },
48112         
48113         mask : function(form, target)
48114         {
48115             this.form = form;
48116             
48117             this.target = target;
48118             
48119             if(!this.form.errorMask || !target.el){
48120                 return;
48121             }
48122             
48123             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.x-layout-active-content', 100, true) || Roo.get(document.body);
48124             
48125             var ot = this.target.el.calcOffsetsTo(scrollable);
48126             
48127             var scrollTo = ot[1] - this.form.maskOffset;
48128             
48129             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
48130             
48131             scrollable.scrollTo('top', scrollTo);
48132             
48133             var el = this.target.wrap || this.target.el;
48134             
48135             var box = el.getBox();
48136             
48137             this.maskEl.top.setStyle('position', 'absolute');
48138             this.maskEl.top.setStyle('z-index', 10000);
48139             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
48140             this.maskEl.top.setLeft(0);
48141             this.maskEl.top.setTop(0);
48142             this.maskEl.top.show();
48143             
48144             this.maskEl.left.setStyle('position', 'absolute');
48145             this.maskEl.left.setStyle('z-index', 10000);
48146             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
48147             this.maskEl.left.setLeft(0);
48148             this.maskEl.left.setTop(box.y - this.padding);
48149             this.maskEl.left.show();
48150
48151             this.maskEl.bottom.setStyle('position', 'absolute');
48152             this.maskEl.bottom.setStyle('z-index', 10000);
48153             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
48154             this.maskEl.bottom.setLeft(0);
48155             this.maskEl.bottom.setTop(box.bottom + this.padding);
48156             this.maskEl.bottom.show();
48157
48158             this.maskEl.right.setStyle('position', 'absolute');
48159             this.maskEl.right.setStyle('z-index', 10000);
48160             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
48161             this.maskEl.right.setLeft(box.right + this.padding);
48162             this.maskEl.right.setTop(box.y - this.padding);
48163             this.maskEl.right.show();
48164
48165             this.intervalID = window.setInterval(function() {
48166                 Roo.form.BasicForm.popover.unmask();
48167             }, 10000);
48168
48169             window.onwheel = function(){ return false;};
48170             
48171             (function(){ this.isMasked = true; }).defer(500, this);
48172             
48173         },
48174         
48175         unmask : function()
48176         {
48177             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
48178                 return;
48179             }
48180             
48181             this.maskEl.top.setStyle('position', 'absolute');
48182             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
48183             this.maskEl.top.hide();
48184
48185             this.maskEl.left.setStyle('position', 'absolute');
48186             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
48187             this.maskEl.left.hide();
48188
48189             this.maskEl.bottom.setStyle('position', 'absolute');
48190             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
48191             this.maskEl.bottom.hide();
48192
48193             this.maskEl.right.setStyle('position', 'absolute');
48194             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
48195             this.maskEl.right.hide();
48196             
48197             window.onwheel = function(){ return true;};
48198             
48199             if(this.intervalID){
48200                 window.clearInterval(this.intervalID);
48201                 this.intervalID = false;
48202             }
48203             
48204             this.isMasked = false;
48205             
48206         }
48207         
48208     }
48209     
48210 });/*
48211  * Based on:
48212  * Ext JS Library 1.1.1
48213  * Copyright(c) 2006-2007, Ext JS, LLC.
48214  *
48215  * Originally Released Under LGPL - original licence link has changed is not relivant.
48216  *
48217  * Fork - LGPL
48218  * <script type="text/javascript">
48219  */
48220
48221 /**
48222  * @class Roo.form.Form
48223  * @extends Roo.form.BasicForm
48224  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
48225  * @constructor
48226  * @param {Object} config Configuration options
48227  */
48228 Roo.form.Form = function(config){
48229     var xitems =  [];
48230     if (config.items) {
48231         xitems = config.items;
48232         delete config.items;
48233     }
48234    
48235     
48236     Roo.form.Form.superclass.constructor.call(this, null, config);
48237     this.url = this.url || this.action;
48238     if(!this.root){
48239         this.root = new Roo.form.Layout(Roo.applyIf({
48240             id: Roo.id()
48241         }, config));
48242     }
48243     this.active = this.root;
48244     /**
48245      * Array of all the buttons that have been added to this form via {@link addButton}
48246      * @type Array
48247      */
48248     this.buttons = [];
48249     this.allItems = [];
48250     this.addEvents({
48251         /**
48252          * @event clientvalidation
48253          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
48254          * @param {Form} this
48255          * @param {Boolean} valid true if the form has passed client-side validation
48256          */
48257         clientvalidation: true,
48258         /**
48259          * @event rendered
48260          * Fires when the form is rendered
48261          * @param {Roo.form.Form} form
48262          */
48263         rendered : true
48264     });
48265     
48266     if (this.progressUrl) {
48267             // push a hidden field onto the list of fields..
48268             this.addxtype( {
48269                     xns: Roo.form, 
48270                     xtype : 'Hidden', 
48271                     name : 'UPLOAD_IDENTIFIER' 
48272             });
48273         }
48274         
48275     
48276     Roo.each(xitems, this.addxtype, this);
48277     
48278 };
48279
48280 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
48281     /**
48282      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
48283      */
48284     /**
48285      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
48286      */
48287     /**
48288      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
48289      */
48290     buttonAlign:'center',
48291
48292     /**
48293      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
48294      */
48295     minButtonWidth:75,
48296
48297     /**
48298      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
48299      * This property cascades to child containers if not set.
48300      */
48301     labelAlign:'left',
48302
48303     /**
48304      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
48305      * fires a looping event with that state. This is required to bind buttons to the valid
48306      * state using the config value formBind:true on the button.
48307      */
48308     monitorValid : false,
48309
48310     /**
48311      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
48312      */
48313     monitorPoll : 200,
48314     
48315     /**
48316      * @cfg {String} progressUrl - Url to return progress data 
48317      */
48318     
48319     progressUrl : false,
48320     /**
48321      * @cfg {boolean|FormData} formData - true to use new 'FormData' post, or set to a new FormData({dom form}) Object, if
48322      * sending a formdata with extra parameters - eg uploaded elements.
48323      */
48324     
48325     formData : false,
48326     
48327     /**
48328      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
48329      * fields are added and the column is closed. If no fields are passed the column remains open
48330      * until end() is called.
48331      * @param {Object} config The config to pass to the column
48332      * @param {Field} field1 (optional)
48333      * @param {Field} field2 (optional)
48334      * @param {Field} etc (optional)
48335      * @return Column The column container object
48336      */
48337     column : function(c){
48338         var col = new Roo.form.Column(c);
48339         this.start(col);
48340         if(arguments.length > 1){ // duplicate code required because of Opera
48341             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
48342             this.end();
48343         }
48344         return col;
48345     },
48346
48347     /**
48348      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
48349      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
48350      * until end() is called.
48351      * @param {Object} config The config to pass to the fieldset
48352      * @param {Field} field1 (optional)
48353      * @param {Field} field2 (optional)
48354      * @param {Field} etc (optional)
48355      * @return FieldSet The fieldset container object
48356      */
48357     fieldset : function(c){
48358         var fs = new Roo.form.FieldSet(c);
48359         this.start(fs);
48360         if(arguments.length > 1){ // duplicate code required because of Opera
48361             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
48362             this.end();
48363         }
48364         return fs;
48365     },
48366
48367     /**
48368      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
48369      * fields are added and the container is closed. If no fields are passed the container remains open
48370      * until end() is called.
48371      * @param {Object} config The config to pass to the Layout
48372      * @param {Field} field1 (optional)
48373      * @param {Field} field2 (optional)
48374      * @param {Field} etc (optional)
48375      * @return Layout The container object
48376      */
48377     container : function(c){
48378         var l = new Roo.form.Layout(c);
48379         this.start(l);
48380         if(arguments.length > 1){ // duplicate code required because of Opera
48381             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
48382             this.end();
48383         }
48384         return l;
48385     },
48386
48387     /**
48388      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
48389      * @param {Object} container A Roo.form.Layout or subclass of Layout
48390      * @return {Form} this
48391      */
48392     start : function(c){
48393         // cascade label info
48394         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
48395         this.active.stack.push(c);
48396         c.ownerCt = this.active;
48397         this.active = c;
48398         return this;
48399     },
48400
48401     /**
48402      * Closes the current open container
48403      * @return {Form} this
48404      */
48405     end : function(){
48406         if(this.active == this.root){
48407             return this;
48408         }
48409         this.active = this.active.ownerCt;
48410         return this;
48411     },
48412
48413     /**
48414      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
48415      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
48416      * as the label of the field.
48417      * @param {Field} field1
48418      * @param {Field} field2 (optional)
48419      * @param {Field} etc. (optional)
48420      * @return {Form} this
48421      */
48422     add : function(){
48423         this.active.stack.push.apply(this.active.stack, arguments);
48424         this.allItems.push.apply(this.allItems,arguments);
48425         var r = [];
48426         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
48427             if(a[i].isFormField){
48428                 r.push(a[i]);
48429             }
48430         }
48431         if(r.length > 0){
48432             Roo.form.Form.superclass.add.apply(this, r);
48433         }
48434         return this;
48435     },
48436     
48437
48438     
48439     
48440     
48441      /**
48442      * Find any element that has been added to a form, using it's ID or name
48443      * This can include framesets, columns etc. along with regular fields..
48444      * @param {String} id - id or name to find.
48445      
48446      * @return {Element} e - or false if nothing found.
48447      */
48448     findbyId : function(id)
48449     {
48450         var ret = false;
48451         if (!id) {
48452             return ret;
48453         }
48454         Roo.each(this.allItems, function(f){
48455             if (f.id == id || f.name == id ){
48456                 ret = f;
48457                 return false;
48458             }
48459         });
48460         return ret;
48461     },
48462
48463     
48464     
48465     /**
48466      * Render this form into the passed container. This should only be called once!
48467      * @param {String/HTMLElement/Element} container The element this component should be rendered into
48468      * @return {Form} this
48469      */
48470     render : function(ct)
48471     {
48472         
48473         
48474         
48475         ct = Roo.get(ct);
48476         var o = this.autoCreate || {
48477             tag: 'form',
48478             method : this.method || 'POST',
48479             id : this.id || Roo.id()
48480         };
48481         this.initEl(ct.createChild(o));
48482
48483         this.root.render(this.el);
48484         
48485        
48486              
48487         this.items.each(function(f){
48488             f.render('x-form-el-'+f.id);
48489         });
48490
48491         if(this.buttons.length > 0){
48492             // tables are required to maintain order and for correct IE layout
48493             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
48494                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
48495                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
48496             }}, null, true);
48497             var tr = tb.getElementsByTagName('tr')[0];
48498             for(var i = 0, len = this.buttons.length; i < len; i++) {
48499                 var b = this.buttons[i];
48500                 var td = document.createElement('td');
48501                 td.className = 'x-form-btn-td';
48502                 b.render(tr.appendChild(td));
48503             }
48504         }
48505         if(this.monitorValid){ // initialize after render
48506             this.startMonitoring();
48507         }
48508         this.fireEvent('rendered', this);
48509         return this;
48510     },
48511
48512     /**
48513      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
48514      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
48515      * object or a valid Roo.DomHelper element config
48516      * @param {Function} handler The function called when the button is clicked
48517      * @param {Object} scope (optional) The scope of the handler function
48518      * @return {Roo.Button}
48519      */
48520     addButton : function(config, handler, scope){
48521         var bc = {
48522             handler: handler,
48523             scope: scope,
48524             minWidth: this.minButtonWidth,
48525             hideParent:true
48526         };
48527         if(typeof config == "string"){
48528             bc.text = config;
48529         }else{
48530             Roo.apply(bc, config);
48531         }
48532         var btn = new Roo.Button(null, bc);
48533         this.buttons.push(btn);
48534         return btn;
48535     },
48536
48537      /**
48538      * Adds a series of form elements (using the xtype property as the factory method.
48539      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
48540      * @param {Object} config 
48541      */
48542     
48543     addxtype : function()
48544     {
48545         var ar = Array.prototype.slice.call(arguments, 0);
48546         var ret = false;
48547         for(var i = 0; i < ar.length; i++) {
48548             if (!ar[i]) {
48549                 continue; // skip -- if this happends something invalid got sent, we 
48550                 // should ignore it, as basically that interface element will not show up
48551                 // and that should be pretty obvious!!
48552             }
48553             
48554             if (Roo.form[ar[i].xtype]) {
48555                 ar[i].form = this;
48556                 var fe = Roo.factory(ar[i], Roo.form);
48557                 if (!ret) {
48558                     ret = fe;
48559                 }
48560                 fe.form = this;
48561                 if (fe.store) {
48562                     fe.store.form = this;
48563                 }
48564                 if (fe.isLayout) {  
48565                          
48566                     this.start(fe);
48567                     this.allItems.push(fe);
48568                     if (fe.items && fe.addxtype) {
48569                         fe.addxtype.apply(fe, fe.items);
48570                         delete fe.items;
48571                     }
48572                      this.end();
48573                     continue;
48574                 }
48575                 
48576                 
48577                  
48578                 this.add(fe);
48579               //  console.log('adding ' + ar[i].xtype);
48580             }
48581             if (ar[i].xtype == 'Button') {  
48582                 //console.log('adding button');
48583                 //console.log(ar[i]);
48584                 this.addButton(ar[i]);
48585                 this.allItems.push(fe);
48586                 continue;
48587             }
48588             
48589             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
48590                 alert('end is not supported on xtype any more, use items');
48591             //    this.end();
48592             //    //console.log('adding end');
48593             }
48594             
48595         }
48596         return ret;
48597     },
48598     
48599     /**
48600      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
48601      * option "monitorValid"
48602      */
48603     startMonitoring : function(){
48604         if(!this.bound){
48605             this.bound = true;
48606             Roo.TaskMgr.start({
48607                 run : this.bindHandler,
48608                 interval : this.monitorPoll || 200,
48609                 scope: this
48610             });
48611         }
48612     },
48613
48614     /**
48615      * Stops monitoring of the valid state of this form
48616      */
48617     stopMonitoring : function(){
48618         this.bound = false;
48619     },
48620
48621     // private
48622     bindHandler : function(){
48623         if(!this.bound){
48624             return false; // stops binding
48625         }
48626         var valid = true;
48627         this.items.each(function(f){
48628             if(!f.isValid(true)){
48629                 valid = false;
48630                 return false;
48631             }
48632         });
48633         for(var i = 0, len = this.buttons.length; i < len; i++){
48634             var btn = this.buttons[i];
48635             if(btn.formBind === true && btn.disabled === valid){
48636                 btn.setDisabled(!valid);
48637             }
48638         }
48639         this.fireEvent('clientvalidation', this, valid);
48640     }
48641     
48642     
48643     
48644     
48645     
48646     
48647     
48648     
48649 });
48650
48651
48652 // back compat
48653 Roo.Form = Roo.form.Form;
48654 /*
48655  * Based on:
48656  * Ext JS Library 1.1.1
48657  * Copyright(c) 2006-2007, Ext JS, LLC.
48658  *
48659  * Originally Released Under LGPL - original licence link has changed is not relivant.
48660  *
48661  * Fork - LGPL
48662  * <script type="text/javascript">
48663  */
48664
48665 // as we use this in bootstrap.
48666 Roo.namespace('Roo.form');
48667  /**
48668  * @class Roo.form.Action
48669  * Internal Class used to handle form actions
48670  * @constructor
48671  * @param {Roo.form.BasicForm} el The form element or its id
48672  * @param {Object} config Configuration options
48673  */
48674
48675  
48676  
48677 // define the action interface
48678 Roo.form.Action = function(form, options){
48679     this.form = form;
48680     this.options = options || {};
48681 };
48682 /**
48683  * Client Validation Failed
48684  * @const 
48685  */
48686 Roo.form.Action.CLIENT_INVALID = 'client';
48687 /**
48688  * Server Validation Failed
48689  * @const 
48690  */
48691 Roo.form.Action.SERVER_INVALID = 'server';
48692  /**
48693  * Connect to Server Failed
48694  * @const 
48695  */
48696 Roo.form.Action.CONNECT_FAILURE = 'connect';
48697 /**
48698  * Reading Data from Server Failed
48699  * @const 
48700  */
48701 Roo.form.Action.LOAD_FAILURE = 'load';
48702
48703 Roo.form.Action.prototype = {
48704     type : 'default',
48705     failureType : undefined,
48706     response : undefined,
48707     result : undefined,
48708
48709     // interface method
48710     run : function(options){
48711
48712     },
48713
48714     // interface method
48715     success : function(response){
48716
48717     },
48718
48719     // interface method
48720     handleResponse : function(response){
48721
48722     },
48723
48724     // default connection failure
48725     failure : function(response){
48726         
48727         this.response = response;
48728         this.failureType = Roo.form.Action.CONNECT_FAILURE;
48729         this.form.afterAction(this, false);
48730     },
48731
48732     processResponse : function(response){
48733         this.response = response;
48734         if(!response.responseText){
48735             return true;
48736         }
48737         this.result = this.handleResponse(response);
48738         return this.result;
48739     },
48740
48741     // utility functions used internally
48742     getUrl : function(appendParams){
48743         var url = this.options.url || this.form.url || this.form.el.dom.action;
48744         if(appendParams){
48745             var p = this.getParams();
48746             if(p){
48747                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
48748             }
48749         }
48750         return url;
48751     },
48752
48753     getMethod : function(){
48754         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
48755     },
48756
48757     getParams : function(){
48758         var bp = this.form.baseParams;
48759         var p = this.options.params;
48760         if(p){
48761             if(typeof p == "object"){
48762                 p = Roo.urlEncode(Roo.applyIf(p, bp));
48763             }else if(typeof p == 'string' && bp){
48764                 p += '&' + Roo.urlEncode(bp);
48765             }
48766         }else if(bp){
48767             p = Roo.urlEncode(bp);
48768         }
48769         return p;
48770     },
48771
48772     createCallback : function(){
48773         return {
48774             success: this.success,
48775             failure: this.failure,
48776             scope: this,
48777             timeout: (this.form.timeout*1000),
48778             upload: this.form.fileUpload ? this.success : undefined
48779         };
48780     }
48781 };
48782
48783 Roo.form.Action.Submit = function(form, options){
48784     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
48785 };
48786
48787 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
48788     type : 'submit',
48789
48790     haveProgress : false,
48791     uploadComplete : false,
48792     
48793     // uploadProgress indicator.
48794     uploadProgress : function()
48795     {
48796         if (!this.form.progressUrl) {
48797             return;
48798         }
48799         
48800         if (!this.haveProgress) {
48801             Roo.MessageBox.progress("Uploading", "Uploading");
48802         }
48803         if (this.uploadComplete) {
48804            Roo.MessageBox.hide();
48805            return;
48806         }
48807         
48808         this.haveProgress = true;
48809    
48810         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
48811         
48812         var c = new Roo.data.Connection();
48813         c.request({
48814             url : this.form.progressUrl,
48815             params: {
48816                 id : uid
48817             },
48818             method: 'GET',
48819             success : function(req){
48820                //console.log(data);
48821                 var rdata = false;
48822                 var edata;
48823                 try  {
48824                    rdata = Roo.decode(req.responseText)
48825                 } catch (e) {
48826                     Roo.log("Invalid data from server..");
48827                     Roo.log(edata);
48828                     return;
48829                 }
48830                 if (!rdata || !rdata.success) {
48831                     Roo.log(rdata);
48832                     Roo.MessageBox.alert(Roo.encode(rdata));
48833                     return;
48834                 }
48835                 var data = rdata.data;
48836                 
48837                 if (this.uploadComplete) {
48838                    Roo.MessageBox.hide();
48839                    return;
48840                 }
48841                    
48842                 if (data){
48843                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
48844                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
48845                     );
48846                 }
48847                 this.uploadProgress.defer(2000,this);
48848             },
48849        
48850             failure: function(data) {
48851                 Roo.log('progress url failed ');
48852                 Roo.log(data);
48853             },
48854             scope : this
48855         });
48856            
48857     },
48858     
48859     
48860     run : function()
48861     {
48862         // run get Values on the form, so it syncs any secondary forms.
48863         this.form.getValues();
48864         
48865         var o = this.options;
48866         var method = this.getMethod();
48867         var isPost = method == 'POST';
48868         if(o.clientValidation === false || this.form.isValid()){
48869             
48870             if (this.form.progressUrl) {
48871                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
48872                     (new Date() * 1) + '' + Math.random());
48873                     
48874             } 
48875             
48876             
48877             Roo.Ajax.request(Roo.apply(this.createCallback(), {
48878                 form:this.form.el.dom,
48879                 url:this.getUrl(!isPost),
48880                 method: method,
48881                 params:isPost ? this.getParams() : null,
48882                 isUpload: this.form.fileUpload,
48883                 formData : this.form.formData
48884             }));
48885             
48886             this.uploadProgress();
48887
48888         }else if (o.clientValidation !== false){ // client validation failed
48889             this.failureType = Roo.form.Action.CLIENT_INVALID;
48890             this.form.afterAction(this, false);
48891         }
48892     },
48893
48894     success : function(response)
48895     {
48896         this.uploadComplete= true;
48897         if (this.haveProgress) {
48898             Roo.MessageBox.hide();
48899         }
48900         
48901         
48902         var result = this.processResponse(response);
48903         if(result === true || result.success){
48904             this.form.afterAction(this, true);
48905             return;
48906         }
48907         if(result.errors){
48908             this.form.markInvalid(result.errors);
48909             this.failureType = Roo.form.Action.SERVER_INVALID;
48910         }
48911         this.form.afterAction(this, false);
48912     },
48913     failure : function(response)
48914     {
48915         this.uploadComplete= true;
48916         if (this.haveProgress) {
48917             Roo.MessageBox.hide();
48918         }
48919         
48920         this.response = response;
48921         this.failureType = Roo.form.Action.CONNECT_FAILURE;
48922         this.form.afterAction(this, false);
48923     },
48924     
48925     handleResponse : function(response){
48926         if(this.form.errorReader){
48927             var rs = this.form.errorReader.read(response);
48928             var errors = [];
48929             if(rs.records){
48930                 for(var i = 0, len = rs.records.length; i < len; i++) {
48931                     var r = rs.records[i];
48932                     errors[i] = r.data;
48933                 }
48934             }
48935             if(errors.length < 1){
48936                 errors = null;
48937             }
48938             return {
48939                 success : rs.success,
48940                 errors : errors
48941             };
48942         }
48943         var ret = false;
48944         try {
48945             ret = Roo.decode(response.responseText);
48946         } catch (e) {
48947             ret = {
48948                 success: false,
48949                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
48950                 errors : []
48951             };
48952         }
48953         return ret;
48954         
48955     }
48956 });
48957
48958
48959 Roo.form.Action.Load = function(form, options){
48960     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
48961     this.reader = this.form.reader;
48962 };
48963
48964 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
48965     type : 'load',
48966
48967     run : function(){
48968         
48969         Roo.Ajax.request(Roo.apply(
48970                 this.createCallback(), {
48971                     method:this.getMethod(),
48972                     url:this.getUrl(false),
48973                     params:this.getParams()
48974         }));
48975     },
48976
48977     success : function(response){
48978         
48979         var result = this.processResponse(response);
48980         if(result === true || !result.success || !result.data){
48981             this.failureType = Roo.form.Action.LOAD_FAILURE;
48982             this.form.afterAction(this, false);
48983             return;
48984         }
48985         this.form.clearInvalid();
48986         this.form.setValues(result.data);
48987         this.form.afterAction(this, true);
48988     },
48989
48990     handleResponse : function(response){
48991         if(this.form.reader){
48992             var rs = this.form.reader.read(response);
48993             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
48994             return {
48995                 success : rs.success,
48996                 data : data
48997             };
48998         }
48999         return Roo.decode(response.responseText);
49000     }
49001 });
49002
49003 Roo.form.Action.ACTION_TYPES = {
49004     'load' : Roo.form.Action.Load,
49005     'submit' : Roo.form.Action.Submit
49006 };/*
49007  * Based on:
49008  * Ext JS Library 1.1.1
49009  * Copyright(c) 2006-2007, Ext JS, LLC.
49010  *
49011  * Originally Released Under LGPL - original licence link has changed is not relivant.
49012  *
49013  * Fork - LGPL
49014  * <script type="text/javascript">
49015  */
49016  
49017 /**
49018  * @class Roo.form.Layout
49019  * @extends Roo.Component
49020  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
49021  * @constructor
49022  * @param {Object} config Configuration options
49023  */
49024 Roo.form.Layout = function(config){
49025     var xitems = [];
49026     if (config.items) {
49027         xitems = config.items;
49028         delete config.items;
49029     }
49030     Roo.form.Layout.superclass.constructor.call(this, config);
49031     this.stack = [];
49032     Roo.each(xitems, this.addxtype, this);
49033      
49034 };
49035
49036 Roo.extend(Roo.form.Layout, Roo.Component, {
49037     /**
49038      * @cfg {String/Object} autoCreate
49039      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
49040      */
49041     /**
49042      * @cfg {String/Object/Function} style
49043      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
49044      * a function which returns such a specification.
49045      */
49046     /**
49047      * @cfg {String} labelAlign
49048      * Valid values are "left," "top" and "right" (defaults to "left")
49049      */
49050     /**
49051      * @cfg {Number} labelWidth
49052      * Fixed width in pixels of all field labels (defaults to undefined)
49053      */
49054     /**
49055      * @cfg {Boolean} clear
49056      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
49057      */
49058     clear : true,
49059     /**
49060      * @cfg {String} labelSeparator
49061      * The separator to use after field labels (defaults to ':')
49062      */
49063     labelSeparator : ':',
49064     /**
49065      * @cfg {Boolean} hideLabels
49066      * True to suppress the display of field labels in this layout (defaults to false)
49067      */
49068     hideLabels : false,
49069
49070     // private
49071     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
49072     
49073     isLayout : true,
49074     
49075     // private
49076     onRender : function(ct, position){
49077         if(this.el){ // from markup
49078             this.el = Roo.get(this.el);
49079         }else {  // generate
49080             var cfg = this.getAutoCreate();
49081             this.el = ct.createChild(cfg, position);
49082         }
49083         if(this.style){
49084             this.el.applyStyles(this.style);
49085         }
49086         if(this.labelAlign){
49087             this.el.addClass('x-form-label-'+this.labelAlign);
49088         }
49089         if(this.hideLabels){
49090             this.labelStyle = "display:none";
49091             this.elementStyle = "padding-left:0;";
49092         }else{
49093             if(typeof this.labelWidth == 'number'){
49094                 this.labelStyle = "width:"+this.labelWidth+"px;";
49095                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
49096             }
49097             if(this.labelAlign == 'top'){
49098                 this.labelStyle = "width:auto;";
49099                 this.elementStyle = "padding-left:0;";
49100             }
49101         }
49102         var stack = this.stack;
49103         var slen = stack.length;
49104         if(slen > 0){
49105             if(!this.fieldTpl){
49106                 var t = new Roo.Template(
49107                     '<div class="x-form-item {5}">',
49108                         '<label for="{0}" style="{2}">{1}{4}</label>',
49109                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
49110                         '</div>',
49111                     '</div><div class="x-form-clear-left"></div>'
49112                 );
49113                 t.disableFormats = true;
49114                 t.compile();
49115                 Roo.form.Layout.prototype.fieldTpl = t;
49116             }
49117             for(var i = 0; i < slen; i++) {
49118                 if(stack[i].isFormField){
49119                     this.renderField(stack[i]);
49120                 }else{
49121                     this.renderComponent(stack[i]);
49122                 }
49123             }
49124         }
49125         if(this.clear){
49126             this.el.createChild({cls:'x-form-clear'});
49127         }
49128     },
49129
49130     // private
49131     renderField : function(f){
49132         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
49133                f.id, //0
49134                f.fieldLabel, //1
49135                f.labelStyle||this.labelStyle||'', //2
49136                this.elementStyle||'', //3
49137                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
49138                f.itemCls||this.itemCls||''  //5
49139        ], true).getPrevSibling());
49140     },
49141
49142     // private
49143     renderComponent : function(c){
49144         c.render(c.isLayout ? this.el : this.el.createChild());    
49145     },
49146     /**
49147      * Adds a object form elements (using the xtype property as the factory method.)
49148      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
49149      * @param {Object} config 
49150      */
49151     addxtype : function(o)
49152     {
49153         // create the lement.
49154         o.form = this.form;
49155         var fe = Roo.factory(o, Roo.form);
49156         this.form.allItems.push(fe);
49157         this.stack.push(fe);
49158         
49159         if (fe.isFormField) {
49160             this.form.items.add(fe);
49161         }
49162          
49163         return fe;
49164     }
49165 });
49166
49167 /**
49168  * @class Roo.form.Column
49169  * @extends Roo.form.Layout
49170  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
49171  * @constructor
49172  * @param {Object} config Configuration options
49173  */
49174 Roo.form.Column = function(config){
49175     Roo.form.Column.superclass.constructor.call(this, config);
49176 };
49177
49178 Roo.extend(Roo.form.Column, Roo.form.Layout, {
49179     /**
49180      * @cfg {Number/String} width
49181      * The fixed width of the column in pixels or CSS value (defaults to "auto")
49182      */
49183     /**
49184      * @cfg {String/Object} autoCreate
49185      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
49186      */
49187
49188     // private
49189     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
49190
49191     // private
49192     onRender : function(ct, position){
49193         Roo.form.Column.superclass.onRender.call(this, ct, position);
49194         if(this.width){
49195             this.el.setWidth(this.width);
49196         }
49197     }
49198 });
49199
49200
49201 /**
49202  * @class Roo.form.Row
49203  * @extends Roo.form.Layout
49204  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
49205  * @constructor
49206  * @param {Object} config Configuration options
49207  */
49208
49209  
49210 Roo.form.Row = function(config){
49211     Roo.form.Row.superclass.constructor.call(this, config);
49212 };
49213  
49214 Roo.extend(Roo.form.Row, Roo.form.Layout, {
49215       /**
49216      * @cfg {Number/String} width
49217      * The fixed width of the column in pixels or CSS value (defaults to "auto")
49218      */
49219     /**
49220      * @cfg {Number/String} height
49221      * The fixed height of the column in pixels or CSS value (defaults to "auto")
49222      */
49223     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
49224     
49225     padWidth : 20,
49226     // private
49227     onRender : function(ct, position){
49228         //console.log('row render');
49229         if(!this.rowTpl){
49230             var t = new Roo.Template(
49231                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
49232                     '<label for="{0}" style="{2}">{1}{4}</label>',
49233                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
49234                     '</div>',
49235                 '</div>'
49236             );
49237             t.disableFormats = true;
49238             t.compile();
49239             Roo.form.Layout.prototype.rowTpl = t;
49240         }
49241         this.fieldTpl = this.rowTpl;
49242         
49243         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
49244         var labelWidth = 100;
49245         
49246         if ((this.labelAlign != 'top')) {
49247             if (typeof this.labelWidth == 'number') {
49248                 labelWidth = this.labelWidth
49249             }
49250             this.padWidth =  20 + labelWidth;
49251             
49252         }
49253         
49254         Roo.form.Column.superclass.onRender.call(this, ct, position);
49255         if(this.width){
49256             this.el.setWidth(this.width);
49257         }
49258         if(this.height){
49259             this.el.setHeight(this.height);
49260         }
49261     },
49262     
49263     // private
49264     renderField : function(f){
49265         f.fieldEl = this.fieldTpl.append(this.el, [
49266                f.id, f.fieldLabel,
49267                f.labelStyle||this.labelStyle||'',
49268                this.elementStyle||'',
49269                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
49270                f.itemCls||this.itemCls||'',
49271                f.width ? f.width + this.padWidth : 160 + this.padWidth
49272        ],true);
49273     }
49274 });
49275  
49276
49277 /**
49278  * @class Roo.form.FieldSet
49279  * @extends Roo.form.Layout
49280  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
49281  * @constructor
49282  * @param {Object} config Configuration options
49283  */
49284 Roo.form.FieldSet = function(config){
49285     Roo.form.FieldSet.superclass.constructor.call(this, config);
49286 };
49287
49288 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
49289     /**
49290      * @cfg {String} legend
49291      * The text to display as the legend for the FieldSet (defaults to '')
49292      */
49293     /**
49294      * @cfg {String/Object} autoCreate
49295      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
49296      */
49297
49298     // private
49299     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
49300
49301     // private
49302     onRender : function(ct, position){
49303         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
49304         if(this.legend){
49305             this.setLegend(this.legend);
49306         }
49307     },
49308
49309     // private
49310     setLegend : function(text){
49311         if(this.rendered){
49312             this.el.child('legend').update(text);
49313         }
49314     }
49315 });/*
49316  * Based on:
49317  * Ext JS Library 1.1.1
49318  * Copyright(c) 2006-2007, Ext JS, LLC.
49319  *
49320  * Originally Released Under LGPL - original licence link has changed is not relivant.
49321  *
49322  * Fork - LGPL
49323  * <script type="text/javascript">
49324  */
49325 /**
49326  * @class Roo.form.VTypes
49327  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
49328  * @singleton
49329  */
49330 Roo.form.VTypes = function(){
49331     // closure these in so they are only created once.
49332     var alpha = /^[a-zA-Z_]+$/;
49333     var alphanum = /^[a-zA-Z0-9_]+$/;
49334     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
49335     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
49336
49337     // All these messages and functions are configurable
49338     return {
49339         /**
49340          * The function used to validate email addresses
49341          * @param {String} value The email address
49342          */
49343         'email' : function(v){
49344             return email.test(v);
49345         },
49346         /**
49347          * The error text to display when the email validation function returns false
49348          * @type String
49349          */
49350         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
49351         /**
49352          * The keystroke filter mask to be applied on email input
49353          * @type RegExp
49354          */
49355         'emailMask' : /[a-z0-9_\.\-@]/i,
49356
49357         /**
49358          * The function used to validate URLs
49359          * @param {String} value The URL
49360          */
49361         'url' : function(v){
49362             return url.test(v);
49363         },
49364         /**
49365          * The error text to display when the url validation function returns false
49366          * @type String
49367          */
49368         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
49369         
49370         /**
49371          * The function used to validate alpha values
49372          * @param {String} value The value
49373          */
49374         'alpha' : function(v){
49375             return alpha.test(v);
49376         },
49377         /**
49378          * The error text to display when the alpha validation function returns false
49379          * @type String
49380          */
49381         'alphaText' : 'This field should only contain letters and _',
49382         /**
49383          * The keystroke filter mask to be applied on alpha input
49384          * @type RegExp
49385          */
49386         'alphaMask' : /[a-z_]/i,
49387
49388         /**
49389          * The function used to validate alphanumeric values
49390          * @param {String} value The value
49391          */
49392         'alphanum' : function(v){
49393             return alphanum.test(v);
49394         },
49395         /**
49396          * The error text to display when the alphanumeric validation function returns false
49397          * @type String
49398          */
49399         'alphanumText' : 'This field should only contain letters, numbers and _',
49400         /**
49401          * The keystroke filter mask to be applied on alphanumeric input
49402          * @type RegExp
49403          */
49404         'alphanumMask' : /[a-z0-9_]/i
49405     };
49406 }();//<script type="text/javascript">
49407
49408 /**
49409  * @class Roo.form.FCKeditor
49410  * @extends Roo.form.TextArea
49411  * Wrapper around the FCKEditor http://www.fckeditor.net
49412  * @constructor
49413  * Creates a new FCKeditor
49414  * @param {Object} config Configuration options
49415  */
49416 Roo.form.FCKeditor = function(config){
49417     Roo.form.FCKeditor.superclass.constructor.call(this, config);
49418     this.addEvents({
49419          /**
49420          * @event editorinit
49421          * Fired when the editor is initialized - you can add extra handlers here..
49422          * @param {FCKeditor} this
49423          * @param {Object} the FCK object.
49424          */
49425         editorinit : true
49426     });
49427     
49428     
49429 };
49430 Roo.form.FCKeditor.editors = { };
49431 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
49432 {
49433     //defaultAutoCreate : {
49434     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
49435     //},
49436     // private
49437     /**
49438      * @cfg {Object} fck options - see fck manual for details.
49439      */
49440     fckconfig : false,
49441     
49442     /**
49443      * @cfg {Object} fck toolbar set (Basic or Default)
49444      */
49445     toolbarSet : 'Basic',
49446     /**
49447      * @cfg {Object} fck BasePath
49448      */ 
49449     basePath : '/fckeditor/',
49450     
49451     
49452     frame : false,
49453     
49454     value : '',
49455     
49456    
49457     onRender : function(ct, position)
49458     {
49459         if(!this.el){
49460             this.defaultAutoCreate = {
49461                 tag: "textarea",
49462                 style:"width:300px;height:60px;",
49463                 autocomplete: "new-password"
49464             };
49465         }
49466         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
49467         /*
49468         if(this.grow){
49469             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
49470             if(this.preventScrollbars){
49471                 this.el.setStyle("overflow", "hidden");
49472             }
49473             this.el.setHeight(this.growMin);
49474         }
49475         */
49476         //console.log('onrender' + this.getId() );
49477         Roo.form.FCKeditor.editors[this.getId()] = this;
49478          
49479
49480         this.replaceTextarea() ;
49481         
49482     },
49483     
49484     getEditor : function() {
49485         return this.fckEditor;
49486     },
49487     /**
49488      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
49489      * @param {Mixed} value The value to set
49490      */
49491     
49492     
49493     setValue : function(value)
49494     {
49495         //console.log('setValue: ' + value);
49496         
49497         if(typeof(value) == 'undefined') { // not sure why this is happending...
49498             return;
49499         }
49500         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
49501         
49502         //if(!this.el || !this.getEditor()) {
49503         //    this.value = value;
49504             //this.setValue.defer(100,this,[value]);    
49505         //    return;
49506         //} 
49507         
49508         if(!this.getEditor()) {
49509             return;
49510         }
49511         
49512         this.getEditor().SetData(value);
49513         
49514         //
49515
49516     },
49517
49518     /**
49519      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
49520      * @return {Mixed} value The field value
49521      */
49522     getValue : function()
49523     {
49524         
49525         if (this.frame && this.frame.dom.style.display == 'none') {
49526             return Roo.form.FCKeditor.superclass.getValue.call(this);
49527         }
49528         
49529         if(!this.el || !this.getEditor()) {
49530            
49531            // this.getValue.defer(100,this); 
49532             return this.value;
49533         }
49534        
49535         
49536         var value=this.getEditor().GetData();
49537         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
49538         return Roo.form.FCKeditor.superclass.getValue.call(this);
49539         
49540
49541     },
49542
49543     /**
49544      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
49545      * @return {Mixed} value The field value
49546      */
49547     getRawValue : function()
49548     {
49549         if (this.frame && this.frame.dom.style.display == 'none') {
49550             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
49551         }
49552         
49553         if(!this.el || !this.getEditor()) {
49554             //this.getRawValue.defer(100,this); 
49555             return this.value;
49556             return;
49557         }
49558         
49559         
49560         
49561         var value=this.getEditor().GetData();
49562         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
49563         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
49564          
49565     },
49566     
49567     setSize : function(w,h) {
49568         
49569         
49570         
49571         //if (this.frame && this.frame.dom.style.display == 'none') {
49572         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
49573         //    return;
49574         //}
49575         //if(!this.el || !this.getEditor()) {
49576         //    this.setSize.defer(100,this, [w,h]); 
49577         //    return;
49578         //}
49579         
49580         
49581         
49582         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
49583         
49584         this.frame.dom.setAttribute('width', w);
49585         this.frame.dom.setAttribute('height', h);
49586         this.frame.setSize(w,h);
49587         
49588     },
49589     
49590     toggleSourceEdit : function(value) {
49591         
49592       
49593          
49594         this.el.dom.style.display = value ? '' : 'none';
49595         this.frame.dom.style.display = value ?  'none' : '';
49596         
49597     },
49598     
49599     
49600     focus: function(tag)
49601     {
49602         if (this.frame.dom.style.display == 'none') {
49603             return Roo.form.FCKeditor.superclass.focus.call(this);
49604         }
49605         if(!this.el || !this.getEditor()) {
49606             this.focus.defer(100,this, [tag]); 
49607             return;
49608         }
49609         
49610         
49611         
49612         
49613         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
49614         this.getEditor().Focus();
49615         if (tgs.length) {
49616             if (!this.getEditor().Selection.GetSelection()) {
49617                 this.focus.defer(100,this, [tag]); 
49618                 return;
49619             }
49620             
49621             
49622             var r = this.getEditor().EditorDocument.createRange();
49623             r.setStart(tgs[0],0);
49624             r.setEnd(tgs[0],0);
49625             this.getEditor().Selection.GetSelection().removeAllRanges();
49626             this.getEditor().Selection.GetSelection().addRange(r);
49627             this.getEditor().Focus();
49628         }
49629         
49630     },
49631     
49632     
49633     
49634     replaceTextarea : function()
49635     {
49636         if ( document.getElementById( this.getId() + '___Frame' ) ) {
49637             return ;
49638         }
49639         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
49640         //{
49641             // We must check the elements firstly using the Id and then the name.
49642         var oTextarea = document.getElementById( this.getId() );
49643         
49644         var colElementsByName = document.getElementsByName( this.getId() ) ;
49645          
49646         oTextarea.style.display = 'none' ;
49647
49648         if ( oTextarea.tabIndex ) {            
49649             this.TabIndex = oTextarea.tabIndex ;
49650         }
49651         
49652         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
49653         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
49654         this.frame = Roo.get(this.getId() + '___Frame')
49655     },
49656     
49657     _getConfigHtml : function()
49658     {
49659         var sConfig = '' ;
49660
49661         for ( var o in this.fckconfig ) {
49662             sConfig += sConfig.length > 0  ? '&amp;' : '';
49663             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
49664         }
49665
49666         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
49667     },
49668     
49669     
49670     _getIFrameHtml : function()
49671     {
49672         var sFile = 'fckeditor.html' ;
49673         /* no idea what this is about..
49674         try
49675         {
49676             if ( (/fcksource=true/i).test( window.top.location.search ) )
49677                 sFile = 'fckeditor.original.html' ;
49678         }
49679         catch (e) { 
49680         */
49681
49682         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
49683         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
49684         
49685         
49686         var html = '<iframe id="' + this.getId() +
49687             '___Frame" src="' + sLink +
49688             '" width="' + this.width +
49689             '" height="' + this.height + '"' +
49690             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
49691             ' frameborder="0" scrolling="no"></iframe>' ;
49692
49693         return html ;
49694     },
49695     
49696     _insertHtmlBefore : function( html, element )
49697     {
49698         if ( element.insertAdjacentHTML )       {
49699             // IE
49700             element.insertAdjacentHTML( 'beforeBegin', html ) ;
49701         } else { // Gecko
49702             var oRange = document.createRange() ;
49703             oRange.setStartBefore( element ) ;
49704             var oFragment = oRange.createContextualFragment( html );
49705             element.parentNode.insertBefore( oFragment, element ) ;
49706         }
49707     }
49708     
49709     
49710   
49711     
49712     
49713     
49714     
49715
49716 });
49717
49718 //Roo.reg('fckeditor', Roo.form.FCKeditor);
49719
49720 function FCKeditor_OnComplete(editorInstance){
49721     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
49722     f.fckEditor = editorInstance;
49723     //console.log("loaded");
49724     f.fireEvent('editorinit', f, editorInstance);
49725
49726   
49727
49728  
49729
49730
49731
49732
49733
49734
49735
49736
49737
49738
49739
49740
49741
49742
49743
49744 //<script type="text/javascript">
49745 /**
49746  * @class Roo.form.GridField
49747  * @extends Roo.form.Field
49748  * Embed a grid (or editable grid into a form)
49749  * STATUS ALPHA
49750  * 
49751  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
49752  * it needs 
49753  * xgrid.store = Roo.data.Store
49754  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
49755  * xgrid.store.reader = Roo.data.JsonReader 
49756  * 
49757  * 
49758  * @constructor
49759  * Creates a new GridField
49760  * @param {Object} config Configuration options
49761  */
49762 Roo.form.GridField = function(config){
49763     Roo.form.GridField.superclass.constructor.call(this, config);
49764      
49765 };
49766
49767 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
49768     /**
49769      * @cfg {Number} width  - used to restrict width of grid..
49770      */
49771     width : 100,
49772     /**
49773      * @cfg {Number} height - used to restrict height of grid..
49774      */
49775     height : 50,
49776      /**
49777      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
49778          * 
49779          *}
49780      */
49781     xgrid : false, 
49782     /**
49783      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
49784      * {tag: "input", type: "checkbox", autocomplete: "off"})
49785      */
49786    // defaultAutoCreate : { tag: 'div' },
49787     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
49788     /**
49789      * @cfg {String} addTitle Text to include for adding a title.
49790      */
49791     addTitle : false,
49792     //
49793     onResize : function(){
49794         Roo.form.Field.superclass.onResize.apply(this, arguments);
49795     },
49796
49797     initEvents : function(){
49798         // Roo.form.Checkbox.superclass.initEvents.call(this);
49799         // has no events...
49800        
49801     },
49802
49803
49804     getResizeEl : function(){
49805         return this.wrap;
49806     },
49807
49808     getPositionEl : function(){
49809         return this.wrap;
49810     },
49811
49812     // private
49813     onRender : function(ct, position){
49814         
49815         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
49816         var style = this.style;
49817         delete this.style;
49818         
49819         Roo.form.GridField.superclass.onRender.call(this, ct, position);
49820         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
49821         this.viewEl = this.wrap.createChild({ tag: 'div' });
49822         if (style) {
49823             this.viewEl.applyStyles(style);
49824         }
49825         if (this.width) {
49826             this.viewEl.setWidth(this.width);
49827         }
49828         if (this.height) {
49829             this.viewEl.setHeight(this.height);
49830         }
49831         //if(this.inputValue !== undefined){
49832         //this.setValue(this.value);
49833         
49834         
49835         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
49836         
49837         
49838         this.grid.render();
49839         this.grid.getDataSource().on('remove', this.refreshValue, this);
49840         this.grid.getDataSource().on('update', this.refreshValue, this);
49841         this.grid.on('afteredit', this.refreshValue, this);
49842  
49843     },
49844      
49845     
49846     /**
49847      * Sets the value of the item. 
49848      * @param {String} either an object  or a string..
49849      */
49850     setValue : function(v){
49851         //this.value = v;
49852         v = v || []; // empty set..
49853         // this does not seem smart - it really only affects memoryproxy grids..
49854         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
49855             var ds = this.grid.getDataSource();
49856             // assumes a json reader..
49857             var data = {}
49858             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
49859             ds.loadData( data);
49860         }
49861         // clear selection so it does not get stale.
49862         if (this.grid.sm) { 
49863             this.grid.sm.clearSelections();
49864         }
49865         
49866         Roo.form.GridField.superclass.setValue.call(this, v);
49867         this.refreshValue();
49868         // should load data in the grid really....
49869     },
49870     
49871     // private
49872     refreshValue: function() {
49873          var val = [];
49874         this.grid.getDataSource().each(function(r) {
49875             val.push(r.data);
49876         });
49877         this.el.dom.value = Roo.encode(val);
49878     }
49879     
49880      
49881     
49882     
49883 });/*
49884  * Based on:
49885  * Ext JS Library 1.1.1
49886  * Copyright(c) 2006-2007, Ext JS, LLC.
49887  *
49888  * Originally Released Under LGPL - original licence link has changed is not relivant.
49889  *
49890  * Fork - LGPL
49891  * <script type="text/javascript">
49892  */
49893 /**
49894  * @class Roo.form.DisplayField
49895  * @extends Roo.form.Field
49896  * A generic Field to display non-editable data.
49897  * @cfg {Boolean} closable (true|false) default false
49898  * @constructor
49899  * Creates a new Display Field item.
49900  * @param {Object} config Configuration options
49901  */
49902 Roo.form.DisplayField = function(config){
49903     Roo.form.DisplayField.superclass.constructor.call(this, config);
49904     
49905     this.addEvents({
49906         /**
49907          * @event close
49908          * Fires after the click the close btn
49909              * @param {Roo.form.DisplayField} this
49910              */
49911         close : true
49912     });
49913 };
49914
49915 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
49916     inputType:      'hidden',
49917     allowBlank:     true,
49918     readOnly:         true,
49919     
49920  
49921     /**
49922      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
49923      */
49924     focusClass : undefined,
49925     /**
49926      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
49927      */
49928     fieldClass: 'x-form-field',
49929     
49930      /**
49931      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
49932      */
49933     valueRenderer: undefined,
49934     
49935     width: 100,
49936     /**
49937      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
49938      * {tag: "input", type: "checkbox", autocomplete: "off"})
49939      */
49940      
49941  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
49942  
49943     closable : false,
49944     
49945     onResize : function(){
49946         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
49947         
49948     },
49949
49950     initEvents : function(){
49951         // Roo.form.Checkbox.superclass.initEvents.call(this);
49952         // has no events...
49953         
49954         if(this.closable){
49955             this.closeEl.on('click', this.onClose, this);
49956         }
49957        
49958     },
49959
49960
49961     getResizeEl : function(){
49962         return this.wrap;
49963     },
49964
49965     getPositionEl : function(){
49966         return this.wrap;
49967     },
49968
49969     // private
49970     onRender : function(ct, position){
49971         
49972         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
49973         //if(this.inputValue !== undefined){
49974         this.wrap = this.el.wrap();
49975         
49976         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
49977         
49978         if(this.closable){
49979             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
49980         }
49981         
49982         if (this.bodyStyle) {
49983             this.viewEl.applyStyles(this.bodyStyle);
49984         }
49985         //this.viewEl.setStyle('padding', '2px');
49986         
49987         this.setValue(this.value);
49988         
49989     },
49990 /*
49991     // private
49992     initValue : Roo.emptyFn,
49993
49994   */
49995
49996         // private
49997     onClick : function(){
49998         
49999     },
50000
50001     /**
50002      * Sets the checked state of the checkbox.
50003      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
50004      */
50005     setValue : function(v){
50006         this.value = v;
50007         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
50008         // this might be called before we have a dom element..
50009         if (!this.viewEl) {
50010             return;
50011         }
50012         this.viewEl.dom.innerHTML = html;
50013         Roo.form.DisplayField.superclass.setValue.call(this, v);
50014
50015     },
50016     
50017     onClose : function(e)
50018     {
50019         e.preventDefault();
50020         
50021         this.fireEvent('close', this);
50022     }
50023 });/*
50024  * 
50025  * Licence- LGPL
50026  * 
50027  */
50028
50029 /**
50030  * @class Roo.form.DayPicker
50031  * @extends Roo.form.Field
50032  * A Day picker show [M] [T] [W] ....
50033  * @constructor
50034  * Creates a new Day Picker
50035  * @param {Object} config Configuration options
50036  */
50037 Roo.form.DayPicker= function(config){
50038     Roo.form.DayPicker.superclass.constructor.call(this, config);
50039      
50040 };
50041
50042 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
50043     /**
50044      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
50045      */
50046     focusClass : undefined,
50047     /**
50048      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
50049      */
50050     fieldClass: "x-form-field",
50051    
50052     /**
50053      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
50054      * {tag: "input", type: "checkbox", autocomplete: "off"})
50055      */
50056     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
50057     
50058    
50059     actionMode : 'viewEl', 
50060     //
50061     // private
50062  
50063     inputType : 'hidden',
50064     
50065      
50066     inputElement: false, // real input element?
50067     basedOn: false, // ????
50068     
50069     isFormField: true, // not sure where this is needed!!!!
50070
50071     onResize : function(){
50072         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
50073         if(!this.boxLabel){
50074             this.el.alignTo(this.wrap, 'c-c');
50075         }
50076     },
50077
50078     initEvents : function(){
50079         Roo.form.Checkbox.superclass.initEvents.call(this);
50080         this.el.on("click", this.onClick,  this);
50081         this.el.on("change", this.onClick,  this);
50082     },
50083
50084
50085     getResizeEl : function(){
50086         return this.wrap;
50087     },
50088
50089     getPositionEl : function(){
50090         return this.wrap;
50091     },
50092
50093     
50094     // private
50095     onRender : function(ct, position){
50096         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
50097        
50098         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
50099         
50100         var r1 = '<table><tr>';
50101         var r2 = '<tr class="x-form-daypick-icons">';
50102         for (var i=0; i < 7; i++) {
50103             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
50104             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
50105         }
50106         
50107         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
50108         viewEl.select('img').on('click', this.onClick, this);
50109         this.viewEl = viewEl;   
50110         
50111         
50112         // this will not work on Chrome!!!
50113         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
50114         this.el.on('propertychange', this.setFromHidden,  this);  //ie
50115         
50116         
50117           
50118
50119     },
50120
50121     // private
50122     initValue : Roo.emptyFn,
50123
50124     /**
50125      * Returns the checked state of the checkbox.
50126      * @return {Boolean} True if checked, else false
50127      */
50128     getValue : function(){
50129         return this.el.dom.value;
50130         
50131     },
50132
50133         // private
50134     onClick : function(e){ 
50135         //this.setChecked(!this.checked);
50136         Roo.get(e.target).toggleClass('x-menu-item-checked');
50137         this.refreshValue();
50138         //if(this.el.dom.checked != this.checked){
50139         //    this.setValue(this.el.dom.checked);
50140        // }
50141     },
50142     
50143     // private
50144     refreshValue : function()
50145     {
50146         var val = '';
50147         this.viewEl.select('img',true).each(function(e,i,n)  {
50148             val += e.is(".x-menu-item-checked") ? String(n) : '';
50149         });
50150         this.setValue(val, true);
50151     },
50152
50153     /**
50154      * Sets the checked state of the checkbox.
50155      * On is always based on a string comparison between inputValue and the param.
50156      * @param {Boolean/String} value - the value to set 
50157      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
50158      */
50159     setValue : function(v,suppressEvent){
50160         if (!this.el.dom) {
50161             return;
50162         }
50163         var old = this.el.dom.value ;
50164         this.el.dom.value = v;
50165         if (suppressEvent) {
50166             return ;
50167         }
50168          
50169         // update display..
50170         this.viewEl.select('img',true).each(function(e,i,n)  {
50171             
50172             var on = e.is(".x-menu-item-checked");
50173             var newv = v.indexOf(String(n)) > -1;
50174             if (on != newv) {
50175                 e.toggleClass('x-menu-item-checked');
50176             }
50177             
50178         });
50179         
50180         
50181         this.fireEvent('change', this, v, old);
50182         
50183         
50184     },
50185    
50186     // handle setting of hidden value by some other method!!?!?
50187     setFromHidden: function()
50188     {
50189         if(!this.el){
50190             return;
50191         }
50192         //console.log("SET FROM HIDDEN");
50193         //alert('setFrom hidden');
50194         this.setValue(this.el.dom.value);
50195     },
50196     
50197     onDestroy : function()
50198     {
50199         if(this.viewEl){
50200             Roo.get(this.viewEl).remove();
50201         }
50202          
50203         Roo.form.DayPicker.superclass.onDestroy.call(this);
50204     }
50205
50206 });/*
50207  * RooJS Library 1.1.1
50208  * Copyright(c) 2008-2011  Alan Knowles
50209  *
50210  * License - LGPL
50211  */
50212  
50213
50214 /**
50215  * @class Roo.form.ComboCheck
50216  * @extends Roo.form.ComboBox
50217  * A combobox for multiple select items.
50218  *
50219  * FIXME - could do with a reset button..
50220  * 
50221  * @constructor
50222  * Create a new ComboCheck
50223  * @param {Object} config Configuration options
50224  */
50225 Roo.form.ComboCheck = function(config){
50226     Roo.form.ComboCheck.superclass.constructor.call(this, config);
50227     // should verify some data...
50228     // like
50229     // hiddenName = required..
50230     // displayField = required
50231     // valudField == required
50232     var req= [ 'hiddenName', 'displayField', 'valueField' ];
50233     var _t = this;
50234     Roo.each(req, function(e) {
50235         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
50236             throw "Roo.form.ComboCheck : missing value for: " + e;
50237         }
50238     });
50239     
50240     
50241 };
50242
50243 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
50244      
50245      
50246     editable : false,
50247      
50248     selectedClass: 'x-menu-item-checked', 
50249     
50250     // private
50251     onRender : function(ct, position){
50252         var _t = this;
50253         
50254         
50255         
50256         if(!this.tpl){
50257             var cls = 'x-combo-list';
50258
50259             
50260             this.tpl =  new Roo.Template({
50261                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
50262                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
50263                    '<span>{' + this.displayField + '}</span>' +
50264                     '</div>' 
50265                 
50266             });
50267         }
50268  
50269         
50270         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
50271         this.view.singleSelect = false;
50272         this.view.multiSelect = true;
50273         this.view.toggleSelect = true;
50274         this.pageTb.add(new Roo.Toolbar.Fill(), {
50275             
50276             text: 'Done',
50277             handler: function()
50278             {
50279                 _t.collapse();
50280             }
50281         });
50282     },
50283     
50284     onViewOver : function(e, t){
50285         // do nothing...
50286         return;
50287         
50288     },
50289     
50290     onViewClick : function(doFocus,index){
50291         return;
50292         
50293     },
50294     select: function () {
50295         //Roo.log("SELECT CALLED");
50296     },
50297      
50298     selectByValue : function(xv, scrollIntoView){
50299         var ar = this.getValueArray();
50300         var sels = [];
50301         
50302         Roo.each(ar, function(v) {
50303             if(v === undefined || v === null){
50304                 return;
50305             }
50306             var r = this.findRecord(this.valueField, v);
50307             if(r){
50308                 sels.push(this.store.indexOf(r))
50309                 
50310             }
50311         },this);
50312         this.view.select(sels);
50313         return false;
50314     },
50315     
50316     
50317     
50318     onSelect : function(record, index){
50319        // Roo.log("onselect Called");
50320        // this is only called by the clear button now..
50321         this.view.clearSelections();
50322         this.setValue('[]');
50323         if (this.value != this.valueBefore) {
50324             this.fireEvent('change', this, this.value, this.valueBefore);
50325             this.valueBefore = this.value;
50326         }
50327     },
50328     getValueArray : function()
50329     {
50330         var ar = [] ;
50331         
50332         try {
50333             //Roo.log(this.value);
50334             if (typeof(this.value) == 'undefined') {
50335                 return [];
50336             }
50337             var ar = Roo.decode(this.value);
50338             return  ar instanceof Array ? ar : []; //?? valid?
50339             
50340         } catch(e) {
50341             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
50342             return [];
50343         }
50344          
50345     },
50346     expand : function ()
50347     {
50348         
50349         Roo.form.ComboCheck.superclass.expand.call(this);
50350         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
50351         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
50352         
50353
50354     },
50355     
50356     collapse : function(){
50357         Roo.form.ComboCheck.superclass.collapse.call(this);
50358         var sl = this.view.getSelectedIndexes();
50359         var st = this.store;
50360         var nv = [];
50361         var tv = [];
50362         var r;
50363         Roo.each(sl, function(i) {
50364             r = st.getAt(i);
50365             nv.push(r.get(this.valueField));
50366         },this);
50367         this.setValue(Roo.encode(nv));
50368         if (this.value != this.valueBefore) {
50369
50370             this.fireEvent('change', this, this.value, this.valueBefore);
50371             this.valueBefore = this.value;
50372         }
50373         
50374     },
50375     
50376     setValue : function(v){
50377         // Roo.log(v);
50378         this.value = v;
50379         
50380         var vals = this.getValueArray();
50381         var tv = [];
50382         Roo.each(vals, function(k) {
50383             var r = this.findRecord(this.valueField, k);
50384             if(r){
50385                 tv.push(r.data[this.displayField]);
50386             }else if(this.valueNotFoundText !== undefined){
50387                 tv.push( this.valueNotFoundText );
50388             }
50389         },this);
50390        // Roo.log(tv);
50391         
50392         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
50393         this.hiddenField.value = v;
50394         this.value = v;
50395     }
50396     
50397 });/*
50398  * Based on:
50399  * Ext JS Library 1.1.1
50400  * Copyright(c) 2006-2007, Ext JS, LLC.
50401  *
50402  * Originally Released Under LGPL - original licence link has changed is not relivant.
50403  *
50404  * Fork - LGPL
50405  * <script type="text/javascript">
50406  */
50407  
50408 /**
50409  * @class Roo.form.Signature
50410  * @extends Roo.form.Field
50411  * Signature field.  
50412  * @constructor
50413  * 
50414  * @param {Object} config Configuration options
50415  */
50416
50417 Roo.form.Signature = function(config){
50418     Roo.form.Signature.superclass.constructor.call(this, config);
50419     
50420     this.addEvents({// not in used??
50421          /**
50422          * @event confirm
50423          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
50424              * @param {Roo.form.Signature} combo This combo box
50425              */
50426         'confirm' : true,
50427         /**
50428          * @event reset
50429          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
50430              * @param {Roo.form.ComboBox} combo This combo box
50431              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
50432              */
50433         'reset' : true
50434     });
50435 };
50436
50437 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
50438     /**
50439      * @cfg {Object} labels Label to use when rendering a form.
50440      * defaults to 
50441      * labels : { 
50442      *      clear : "Clear",
50443      *      confirm : "Confirm"
50444      *  }
50445      */
50446     labels : { 
50447         clear : "Clear",
50448         confirm : "Confirm"
50449     },
50450     /**
50451      * @cfg {Number} width The signature panel width (defaults to 300)
50452      */
50453     width: 300,
50454     /**
50455      * @cfg {Number} height The signature panel height (defaults to 100)
50456      */
50457     height : 100,
50458     /**
50459      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
50460      */
50461     allowBlank : false,
50462     
50463     //private
50464     // {Object} signPanel The signature SVG panel element (defaults to {})
50465     signPanel : {},
50466     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
50467     isMouseDown : false,
50468     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
50469     isConfirmed : false,
50470     // {String} signatureTmp SVG mapping string (defaults to empty string)
50471     signatureTmp : '',
50472     
50473     
50474     defaultAutoCreate : { // modified by initCompnoent..
50475         tag: "input",
50476         type:"hidden"
50477     },
50478
50479     // private
50480     onRender : function(ct, position){
50481         
50482         Roo.form.Signature.superclass.onRender.call(this, ct, position);
50483         
50484         this.wrap = this.el.wrap({
50485             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
50486         });
50487         
50488         this.createToolbar(this);
50489         this.signPanel = this.wrap.createChild({
50490                 tag: 'div',
50491                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
50492             }, this.el
50493         );
50494             
50495         this.svgID = Roo.id();
50496         this.svgEl = this.signPanel.createChild({
50497               xmlns : 'http://www.w3.org/2000/svg',
50498               tag : 'svg',
50499               id : this.svgID + "-svg",
50500               width: this.width,
50501               height: this.height,
50502               viewBox: '0 0 '+this.width+' '+this.height,
50503               cn : [
50504                 {
50505                     tag: "rect",
50506                     id: this.svgID + "-svg-r",
50507                     width: this.width,
50508                     height: this.height,
50509                     fill: "#ffa"
50510                 },
50511                 {
50512                     tag: "line",
50513                     id: this.svgID + "-svg-l",
50514                     x1: "0", // start
50515                     y1: (this.height*0.8), // start set the line in 80% of height
50516                     x2: this.width, // end
50517                     y2: (this.height*0.8), // end set the line in 80% of height
50518                     'stroke': "#666",
50519                     'stroke-width': "1",
50520                     'stroke-dasharray': "3",
50521                     'shape-rendering': "crispEdges",
50522                     'pointer-events': "none"
50523                 },
50524                 {
50525                     tag: "path",
50526                     id: this.svgID + "-svg-p",
50527                     'stroke': "navy",
50528                     'stroke-width': "3",
50529                     'fill': "none",
50530                     'pointer-events': 'none'
50531                 }
50532               ]
50533         });
50534         this.createSVG();
50535         this.svgBox = this.svgEl.dom.getScreenCTM();
50536     },
50537     createSVG : function(){ 
50538         var svg = this.signPanel;
50539         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
50540         var t = this;
50541
50542         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
50543         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
50544         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
50545         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
50546         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
50547         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
50548         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
50549         
50550     },
50551     isTouchEvent : function(e){
50552         return e.type.match(/^touch/);
50553     },
50554     getCoords : function (e) {
50555         var pt    = this.svgEl.dom.createSVGPoint();
50556         pt.x = e.clientX; 
50557         pt.y = e.clientY;
50558         if (this.isTouchEvent(e)) {
50559             pt.x =  e.targetTouches[0].clientX;
50560             pt.y = e.targetTouches[0].clientY;
50561         }
50562         var a = this.svgEl.dom.getScreenCTM();
50563         var b = a.inverse();
50564         var mx = pt.matrixTransform(b);
50565         return mx.x + ',' + mx.y;
50566     },
50567     //mouse event headler 
50568     down : function (e) {
50569         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
50570         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
50571         
50572         this.isMouseDown = true;
50573         
50574         e.preventDefault();
50575     },
50576     move : function (e) {
50577         if (this.isMouseDown) {
50578             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
50579             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
50580         }
50581         
50582         e.preventDefault();
50583     },
50584     up : function (e) {
50585         this.isMouseDown = false;
50586         var sp = this.signatureTmp.split(' ');
50587         
50588         if(sp.length > 1){
50589             if(!sp[sp.length-2].match(/^L/)){
50590                 sp.pop();
50591                 sp.pop();
50592                 sp.push("");
50593                 this.signatureTmp = sp.join(" ");
50594             }
50595         }
50596         if(this.getValue() != this.signatureTmp){
50597             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
50598             this.isConfirmed = false;
50599         }
50600         e.preventDefault();
50601     },
50602     
50603     /**
50604      * Protected method that will not generally be called directly. It
50605      * is called when the editor creates its toolbar. Override this method if you need to
50606      * add custom toolbar buttons.
50607      * @param {HtmlEditor} editor
50608      */
50609     createToolbar : function(editor){
50610          function btn(id, toggle, handler){
50611             var xid = fid + '-'+ id ;
50612             return {
50613                 id : xid,
50614                 cmd : id,
50615                 cls : 'x-btn-icon x-edit-'+id,
50616                 enableToggle:toggle !== false,
50617                 scope: editor, // was editor...
50618                 handler:handler||editor.relayBtnCmd,
50619                 clickEvent:'mousedown',
50620                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
50621                 tabIndex:-1
50622             };
50623         }
50624         
50625         
50626         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
50627         this.tb = tb;
50628         this.tb.add(
50629            {
50630                 cls : ' x-signature-btn x-signature-'+id,
50631                 scope: editor, // was editor...
50632                 handler: this.reset,
50633                 clickEvent:'mousedown',
50634                 text: this.labels.clear
50635             },
50636             {
50637                  xtype : 'Fill',
50638                  xns: Roo.Toolbar
50639             }, 
50640             {
50641                 cls : '  x-signature-btn x-signature-'+id,
50642                 scope: editor, // was editor...
50643                 handler: this.confirmHandler,
50644                 clickEvent:'mousedown',
50645                 text: this.labels.confirm
50646             }
50647         );
50648     
50649     },
50650     //public
50651     /**
50652      * when user is clicked confirm then show this image.....
50653      * 
50654      * @return {String} Image Data URI
50655      */
50656     getImageDataURI : function(){
50657         var svg = this.svgEl.dom.parentNode.innerHTML;
50658         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
50659         return src; 
50660     },
50661     /**
50662      * 
50663      * @return {Boolean} this.isConfirmed
50664      */
50665     getConfirmed : function(){
50666         return this.isConfirmed;
50667     },
50668     /**
50669      * 
50670      * @return {Number} this.width
50671      */
50672     getWidth : function(){
50673         return this.width;
50674     },
50675     /**
50676      * 
50677      * @return {Number} this.height
50678      */
50679     getHeight : function(){
50680         return this.height;
50681     },
50682     // private
50683     getSignature : function(){
50684         return this.signatureTmp;
50685     },
50686     // private
50687     reset : function(){
50688         this.signatureTmp = '';
50689         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
50690         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
50691         this.isConfirmed = false;
50692         Roo.form.Signature.superclass.reset.call(this);
50693     },
50694     setSignature : function(s){
50695         this.signatureTmp = s;
50696         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
50697         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
50698         this.setValue(s);
50699         this.isConfirmed = false;
50700         Roo.form.Signature.superclass.reset.call(this);
50701     }, 
50702     test : function(){
50703 //        Roo.log(this.signPanel.dom.contentWindow.up())
50704     },
50705     //private
50706     setConfirmed : function(){
50707         
50708         
50709         
50710 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
50711     },
50712     // private
50713     confirmHandler : function(){
50714         if(!this.getSignature()){
50715             return;
50716         }
50717         
50718         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
50719         this.setValue(this.getSignature());
50720         this.isConfirmed = true;
50721         
50722         this.fireEvent('confirm', this);
50723     },
50724     // private
50725     // Subclasses should provide the validation implementation by overriding this
50726     validateValue : function(value){
50727         if(this.allowBlank){
50728             return true;
50729         }
50730         
50731         if(this.isConfirmed){
50732             return true;
50733         }
50734         return false;
50735     }
50736 });/*
50737  * Based on:
50738  * Ext JS Library 1.1.1
50739  * Copyright(c) 2006-2007, Ext JS, LLC.
50740  *
50741  * Originally Released Under LGPL - original licence link has changed is not relivant.
50742  *
50743  * Fork - LGPL
50744  * <script type="text/javascript">
50745  */
50746  
50747
50748 /**
50749  * @class Roo.form.ComboBox
50750  * @extends Roo.form.TriggerField
50751  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
50752  * @constructor
50753  * Create a new ComboBox.
50754  * @param {Object} config Configuration options
50755  */
50756 Roo.form.Select = function(config){
50757     Roo.form.Select.superclass.constructor.call(this, config);
50758      
50759 };
50760
50761 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
50762     /**
50763      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
50764      */
50765     /**
50766      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
50767      * rendering into an Roo.Editor, defaults to false)
50768      */
50769     /**
50770      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
50771      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
50772      */
50773     /**
50774      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
50775      */
50776     /**
50777      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
50778      * the dropdown list (defaults to undefined, with no header element)
50779      */
50780
50781      /**
50782      * @cfg {String/Roo.Template} tpl The template to use to render the output
50783      */
50784      
50785     // private
50786     defaultAutoCreate : {tag: "select"  },
50787     /**
50788      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
50789      */
50790     listWidth: undefined,
50791     /**
50792      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
50793      * mode = 'remote' or 'text' if mode = 'local')
50794      */
50795     displayField: undefined,
50796     /**
50797      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
50798      * mode = 'remote' or 'value' if mode = 'local'). 
50799      * Note: use of a valueField requires the user make a selection
50800      * in order for a value to be mapped.
50801      */
50802     valueField: undefined,
50803     
50804     
50805     /**
50806      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
50807      * field's data value (defaults to the underlying DOM element's name)
50808      */
50809     hiddenName: undefined,
50810     /**
50811      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
50812      */
50813     listClass: '',
50814     /**
50815      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
50816      */
50817     selectedClass: 'x-combo-selected',
50818     /**
50819      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
50820      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
50821      * which displays a downward arrow icon).
50822      */
50823     triggerClass : 'x-form-arrow-trigger',
50824     /**
50825      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
50826      */
50827     shadow:'sides',
50828     /**
50829      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
50830      * anchor positions (defaults to 'tl-bl')
50831      */
50832     listAlign: 'tl-bl?',
50833     /**
50834      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
50835      */
50836     maxHeight: 300,
50837     /**
50838      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
50839      * query specified by the allQuery config option (defaults to 'query')
50840      */
50841     triggerAction: 'query',
50842     /**
50843      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
50844      * (defaults to 4, does not apply if editable = false)
50845      */
50846     minChars : 4,
50847     /**
50848      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
50849      * delay (typeAheadDelay) if it matches a known value (defaults to false)
50850      */
50851     typeAhead: false,
50852     /**
50853      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
50854      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
50855      */
50856     queryDelay: 500,
50857     /**
50858      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
50859      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
50860      */
50861     pageSize: 0,
50862     /**
50863      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
50864      * when editable = true (defaults to false)
50865      */
50866     selectOnFocus:false,
50867     /**
50868      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
50869      */
50870     queryParam: 'query',
50871     /**
50872      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
50873      * when mode = 'remote' (defaults to 'Loading...')
50874      */
50875     loadingText: 'Loading...',
50876     /**
50877      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
50878      */
50879     resizable: false,
50880     /**
50881      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
50882      */
50883     handleHeight : 8,
50884     /**
50885      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
50886      * traditional select (defaults to true)
50887      */
50888     editable: true,
50889     /**
50890      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
50891      */
50892     allQuery: '',
50893     /**
50894      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
50895      */
50896     mode: 'remote',
50897     /**
50898      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
50899      * listWidth has a higher value)
50900      */
50901     minListWidth : 70,
50902     /**
50903      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
50904      * allow the user to set arbitrary text into the field (defaults to false)
50905      */
50906     forceSelection:false,
50907     /**
50908      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
50909      * if typeAhead = true (defaults to 250)
50910      */
50911     typeAheadDelay : 250,
50912     /**
50913      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
50914      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
50915      */
50916     valueNotFoundText : undefined,
50917     
50918     /**
50919      * @cfg {String} defaultValue The value displayed after loading the store.
50920      */
50921     defaultValue: '',
50922     
50923     /**
50924      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
50925      */
50926     blockFocus : false,
50927     
50928     /**
50929      * @cfg {Boolean} disableClear Disable showing of clear button.
50930      */
50931     disableClear : false,
50932     /**
50933      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
50934      */
50935     alwaysQuery : false,
50936     
50937     //private
50938     addicon : false,
50939     editicon: false,
50940     
50941     // element that contains real text value.. (when hidden is used..)
50942      
50943     // private
50944     onRender : function(ct, position){
50945         Roo.form.Field.prototype.onRender.call(this, ct, position);
50946         
50947         if(this.store){
50948             this.store.on('beforeload', this.onBeforeLoad, this);
50949             this.store.on('load', this.onLoad, this);
50950             this.store.on('loadexception', this.onLoadException, this);
50951             this.store.load({});
50952         }
50953         
50954         
50955         
50956     },
50957
50958     // private
50959     initEvents : function(){
50960         //Roo.form.ComboBox.superclass.initEvents.call(this);
50961  
50962     },
50963
50964     onDestroy : function(){
50965        
50966         if(this.store){
50967             this.store.un('beforeload', this.onBeforeLoad, this);
50968             this.store.un('load', this.onLoad, this);
50969             this.store.un('loadexception', this.onLoadException, this);
50970         }
50971         //Roo.form.ComboBox.superclass.onDestroy.call(this);
50972     },
50973
50974     // private
50975     fireKey : function(e){
50976         if(e.isNavKeyPress() && !this.list.isVisible()){
50977             this.fireEvent("specialkey", this, e);
50978         }
50979     },
50980
50981     // private
50982     onResize: function(w, h){
50983         
50984         return; 
50985     
50986         
50987     },
50988
50989     /**
50990      * Allow or prevent the user from directly editing the field text.  If false is passed,
50991      * the user will only be able to select from the items defined in the dropdown list.  This method
50992      * is the runtime equivalent of setting the 'editable' config option at config time.
50993      * @param {Boolean} value True to allow the user to directly edit the field text
50994      */
50995     setEditable : function(value){
50996          
50997     },
50998
50999     // private
51000     onBeforeLoad : function(){
51001         
51002         Roo.log("Select before load");
51003         return;
51004     
51005         this.innerList.update(this.loadingText ?
51006                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
51007         //this.restrictHeight();
51008         this.selectedIndex = -1;
51009     },
51010
51011     // private
51012     onLoad : function(){
51013
51014     
51015         var dom = this.el.dom;
51016         dom.innerHTML = '';
51017          var od = dom.ownerDocument;
51018          
51019         if (this.emptyText) {
51020             var op = od.createElement('option');
51021             op.setAttribute('value', '');
51022             op.innerHTML = String.format('{0}', this.emptyText);
51023             dom.appendChild(op);
51024         }
51025         if(this.store.getCount() > 0){
51026            
51027             var vf = this.valueField;
51028             var df = this.displayField;
51029             this.store.data.each(function(r) {
51030                 // which colmsn to use... testing - cdoe / title..
51031                 var op = od.createElement('option');
51032                 op.setAttribute('value', r.data[vf]);
51033                 op.innerHTML = String.format('{0}', r.data[df]);
51034                 dom.appendChild(op);
51035             });
51036             if (typeof(this.defaultValue != 'undefined')) {
51037                 this.setValue(this.defaultValue);
51038             }
51039             
51040              
51041         }else{
51042             //this.onEmptyResults();
51043         }
51044         //this.el.focus();
51045     },
51046     // private
51047     onLoadException : function()
51048     {
51049         dom.innerHTML = '';
51050             
51051         Roo.log("Select on load exception");
51052         return;
51053     
51054         this.collapse();
51055         Roo.log(this.store.reader.jsonData);
51056         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
51057             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
51058         }
51059         
51060         
51061     },
51062     // private
51063     onTypeAhead : function(){
51064          
51065     },
51066
51067     // private
51068     onSelect : function(record, index){
51069         Roo.log('on select?');
51070         return;
51071         if(this.fireEvent('beforeselect', this, record, index) !== false){
51072             this.setFromData(index > -1 ? record.data : false);
51073             this.collapse();
51074             this.fireEvent('select', this, record, index);
51075         }
51076     },
51077
51078     /**
51079      * Returns the currently selected field value or empty string if no value is set.
51080      * @return {String} value The selected value
51081      */
51082     getValue : function(){
51083         var dom = this.el.dom;
51084         this.value = dom.options[dom.selectedIndex].value;
51085         return this.value;
51086         
51087     },
51088
51089     /**
51090      * Clears any text/value currently set in the field
51091      */
51092     clearValue : function(){
51093         this.value = '';
51094         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
51095         
51096     },
51097
51098     /**
51099      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
51100      * will be displayed in the field.  If the value does not match the data value of an existing item,
51101      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
51102      * Otherwise the field will be blank (although the value will still be set).
51103      * @param {String} value The value to match
51104      */
51105     setValue : function(v){
51106         var d = this.el.dom;
51107         for (var i =0; i < d.options.length;i++) {
51108             if (v == d.options[i].value) {
51109                 d.selectedIndex = i;
51110                 this.value = v;
51111                 return;
51112             }
51113         }
51114         this.clearValue();
51115     },
51116     /**
51117      * @property {Object} the last set data for the element
51118      */
51119     
51120     lastData : false,
51121     /**
51122      * Sets the value of the field based on a object which is related to the record format for the store.
51123      * @param {Object} value the value to set as. or false on reset?
51124      */
51125     setFromData : function(o){
51126         Roo.log('setfrom data?');
51127          
51128         
51129         
51130     },
51131     // private
51132     reset : function(){
51133         this.clearValue();
51134     },
51135     // private
51136     findRecord : function(prop, value){
51137         
51138         return false;
51139     
51140         var record;
51141         if(this.store.getCount() > 0){
51142             this.store.each(function(r){
51143                 if(r.data[prop] == value){
51144                     record = r;
51145                     return false;
51146                 }
51147                 return true;
51148             });
51149         }
51150         return record;
51151     },
51152     
51153     getName: function()
51154     {
51155         // returns hidden if it's set..
51156         if (!this.rendered) {return ''};
51157         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
51158         
51159     },
51160      
51161
51162     
51163
51164     // private
51165     onEmptyResults : function(){
51166         Roo.log('empty results');
51167         //this.collapse();
51168     },
51169
51170     /**
51171      * Returns true if the dropdown list is expanded, else false.
51172      */
51173     isExpanded : function(){
51174         return false;
51175     },
51176
51177     /**
51178      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
51179      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
51180      * @param {String} value The data value of the item to select
51181      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
51182      * selected item if it is not currently in view (defaults to true)
51183      * @return {Boolean} True if the value matched an item in the list, else false
51184      */
51185     selectByValue : function(v, scrollIntoView){
51186         Roo.log('select By Value');
51187         return false;
51188     
51189         if(v !== undefined && v !== null){
51190             var r = this.findRecord(this.valueField || this.displayField, v);
51191             if(r){
51192                 this.select(this.store.indexOf(r), scrollIntoView);
51193                 return true;
51194             }
51195         }
51196         return false;
51197     },
51198
51199     /**
51200      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
51201      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
51202      * @param {Number} index The zero-based index of the list item to select
51203      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
51204      * selected item if it is not currently in view (defaults to true)
51205      */
51206     select : function(index, scrollIntoView){
51207         Roo.log('select ');
51208         return  ;
51209         
51210         this.selectedIndex = index;
51211         this.view.select(index);
51212         if(scrollIntoView !== false){
51213             var el = this.view.getNode(index);
51214             if(el){
51215                 this.innerList.scrollChildIntoView(el, false);
51216             }
51217         }
51218     },
51219
51220       
51221
51222     // private
51223     validateBlur : function(){
51224         
51225         return;
51226         
51227     },
51228
51229     // private
51230     initQuery : function(){
51231         this.doQuery(this.getRawValue());
51232     },
51233
51234     // private
51235     doForce : function(){
51236         if(this.el.dom.value.length > 0){
51237             this.el.dom.value =
51238                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
51239              
51240         }
51241     },
51242
51243     /**
51244      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
51245      * query allowing the query action to be canceled if needed.
51246      * @param {String} query The SQL query to execute
51247      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
51248      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
51249      * saved in the current store (defaults to false)
51250      */
51251     doQuery : function(q, forceAll){
51252         
51253         Roo.log('doQuery?');
51254         if(q === undefined || q === null){
51255             q = '';
51256         }
51257         var qe = {
51258             query: q,
51259             forceAll: forceAll,
51260             combo: this,
51261             cancel:false
51262         };
51263         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
51264             return false;
51265         }
51266         q = qe.query;
51267         forceAll = qe.forceAll;
51268         if(forceAll === true || (q.length >= this.minChars)){
51269             if(this.lastQuery != q || this.alwaysQuery){
51270                 this.lastQuery = q;
51271                 if(this.mode == 'local'){
51272                     this.selectedIndex = -1;
51273                     if(forceAll){
51274                         this.store.clearFilter();
51275                     }else{
51276                         this.store.filter(this.displayField, q);
51277                     }
51278                     this.onLoad();
51279                 }else{
51280                     this.store.baseParams[this.queryParam] = q;
51281                     this.store.load({
51282                         params: this.getParams(q)
51283                     });
51284                     this.expand();
51285                 }
51286             }else{
51287                 this.selectedIndex = -1;
51288                 this.onLoad();   
51289             }
51290         }
51291     },
51292
51293     // private
51294     getParams : function(q){
51295         var p = {};
51296         //p[this.queryParam] = q;
51297         if(this.pageSize){
51298             p.start = 0;
51299             p.limit = this.pageSize;
51300         }
51301         return p;
51302     },
51303
51304     /**
51305      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
51306      */
51307     collapse : function(){
51308         
51309     },
51310
51311     // private
51312     collapseIf : function(e){
51313         
51314     },
51315
51316     /**
51317      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
51318      */
51319     expand : function(){
51320         
51321     } ,
51322
51323     // private
51324      
51325
51326     /** 
51327     * @cfg {Boolean} grow 
51328     * @hide 
51329     */
51330     /** 
51331     * @cfg {Number} growMin 
51332     * @hide 
51333     */
51334     /** 
51335     * @cfg {Number} growMax 
51336     * @hide 
51337     */
51338     /**
51339      * @hide
51340      * @method autoSize
51341      */
51342     
51343     setWidth : function()
51344     {
51345         
51346     },
51347     getResizeEl : function(){
51348         return this.el;
51349     }
51350 });//<script type="text/javasscript">
51351  
51352
51353 /**
51354  * @class Roo.DDView
51355  * A DnD enabled version of Roo.View.
51356  * @param {Element/String} container The Element in which to create the View.
51357  * @param {String} tpl The template string used to create the markup for each element of the View
51358  * @param {Object} config The configuration properties. These include all the config options of
51359  * {@link Roo.View} plus some specific to this class.<br>
51360  * <p>
51361  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
51362  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
51363  * <p>
51364  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
51365 .x-view-drag-insert-above {
51366         border-top:1px dotted #3366cc;
51367 }
51368 .x-view-drag-insert-below {
51369         border-bottom:1px dotted #3366cc;
51370 }
51371 </code></pre>
51372  * 
51373  */
51374  
51375 Roo.DDView = function(container, tpl, config) {
51376     Roo.DDView.superclass.constructor.apply(this, arguments);
51377     this.getEl().setStyle("outline", "0px none");
51378     this.getEl().unselectable();
51379     if (this.dragGroup) {
51380                 this.setDraggable(this.dragGroup.split(","));
51381     }
51382     if (this.dropGroup) {
51383                 this.setDroppable(this.dropGroup.split(","));
51384     }
51385     if (this.deletable) {
51386         this.setDeletable();
51387     }
51388     this.isDirtyFlag = false;
51389         this.addEvents({
51390                 "drop" : true
51391         });
51392 };
51393
51394 Roo.extend(Roo.DDView, Roo.View, {
51395 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
51396 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
51397 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
51398 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
51399
51400         isFormField: true,
51401
51402         reset: Roo.emptyFn,
51403         
51404         clearInvalid: Roo.form.Field.prototype.clearInvalid,
51405
51406         validate: function() {
51407                 return true;
51408         },
51409         
51410         destroy: function() {
51411                 this.purgeListeners();
51412                 this.getEl.removeAllListeners();
51413                 this.getEl().remove();
51414                 if (this.dragZone) {
51415                         if (this.dragZone.destroy) {
51416                                 this.dragZone.destroy();
51417                         }
51418                 }
51419                 if (this.dropZone) {
51420                         if (this.dropZone.destroy) {
51421                                 this.dropZone.destroy();
51422                         }
51423                 }
51424         },
51425
51426 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
51427         getName: function() {
51428                 return this.name;
51429         },
51430
51431 /**     Loads the View from a JSON string representing the Records to put into the Store. */
51432         setValue: function(v) {
51433                 if (!this.store) {
51434                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
51435                 }
51436                 var data = {};
51437                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
51438                 this.store.proxy = new Roo.data.MemoryProxy(data);
51439                 this.store.load();
51440         },
51441
51442 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
51443         getValue: function() {
51444                 var result = '(';
51445                 this.store.each(function(rec) {
51446                         result += rec.id + ',';
51447                 });
51448                 return result.substr(0, result.length - 1) + ')';
51449         },
51450         
51451         getIds: function() {
51452                 var i = 0, result = new Array(this.store.getCount());
51453                 this.store.each(function(rec) {
51454                         result[i++] = rec.id;
51455                 });
51456                 return result;
51457         },
51458         
51459         isDirty: function() {
51460                 return this.isDirtyFlag;
51461         },
51462
51463 /**
51464  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
51465  *      whole Element becomes the target, and this causes the drop gesture to append.
51466  */
51467     getTargetFromEvent : function(e) {
51468                 var target = e.getTarget();
51469                 while ((target !== null) && (target.parentNode != this.el.dom)) {
51470                 target = target.parentNode;
51471                 }
51472                 if (!target) {
51473                         target = this.el.dom.lastChild || this.el.dom;
51474                 }
51475                 return target;
51476     },
51477
51478 /**
51479  *      Create the drag data which consists of an object which has the property "ddel" as
51480  *      the drag proxy element. 
51481  */
51482     getDragData : function(e) {
51483         var target = this.findItemFromChild(e.getTarget());
51484                 if(target) {
51485                         this.handleSelection(e);
51486                         var selNodes = this.getSelectedNodes();
51487             var dragData = {
51488                 source: this,
51489                 copy: this.copy || (this.allowCopy && e.ctrlKey),
51490                 nodes: selNodes,
51491                 records: []
51492                         };
51493                         var selectedIndices = this.getSelectedIndexes();
51494                         for (var i = 0; i < selectedIndices.length; i++) {
51495                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
51496                         }
51497                         if (selNodes.length == 1) {
51498                                 dragData.ddel = target.cloneNode(true); // the div element
51499                         } else {
51500                                 var div = document.createElement('div'); // create the multi element drag "ghost"
51501                                 div.className = 'multi-proxy';
51502                                 for (var i = 0, len = selNodes.length; i < len; i++) {
51503                                         div.appendChild(selNodes[i].cloneNode(true));
51504                                 }
51505                                 dragData.ddel = div;
51506                         }
51507             //console.log(dragData)
51508             //console.log(dragData.ddel.innerHTML)
51509                         return dragData;
51510                 }
51511         //console.log('nodragData')
51512                 return false;
51513     },
51514     
51515 /**     Specify to which ddGroup items in this DDView may be dragged. */
51516     setDraggable: function(ddGroup) {
51517         if (ddGroup instanceof Array) {
51518                 Roo.each(ddGroup, this.setDraggable, this);
51519                 return;
51520         }
51521         if (this.dragZone) {
51522                 this.dragZone.addToGroup(ddGroup);
51523         } else {
51524                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
51525                                 containerScroll: true,
51526                                 ddGroup: ddGroup 
51527
51528                         });
51529 //                      Draggability implies selection. DragZone's mousedown selects the element.
51530                         if (!this.multiSelect) { this.singleSelect = true; }
51531
51532 //                      Wire the DragZone's handlers up to methods in *this*
51533                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
51534                 }
51535     },
51536
51537 /**     Specify from which ddGroup this DDView accepts drops. */
51538     setDroppable: function(ddGroup) {
51539         if (ddGroup instanceof Array) {
51540                 Roo.each(ddGroup, this.setDroppable, this);
51541                 return;
51542         }
51543         if (this.dropZone) {
51544                 this.dropZone.addToGroup(ddGroup);
51545         } else {
51546                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
51547                                 containerScroll: true,
51548                                 ddGroup: ddGroup
51549                         });
51550
51551 //                      Wire the DropZone's handlers up to methods in *this*
51552                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
51553                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
51554                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
51555                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
51556                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
51557                 }
51558     },
51559
51560 /**     Decide whether to drop above or below a View node. */
51561     getDropPoint : function(e, n, dd){
51562         if (n == this.el.dom) { return "above"; }
51563                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
51564                 var c = t + (b - t) / 2;
51565                 var y = Roo.lib.Event.getPageY(e);
51566                 if(y <= c) {
51567                         return "above";
51568                 }else{
51569                         return "below";
51570                 }
51571     },
51572
51573     onNodeEnter : function(n, dd, e, data){
51574                 return false;
51575     },
51576     
51577     onNodeOver : function(n, dd, e, data){
51578                 var pt = this.getDropPoint(e, n, dd);
51579                 // set the insert point style on the target node
51580                 var dragElClass = this.dropNotAllowed;
51581                 if (pt) {
51582                         var targetElClass;
51583                         if (pt == "above"){
51584                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
51585                                 targetElClass = "x-view-drag-insert-above";
51586                         } else {
51587                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
51588                                 targetElClass = "x-view-drag-insert-below";
51589                         }
51590                         if (this.lastInsertClass != targetElClass){
51591                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
51592                                 this.lastInsertClass = targetElClass;
51593                         }
51594                 }
51595                 return dragElClass;
51596         },
51597
51598     onNodeOut : function(n, dd, e, data){
51599                 this.removeDropIndicators(n);
51600     },
51601
51602     onNodeDrop : function(n, dd, e, data){
51603         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
51604                 return false;
51605         }
51606         var pt = this.getDropPoint(e, n, dd);
51607                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
51608                 if (pt == "below") { insertAt++; }
51609                 for (var i = 0; i < data.records.length; i++) {
51610                         var r = data.records[i];
51611                         var dup = this.store.getById(r.id);
51612                         if (dup && (dd != this.dragZone)) {
51613                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
51614                         } else {
51615                                 if (data.copy) {
51616                                         this.store.insert(insertAt++, r.copy());
51617                                 } else {
51618                                         data.source.isDirtyFlag = true;
51619                                         r.store.remove(r);
51620                                         this.store.insert(insertAt++, r);
51621                                 }
51622                                 this.isDirtyFlag = true;
51623                         }
51624                 }
51625                 this.dragZone.cachedTarget = null;
51626                 return true;
51627     },
51628
51629     removeDropIndicators : function(n){
51630                 if(n){
51631                         Roo.fly(n).removeClass([
51632                                 "x-view-drag-insert-above",
51633                                 "x-view-drag-insert-below"]);
51634                         this.lastInsertClass = "_noclass";
51635                 }
51636     },
51637
51638 /**
51639  *      Utility method. Add a delete option to the DDView's context menu.
51640  *      @param {String} imageUrl The URL of the "delete" icon image.
51641  */
51642         setDeletable: function(imageUrl) {
51643                 if (!this.singleSelect && !this.multiSelect) {
51644                         this.singleSelect = true;
51645                 }
51646                 var c = this.getContextMenu();
51647                 this.contextMenu.on("itemclick", function(item) {
51648                         switch (item.id) {
51649                                 case "delete":
51650                                         this.remove(this.getSelectedIndexes());
51651                                         break;
51652                         }
51653                 }, this);
51654                 this.contextMenu.add({
51655                         icon: imageUrl,
51656                         id: "delete",
51657                         text: 'Delete'
51658                 });
51659         },
51660         
51661 /**     Return the context menu for this DDView. */
51662         getContextMenu: function() {
51663                 if (!this.contextMenu) {
51664 //                      Create the View's context menu
51665                         this.contextMenu = new Roo.menu.Menu({
51666                                 id: this.id + "-contextmenu"
51667                         });
51668                         this.el.on("contextmenu", this.showContextMenu, this);
51669                 }
51670                 return this.contextMenu;
51671         },
51672         
51673         disableContextMenu: function() {
51674                 if (this.contextMenu) {
51675                         this.el.un("contextmenu", this.showContextMenu, this);
51676                 }
51677         },
51678
51679         showContextMenu: function(e, item) {
51680         item = this.findItemFromChild(e.getTarget());
51681                 if (item) {
51682                         e.stopEvent();
51683                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
51684                         this.contextMenu.showAt(e.getXY());
51685             }
51686     },
51687
51688 /**
51689  *      Remove {@link Roo.data.Record}s at the specified indices.
51690  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
51691  */
51692     remove: function(selectedIndices) {
51693                 selectedIndices = [].concat(selectedIndices);
51694                 for (var i = 0; i < selectedIndices.length; i++) {
51695                         var rec = this.store.getAt(selectedIndices[i]);
51696                         this.store.remove(rec);
51697                 }
51698     },
51699
51700 /**
51701  *      Double click fires the event, but also, if this is draggable, and there is only one other
51702  *      related DropZone, it transfers the selected node.
51703  */
51704     onDblClick : function(e){
51705         var item = this.findItemFromChild(e.getTarget());
51706         if(item){
51707             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
51708                 return false;
51709             }
51710             if (this.dragGroup) {
51711                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
51712                     while (targets.indexOf(this.dropZone) > -1) {
51713                             targets.remove(this.dropZone);
51714                                 }
51715                     if (targets.length == 1) {
51716                                         this.dragZone.cachedTarget = null;
51717                         var el = Roo.get(targets[0].getEl());
51718                         var box = el.getBox(true);
51719                         targets[0].onNodeDrop(el.dom, {
51720                                 target: el.dom,
51721                                 xy: [box.x, box.y + box.height - 1]
51722                         }, null, this.getDragData(e));
51723                     }
51724                 }
51725         }
51726     },
51727     
51728     handleSelection: function(e) {
51729                 this.dragZone.cachedTarget = null;
51730         var item = this.findItemFromChild(e.getTarget());
51731         if (!item) {
51732                 this.clearSelections(true);
51733                 return;
51734         }
51735                 if (item && (this.multiSelect || this.singleSelect)){
51736                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
51737                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
51738                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
51739                                 this.unselect(item);
51740                         } else {
51741                                 this.select(item, this.multiSelect && e.ctrlKey);
51742                                 this.lastSelection = item;
51743                         }
51744                 }
51745     },
51746
51747     onItemClick : function(item, index, e){
51748                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
51749                         return false;
51750                 }
51751                 return true;
51752     },
51753
51754     unselect : function(nodeInfo, suppressEvent){
51755                 var node = this.getNode(nodeInfo);
51756                 if(node && this.isSelected(node)){
51757                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
51758                                 Roo.fly(node).removeClass(this.selectedClass);
51759                                 this.selections.remove(node);
51760                                 if(!suppressEvent){
51761                                         this.fireEvent("selectionchange", this, this.selections);
51762                                 }
51763                         }
51764                 }
51765     }
51766 });
51767 /*
51768  * Based on:
51769  * Ext JS Library 1.1.1
51770  * Copyright(c) 2006-2007, Ext JS, LLC.
51771  *
51772  * Originally Released Under LGPL - original licence link has changed is not relivant.
51773  *
51774  * Fork - LGPL
51775  * <script type="text/javascript">
51776  */
51777  
51778 /**
51779  * @class Roo.LayoutManager
51780  * @extends Roo.util.Observable
51781  * Base class for layout managers.
51782  */
51783 Roo.LayoutManager = function(container, config){
51784     Roo.LayoutManager.superclass.constructor.call(this);
51785     this.el = Roo.get(container);
51786     // ie scrollbar fix
51787     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
51788         document.body.scroll = "no";
51789     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
51790         this.el.position('relative');
51791     }
51792     this.id = this.el.id;
51793     this.el.addClass("x-layout-container");
51794     /** false to disable window resize monitoring @type Boolean */
51795     this.monitorWindowResize = true;
51796     this.regions = {};
51797     this.addEvents({
51798         /**
51799          * @event layout
51800          * Fires when a layout is performed. 
51801          * @param {Roo.LayoutManager} this
51802          */
51803         "layout" : true,
51804         /**
51805          * @event regionresized
51806          * Fires when the user resizes a region. 
51807          * @param {Roo.LayoutRegion} region The resized region
51808          * @param {Number} newSize The new size (width for east/west, height for north/south)
51809          */
51810         "regionresized" : true,
51811         /**
51812          * @event regioncollapsed
51813          * Fires when a region is collapsed. 
51814          * @param {Roo.LayoutRegion} region The collapsed region
51815          */
51816         "regioncollapsed" : true,
51817         /**
51818          * @event regionexpanded
51819          * Fires when a region is expanded.  
51820          * @param {Roo.LayoutRegion} region The expanded region
51821          */
51822         "regionexpanded" : true
51823     });
51824     this.updating = false;
51825     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
51826 };
51827
51828 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
51829     /**
51830      * Returns true if this layout is currently being updated
51831      * @return {Boolean}
51832      */
51833     isUpdating : function(){
51834         return this.updating; 
51835     },
51836     
51837     /**
51838      * Suspend the LayoutManager from doing auto-layouts while
51839      * making multiple add or remove calls
51840      */
51841     beginUpdate : function(){
51842         this.updating = true;    
51843     },
51844     
51845     /**
51846      * Restore auto-layouts and optionally disable the manager from performing a layout
51847      * @param {Boolean} noLayout true to disable a layout update 
51848      */
51849     endUpdate : function(noLayout){
51850         this.updating = false;
51851         if(!noLayout){
51852             this.layout();
51853         }    
51854     },
51855     
51856     layout: function(){
51857         
51858     },
51859     
51860     onRegionResized : function(region, newSize){
51861         this.fireEvent("regionresized", region, newSize);
51862         this.layout();
51863     },
51864     
51865     onRegionCollapsed : function(region){
51866         this.fireEvent("regioncollapsed", region);
51867     },
51868     
51869     onRegionExpanded : function(region){
51870         this.fireEvent("regionexpanded", region);
51871     },
51872         
51873     /**
51874      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
51875      * performs box-model adjustments.
51876      * @return {Object} The size as an object {width: (the width), height: (the height)}
51877      */
51878     getViewSize : function(){
51879         var size;
51880         if(this.el.dom != document.body){
51881             size = this.el.getSize();
51882         }else{
51883             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
51884         }
51885         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
51886         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
51887         return size;
51888     },
51889     
51890     /**
51891      * Returns the Element this layout is bound to.
51892      * @return {Roo.Element}
51893      */
51894     getEl : function(){
51895         return this.el;
51896     },
51897     
51898     /**
51899      * Returns the specified region.
51900      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
51901      * @return {Roo.LayoutRegion}
51902      */
51903     getRegion : function(target){
51904         return this.regions[target.toLowerCase()];
51905     },
51906     
51907     onWindowResize : function(){
51908         if(this.monitorWindowResize){
51909             this.layout();
51910         }
51911     }
51912 });/*
51913  * Based on:
51914  * Ext JS Library 1.1.1
51915  * Copyright(c) 2006-2007, Ext JS, LLC.
51916  *
51917  * Originally Released Under LGPL - original licence link has changed is not relivant.
51918  *
51919  * Fork - LGPL
51920  * <script type="text/javascript">
51921  */
51922 /**
51923  * @class Roo.BorderLayout
51924  * @extends Roo.LayoutManager
51925  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
51926  * please see: <br><br>
51927  * <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>
51928  * <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>
51929  * Example:
51930  <pre><code>
51931  var layout = new Roo.BorderLayout(document.body, {
51932     north: {
51933         initialSize: 25,
51934         titlebar: false
51935     },
51936     west: {
51937         split:true,
51938         initialSize: 200,
51939         minSize: 175,
51940         maxSize: 400,
51941         titlebar: true,
51942         collapsible: true
51943     },
51944     east: {
51945         split:true,
51946         initialSize: 202,
51947         minSize: 175,
51948         maxSize: 400,
51949         titlebar: true,
51950         collapsible: true
51951     },
51952     south: {
51953         split:true,
51954         initialSize: 100,
51955         minSize: 100,
51956         maxSize: 200,
51957         titlebar: true,
51958         collapsible: true
51959     },
51960     center: {
51961         titlebar: true,
51962         autoScroll:true,
51963         resizeTabs: true,
51964         minTabWidth: 50,
51965         preferredTabWidth: 150
51966     }
51967 });
51968
51969 // shorthand
51970 var CP = Roo.ContentPanel;
51971
51972 layout.beginUpdate();
51973 layout.add("north", new CP("north", "North"));
51974 layout.add("south", new CP("south", {title: "South", closable: true}));
51975 layout.add("west", new CP("west", {title: "West"}));
51976 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
51977 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
51978 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
51979 layout.getRegion("center").showPanel("center1");
51980 layout.endUpdate();
51981 </code></pre>
51982
51983 <b>The container the layout is rendered into can be either the body element or any other element.
51984 If it is not the body element, the container needs to either be an absolute positioned element,
51985 or you will need to add "position:relative" to the css of the container.  You will also need to specify
51986 the container size if it is not the body element.</b>
51987
51988 * @constructor
51989 * Create a new BorderLayout
51990 * @param {String/HTMLElement/Element} container The container this layout is bound to
51991 * @param {Object} config Configuration options
51992  */
51993 Roo.BorderLayout = function(container, config){
51994     config = config || {};
51995     Roo.BorderLayout.superclass.constructor.call(this, container, config);
51996     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
51997     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
51998         var target = this.factory.validRegions[i];
51999         if(config[target]){
52000             this.addRegion(target, config[target]);
52001         }
52002     }
52003 };
52004
52005 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
52006     /**
52007      * Creates and adds a new region if it doesn't already exist.
52008      * @param {String} target The target region key (north, south, east, west or center).
52009      * @param {Object} config The regions config object
52010      * @return {BorderLayoutRegion} The new region
52011      */
52012     addRegion : function(target, config){
52013         if(!this.regions[target]){
52014             var r = this.factory.create(target, this, config);
52015             this.bindRegion(target, r);
52016         }
52017         return this.regions[target];
52018     },
52019
52020     // private (kinda)
52021     bindRegion : function(name, r){
52022         this.regions[name] = r;
52023         r.on("visibilitychange", this.layout, this);
52024         r.on("paneladded", this.layout, this);
52025         r.on("panelremoved", this.layout, this);
52026         r.on("invalidated", this.layout, this);
52027         r.on("resized", this.onRegionResized, this);
52028         r.on("collapsed", this.onRegionCollapsed, this);
52029         r.on("expanded", this.onRegionExpanded, this);
52030     },
52031
52032     /**
52033      * Performs a layout update.
52034      */
52035     layout : function(){
52036         if(this.updating) {
52037             return;
52038         }
52039         var size = this.getViewSize();
52040         var w = size.width;
52041         var h = size.height;
52042         var centerW = w;
52043         var centerH = h;
52044         var centerY = 0;
52045         var centerX = 0;
52046         //var x = 0, y = 0;
52047
52048         var rs = this.regions;
52049         var north = rs["north"];
52050         var south = rs["south"]; 
52051         var west = rs["west"];
52052         var east = rs["east"];
52053         var center = rs["center"];
52054         //if(this.hideOnLayout){ // not supported anymore
52055             //c.el.setStyle("display", "none");
52056         //}
52057         if(north && north.isVisible()){
52058             var b = north.getBox();
52059             var m = north.getMargins();
52060             b.width = w - (m.left+m.right);
52061             b.x = m.left;
52062             b.y = m.top;
52063             centerY = b.height + b.y + m.bottom;
52064             centerH -= centerY;
52065             north.updateBox(this.safeBox(b));
52066         }
52067         if(south && south.isVisible()){
52068             var b = south.getBox();
52069             var m = south.getMargins();
52070             b.width = w - (m.left+m.right);
52071             b.x = m.left;
52072             var totalHeight = (b.height + m.top + m.bottom);
52073             b.y = h - totalHeight + m.top;
52074             centerH -= totalHeight;
52075             south.updateBox(this.safeBox(b));
52076         }
52077         if(west && west.isVisible()){
52078             var b = west.getBox();
52079             var m = west.getMargins();
52080             b.height = centerH - (m.top+m.bottom);
52081             b.x = m.left;
52082             b.y = centerY + m.top;
52083             var totalWidth = (b.width + m.left + m.right);
52084             centerX += totalWidth;
52085             centerW -= totalWidth;
52086             west.updateBox(this.safeBox(b));
52087         }
52088         if(east && east.isVisible()){
52089             var b = east.getBox();
52090             var m = east.getMargins();
52091             b.height = centerH - (m.top+m.bottom);
52092             var totalWidth = (b.width + m.left + m.right);
52093             b.x = w - totalWidth + m.left;
52094             b.y = centerY + m.top;
52095             centerW -= totalWidth;
52096             east.updateBox(this.safeBox(b));
52097         }
52098         if(center){
52099             var m = center.getMargins();
52100             var centerBox = {
52101                 x: centerX + m.left,
52102                 y: centerY + m.top,
52103                 width: centerW - (m.left+m.right),
52104                 height: centerH - (m.top+m.bottom)
52105             };
52106             //if(this.hideOnLayout){
52107                 //center.el.setStyle("display", "block");
52108             //}
52109             center.updateBox(this.safeBox(centerBox));
52110         }
52111         this.el.repaint();
52112         this.fireEvent("layout", this);
52113     },
52114
52115     // private
52116     safeBox : function(box){
52117         box.width = Math.max(0, box.width);
52118         box.height = Math.max(0, box.height);
52119         return box;
52120     },
52121
52122     /**
52123      * Adds a ContentPanel (or subclass) to this layout.
52124      * @param {String} target The target region key (north, south, east, west or center).
52125      * @param {Roo.ContentPanel} panel The panel to add
52126      * @return {Roo.ContentPanel} The added panel
52127      */
52128     add : function(target, panel){
52129          
52130         target = target.toLowerCase();
52131         return this.regions[target].add(panel);
52132     },
52133
52134     /**
52135      * Remove a ContentPanel (or subclass) to this layout.
52136      * @param {String} target The target region key (north, south, east, west or center).
52137      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
52138      * @return {Roo.ContentPanel} The removed panel
52139      */
52140     remove : function(target, panel){
52141         target = target.toLowerCase();
52142         return this.regions[target].remove(panel);
52143     },
52144
52145     /**
52146      * Searches all regions for a panel with the specified id
52147      * @param {String} panelId
52148      * @return {Roo.ContentPanel} The panel or null if it wasn't found
52149      */
52150     findPanel : function(panelId){
52151         var rs = this.regions;
52152         for(var target in rs){
52153             if(typeof rs[target] != "function"){
52154                 var p = rs[target].getPanel(panelId);
52155                 if(p){
52156                     return p;
52157                 }
52158             }
52159         }
52160         return null;
52161     },
52162
52163     /**
52164      * Searches all regions for a panel with the specified id and activates (shows) it.
52165      * @param {String/ContentPanel} panelId The panels id or the panel itself
52166      * @return {Roo.ContentPanel} The shown panel or null
52167      */
52168     showPanel : function(panelId) {
52169       var rs = this.regions;
52170       for(var target in rs){
52171          var r = rs[target];
52172          if(typeof r != "function"){
52173             if(r.hasPanel(panelId)){
52174                return r.showPanel(panelId);
52175             }
52176          }
52177       }
52178       return null;
52179    },
52180
52181    /**
52182      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
52183      * @param {Roo.state.Provider} provider (optional) An alternate state provider
52184      */
52185     restoreState : function(provider){
52186         if(!provider){
52187             provider = Roo.state.Manager;
52188         }
52189         var sm = new Roo.LayoutStateManager();
52190         sm.init(this, provider);
52191     },
52192
52193     /**
52194      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
52195      * object should contain properties for each region to add ContentPanels to, and each property's value should be
52196      * a valid ContentPanel config object.  Example:
52197      * <pre><code>
52198 // Create the main layout
52199 var layout = new Roo.BorderLayout('main-ct', {
52200     west: {
52201         split:true,
52202         minSize: 175,
52203         titlebar: true
52204     },
52205     center: {
52206         title:'Components'
52207     }
52208 }, 'main-ct');
52209
52210 // Create and add multiple ContentPanels at once via configs
52211 layout.batchAdd({
52212    west: {
52213        id: 'source-files',
52214        autoCreate:true,
52215        title:'Ext Source Files',
52216        autoScroll:true,
52217        fitToFrame:true
52218    },
52219    center : {
52220        el: cview,
52221        autoScroll:true,
52222        fitToFrame:true,
52223        toolbar: tb,
52224        resizeEl:'cbody'
52225    }
52226 });
52227 </code></pre>
52228      * @param {Object} regions An object containing ContentPanel configs by region name
52229      */
52230     batchAdd : function(regions){
52231         this.beginUpdate();
52232         for(var rname in regions){
52233             var lr = this.regions[rname];
52234             if(lr){
52235                 this.addTypedPanels(lr, regions[rname]);
52236             }
52237         }
52238         this.endUpdate();
52239     },
52240
52241     // private
52242     addTypedPanels : function(lr, ps){
52243         if(typeof ps == 'string'){
52244             lr.add(new Roo.ContentPanel(ps));
52245         }
52246         else if(ps instanceof Array){
52247             for(var i =0, len = ps.length; i < len; i++){
52248                 this.addTypedPanels(lr, ps[i]);
52249             }
52250         }
52251         else if(!ps.events){ // raw config?
52252             var el = ps.el;
52253             delete ps.el; // prevent conflict
52254             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
52255         }
52256         else {  // panel object assumed!
52257             lr.add(ps);
52258         }
52259     },
52260     /**
52261      * Adds a xtype elements to the layout.
52262      * <pre><code>
52263
52264 layout.addxtype({
52265        xtype : 'ContentPanel',
52266        region: 'west',
52267        items: [ .... ]
52268    }
52269 );
52270
52271 layout.addxtype({
52272         xtype : 'NestedLayoutPanel',
52273         region: 'west',
52274         layout: {
52275            center: { },
52276            west: { }   
52277         },
52278         items : [ ... list of content panels or nested layout panels.. ]
52279    }
52280 );
52281 </code></pre>
52282      * @param {Object} cfg Xtype definition of item to add.
52283      */
52284     addxtype : function(cfg)
52285     {
52286         // basically accepts a pannel...
52287         // can accept a layout region..!?!?
52288         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
52289         
52290         if (!cfg.xtype.match(/Panel$/)) {
52291             return false;
52292         }
52293         var ret = false;
52294         
52295         if (typeof(cfg.region) == 'undefined') {
52296             Roo.log("Failed to add Panel, region was not set");
52297             Roo.log(cfg);
52298             return false;
52299         }
52300         var region = cfg.region;
52301         delete cfg.region;
52302         
52303           
52304         var xitems = [];
52305         if (cfg.items) {
52306             xitems = cfg.items;
52307             delete cfg.items;
52308         }
52309         var nb = false;
52310         
52311         switch(cfg.xtype) 
52312         {
52313             case 'ContentPanel':  // ContentPanel (el, cfg)
52314             case 'ScrollPanel':  // ContentPanel (el, cfg)
52315             case 'ViewPanel': 
52316                 if(cfg.autoCreate) {
52317                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
52318                 } else {
52319                     var el = this.el.createChild();
52320                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
52321                 }
52322                 
52323                 this.add(region, ret);
52324                 break;
52325             
52326             
52327             case 'TreePanel': // our new panel!
52328                 cfg.el = this.el.createChild();
52329                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
52330                 this.add(region, ret);
52331                 break;
52332             
52333             case 'NestedLayoutPanel': 
52334                 // create a new Layout (which is  a Border Layout...
52335                 var el = this.el.createChild();
52336                 var clayout = cfg.layout;
52337                 delete cfg.layout;
52338                 clayout.items   = clayout.items  || [];
52339                 // replace this exitems with the clayout ones..
52340                 xitems = clayout.items;
52341                  
52342                 
52343                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
52344                     cfg.background = false;
52345                 }
52346                 var layout = new Roo.BorderLayout(el, clayout);
52347                 
52348                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
52349                 //console.log('adding nested layout panel '  + cfg.toSource());
52350                 this.add(region, ret);
52351                 nb = {}; /// find first...
52352                 break;
52353                 
52354             case 'GridPanel': 
52355             
52356                 // needs grid and region
52357                 
52358                 //var el = this.getRegion(region).el.createChild();
52359                 var el = this.el.createChild();
52360                 // create the grid first...
52361                 
52362                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
52363                 delete cfg.grid;
52364                 if (region == 'center' && this.active ) {
52365                     cfg.background = false;
52366                 }
52367                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
52368                 
52369                 this.add(region, ret);
52370                 if (cfg.background) {
52371                     ret.on('activate', function(gp) {
52372                         if (!gp.grid.rendered) {
52373                             gp.grid.render();
52374                         }
52375                     });
52376                 } else {
52377                     grid.render();
52378                 }
52379                 break;
52380            
52381            
52382            
52383                 
52384                 
52385                 
52386             default:
52387                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
52388                     
52389                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
52390                     this.add(region, ret);
52391                 } else {
52392                 
52393                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
52394                     return null;
52395                 }
52396                 
52397              // GridPanel (grid, cfg)
52398             
52399         }
52400         this.beginUpdate();
52401         // add children..
52402         var region = '';
52403         var abn = {};
52404         Roo.each(xitems, function(i)  {
52405             region = nb && i.region ? i.region : false;
52406             
52407             var add = ret.addxtype(i);
52408            
52409             if (region) {
52410                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
52411                 if (!i.background) {
52412                     abn[region] = nb[region] ;
52413                 }
52414             }
52415             
52416         });
52417         this.endUpdate();
52418
52419         // make the last non-background panel active..
52420         //if (nb) { Roo.log(abn); }
52421         if (nb) {
52422             
52423             for(var r in abn) {
52424                 region = this.getRegion(r);
52425                 if (region) {
52426                     // tried using nb[r], but it does not work..
52427                      
52428                     region.showPanel(abn[r]);
52429                    
52430                 }
52431             }
52432         }
52433         return ret;
52434         
52435     }
52436 });
52437
52438 /**
52439  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
52440  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
52441  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
52442  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
52443  * <pre><code>
52444 // shorthand
52445 var CP = Roo.ContentPanel;
52446
52447 var layout = Roo.BorderLayout.create({
52448     north: {
52449         initialSize: 25,
52450         titlebar: false,
52451         panels: [new CP("north", "North")]
52452     },
52453     west: {
52454         split:true,
52455         initialSize: 200,
52456         minSize: 175,
52457         maxSize: 400,
52458         titlebar: true,
52459         collapsible: true,
52460         panels: [new CP("west", {title: "West"})]
52461     },
52462     east: {
52463         split:true,
52464         initialSize: 202,
52465         minSize: 175,
52466         maxSize: 400,
52467         titlebar: true,
52468         collapsible: true,
52469         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
52470     },
52471     south: {
52472         split:true,
52473         initialSize: 100,
52474         minSize: 100,
52475         maxSize: 200,
52476         titlebar: true,
52477         collapsible: true,
52478         panels: [new CP("south", {title: "South", closable: true})]
52479     },
52480     center: {
52481         titlebar: true,
52482         autoScroll:true,
52483         resizeTabs: true,
52484         minTabWidth: 50,
52485         preferredTabWidth: 150,
52486         panels: [
52487             new CP("center1", {title: "Close Me", closable: true}),
52488             new CP("center2", {title: "Center Panel", closable: false})
52489         ]
52490     }
52491 }, document.body);
52492
52493 layout.getRegion("center").showPanel("center1");
52494 </code></pre>
52495  * @param config
52496  * @param targetEl
52497  */
52498 Roo.BorderLayout.create = function(config, targetEl){
52499     var layout = new Roo.BorderLayout(targetEl || document.body, config);
52500     layout.beginUpdate();
52501     var regions = Roo.BorderLayout.RegionFactory.validRegions;
52502     for(var j = 0, jlen = regions.length; j < jlen; j++){
52503         var lr = regions[j];
52504         if(layout.regions[lr] && config[lr].panels){
52505             var r = layout.regions[lr];
52506             var ps = config[lr].panels;
52507             layout.addTypedPanels(r, ps);
52508         }
52509     }
52510     layout.endUpdate();
52511     return layout;
52512 };
52513
52514 // private
52515 Roo.BorderLayout.RegionFactory = {
52516     // private
52517     validRegions : ["north","south","east","west","center"],
52518
52519     // private
52520     create : function(target, mgr, config){
52521         target = target.toLowerCase();
52522         if(config.lightweight || config.basic){
52523             return new Roo.BasicLayoutRegion(mgr, config, target);
52524         }
52525         switch(target){
52526             case "north":
52527                 return new Roo.NorthLayoutRegion(mgr, config);
52528             case "south":
52529                 return new Roo.SouthLayoutRegion(mgr, config);
52530             case "east":
52531                 return new Roo.EastLayoutRegion(mgr, config);
52532             case "west":
52533                 return new Roo.WestLayoutRegion(mgr, config);
52534             case "center":
52535                 return new Roo.CenterLayoutRegion(mgr, config);
52536         }
52537         throw 'Layout region "'+target+'" not supported.';
52538     }
52539 };/*
52540  * Based on:
52541  * Ext JS Library 1.1.1
52542  * Copyright(c) 2006-2007, Ext JS, LLC.
52543  *
52544  * Originally Released Under LGPL - original licence link has changed is not relivant.
52545  *
52546  * Fork - LGPL
52547  * <script type="text/javascript">
52548  */
52549  
52550 /**
52551  * @class Roo.BasicLayoutRegion
52552  * @extends Roo.util.Observable
52553  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
52554  * and does not have a titlebar, tabs or any other features. All it does is size and position 
52555  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
52556  */
52557 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
52558     this.mgr = mgr;
52559     this.position  = pos;
52560     this.events = {
52561         /**
52562          * @scope Roo.BasicLayoutRegion
52563          */
52564         
52565         /**
52566          * @event beforeremove
52567          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
52568          * @param {Roo.LayoutRegion} this
52569          * @param {Roo.ContentPanel} panel The panel
52570          * @param {Object} e The cancel event object
52571          */
52572         "beforeremove" : true,
52573         /**
52574          * @event invalidated
52575          * Fires when the layout for this region is changed.
52576          * @param {Roo.LayoutRegion} this
52577          */
52578         "invalidated" : true,
52579         /**
52580          * @event visibilitychange
52581          * Fires when this region is shown or hidden 
52582          * @param {Roo.LayoutRegion} this
52583          * @param {Boolean} visibility true or false
52584          */
52585         "visibilitychange" : true,
52586         /**
52587          * @event paneladded
52588          * Fires when a panel is added. 
52589          * @param {Roo.LayoutRegion} this
52590          * @param {Roo.ContentPanel} panel The panel
52591          */
52592         "paneladded" : true,
52593         /**
52594          * @event panelremoved
52595          * Fires when a panel is removed. 
52596          * @param {Roo.LayoutRegion} this
52597          * @param {Roo.ContentPanel} panel The panel
52598          */
52599         "panelremoved" : true,
52600         /**
52601          * @event beforecollapse
52602          * Fires when this region before collapse.
52603          * @param {Roo.LayoutRegion} this
52604          */
52605         "beforecollapse" : true,
52606         /**
52607          * @event collapsed
52608          * Fires when this region is collapsed.
52609          * @param {Roo.LayoutRegion} this
52610          */
52611         "collapsed" : true,
52612         /**
52613          * @event expanded
52614          * Fires when this region is expanded.
52615          * @param {Roo.LayoutRegion} this
52616          */
52617         "expanded" : true,
52618         /**
52619          * @event slideshow
52620          * Fires when this region is slid into view.
52621          * @param {Roo.LayoutRegion} this
52622          */
52623         "slideshow" : true,
52624         /**
52625          * @event slidehide
52626          * Fires when this region slides out of view. 
52627          * @param {Roo.LayoutRegion} this
52628          */
52629         "slidehide" : true,
52630         /**
52631          * @event panelactivated
52632          * Fires when a panel is activated. 
52633          * @param {Roo.LayoutRegion} this
52634          * @param {Roo.ContentPanel} panel The activated panel
52635          */
52636         "panelactivated" : true,
52637         /**
52638          * @event resized
52639          * Fires when the user resizes this region. 
52640          * @param {Roo.LayoutRegion} this
52641          * @param {Number} newSize The new size (width for east/west, height for north/south)
52642          */
52643         "resized" : true
52644     };
52645     /** A collection of panels in this region. @type Roo.util.MixedCollection */
52646     this.panels = new Roo.util.MixedCollection();
52647     this.panels.getKey = this.getPanelId.createDelegate(this);
52648     this.box = null;
52649     this.activePanel = null;
52650     // ensure listeners are added...
52651     
52652     if (config.listeners || config.events) {
52653         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
52654             listeners : config.listeners || {},
52655             events : config.events || {}
52656         });
52657     }
52658     
52659     if(skipConfig !== true){
52660         this.applyConfig(config);
52661     }
52662 };
52663
52664 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
52665     getPanelId : function(p){
52666         return p.getId();
52667     },
52668     
52669     applyConfig : function(config){
52670         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
52671         this.config = config;
52672         
52673     },
52674     
52675     /**
52676      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
52677      * the width, for horizontal (north, south) the height.
52678      * @param {Number} newSize The new width or height
52679      */
52680     resizeTo : function(newSize){
52681         var el = this.el ? this.el :
52682                  (this.activePanel ? this.activePanel.getEl() : null);
52683         if(el){
52684             switch(this.position){
52685                 case "east":
52686                 case "west":
52687                     el.setWidth(newSize);
52688                     this.fireEvent("resized", this, newSize);
52689                 break;
52690                 case "north":
52691                 case "south":
52692                     el.setHeight(newSize);
52693                     this.fireEvent("resized", this, newSize);
52694                 break;                
52695             }
52696         }
52697     },
52698     
52699     getBox : function(){
52700         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
52701     },
52702     
52703     getMargins : function(){
52704         return this.margins;
52705     },
52706     
52707     updateBox : function(box){
52708         this.box = box;
52709         var el = this.activePanel.getEl();
52710         el.dom.style.left = box.x + "px";
52711         el.dom.style.top = box.y + "px";
52712         this.activePanel.setSize(box.width, box.height);
52713     },
52714     
52715     /**
52716      * Returns the container element for this region.
52717      * @return {Roo.Element}
52718      */
52719     getEl : function(){
52720         return this.activePanel;
52721     },
52722     
52723     /**
52724      * Returns true if this region is currently visible.
52725      * @return {Boolean}
52726      */
52727     isVisible : function(){
52728         return this.activePanel ? true : false;
52729     },
52730     
52731     setActivePanel : function(panel){
52732         panel = this.getPanel(panel);
52733         if(this.activePanel && this.activePanel != panel){
52734             this.activePanel.setActiveState(false);
52735             this.activePanel.getEl().setLeftTop(-10000,-10000);
52736         }
52737         this.activePanel = panel;
52738         panel.setActiveState(true);
52739         if(this.box){
52740             panel.setSize(this.box.width, this.box.height);
52741         }
52742         this.fireEvent("panelactivated", this, panel);
52743         this.fireEvent("invalidated");
52744     },
52745     
52746     /**
52747      * Show the specified panel.
52748      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
52749      * @return {Roo.ContentPanel} The shown panel or null
52750      */
52751     showPanel : function(panel){
52752         if(panel = this.getPanel(panel)){
52753             this.setActivePanel(panel);
52754         }
52755         return panel;
52756     },
52757     
52758     /**
52759      * Get the active panel for this region.
52760      * @return {Roo.ContentPanel} The active panel or null
52761      */
52762     getActivePanel : function(){
52763         return this.activePanel;
52764     },
52765     
52766     /**
52767      * Add the passed ContentPanel(s)
52768      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
52769      * @return {Roo.ContentPanel} The panel added (if only one was added)
52770      */
52771     add : function(panel){
52772         if(arguments.length > 1){
52773             for(var i = 0, len = arguments.length; i < len; i++) {
52774                 this.add(arguments[i]);
52775             }
52776             return null;
52777         }
52778         if(this.hasPanel(panel)){
52779             this.showPanel(panel);
52780             return panel;
52781         }
52782         var el = panel.getEl();
52783         if(el.dom.parentNode != this.mgr.el.dom){
52784             this.mgr.el.dom.appendChild(el.dom);
52785         }
52786         if(panel.setRegion){
52787             panel.setRegion(this);
52788         }
52789         this.panels.add(panel);
52790         el.setStyle("position", "absolute");
52791         if(!panel.background){
52792             this.setActivePanel(panel);
52793             if(this.config.initialSize && this.panels.getCount()==1){
52794                 this.resizeTo(this.config.initialSize);
52795             }
52796         }
52797         this.fireEvent("paneladded", this, panel);
52798         return panel;
52799     },
52800     
52801     /**
52802      * Returns true if the panel is in this region.
52803      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
52804      * @return {Boolean}
52805      */
52806     hasPanel : function(panel){
52807         if(typeof panel == "object"){ // must be panel obj
52808             panel = panel.getId();
52809         }
52810         return this.getPanel(panel) ? true : false;
52811     },
52812     
52813     /**
52814      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
52815      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
52816      * @param {Boolean} preservePanel Overrides the config preservePanel option
52817      * @return {Roo.ContentPanel} The panel that was removed
52818      */
52819     remove : function(panel, preservePanel){
52820         panel = this.getPanel(panel);
52821         if(!panel){
52822             return null;
52823         }
52824         var e = {};
52825         this.fireEvent("beforeremove", this, panel, e);
52826         if(e.cancel === true){
52827             return null;
52828         }
52829         var panelId = panel.getId();
52830         this.panels.removeKey(panelId);
52831         return panel;
52832     },
52833     
52834     /**
52835      * Returns the panel specified or null if it's not in this region.
52836      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
52837      * @return {Roo.ContentPanel}
52838      */
52839     getPanel : function(id){
52840         if(typeof id == "object"){ // must be panel obj
52841             return id;
52842         }
52843         return this.panels.get(id);
52844     },
52845     
52846     /**
52847      * Returns this regions position (north/south/east/west/center).
52848      * @return {String} 
52849      */
52850     getPosition: function(){
52851         return this.position;    
52852     }
52853 });/*
52854  * Based on:
52855  * Ext JS Library 1.1.1
52856  * Copyright(c) 2006-2007, Ext JS, LLC.
52857  *
52858  * Originally Released Under LGPL - original licence link has changed is not relivant.
52859  *
52860  * Fork - LGPL
52861  * <script type="text/javascript">
52862  */
52863  
52864 /**
52865  * @class Roo.LayoutRegion
52866  * @extends Roo.BasicLayoutRegion
52867  * This class represents a region in a layout manager.
52868  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
52869  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
52870  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
52871  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
52872  * @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})
52873  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
52874  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
52875  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
52876  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
52877  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
52878  * @cfg {String}    title           The title for the region (overrides panel titles)
52879  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
52880  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
52881  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
52882  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
52883  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
52884  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
52885  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
52886  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
52887  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
52888  * @cfg {Boolean}   showPin         True to show a pin button
52889  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
52890  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
52891  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
52892  * @cfg {Number}    width           For East/West panels
52893  * @cfg {Number}    height          For North/South panels
52894  * @cfg {Boolean}   split           To show the splitter
52895  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
52896  */
52897 Roo.LayoutRegion = function(mgr, config, pos){
52898     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
52899     var dh = Roo.DomHelper;
52900     /** This region's container element 
52901     * @type Roo.Element */
52902     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
52903     /** This region's title element 
52904     * @type Roo.Element */
52905
52906     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
52907         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
52908         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
52909     ]}, true);
52910     this.titleEl.enableDisplayMode();
52911     /** This region's title text element 
52912     * @type HTMLElement */
52913     this.titleTextEl = this.titleEl.dom.firstChild;
52914     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
52915     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
52916     this.closeBtn.enableDisplayMode();
52917     this.closeBtn.on("click", this.closeClicked, this);
52918     this.closeBtn.hide();
52919
52920     this.createBody(config);
52921     this.visible = true;
52922     this.collapsed = false;
52923
52924     if(config.hideWhenEmpty){
52925         this.hide();
52926         this.on("paneladded", this.validateVisibility, this);
52927         this.on("panelremoved", this.validateVisibility, this);
52928     }
52929     this.applyConfig(config);
52930 };
52931
52932 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
52933
52934     createBody : function(){
52935         /** This region's body element 
52936         * @type Roo.Element */
52937         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
52938     },
52939
52940     applyConfig : function(c){
52941         if(c.collapsible && this.position != "center" && !this.collapsedEl){
52942             var dh = Roo.DomHelper;
52943             if(c.titlebar !== false){
52944                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
52945                 this.collapseBtn.on("click", this.collapse, this);
52946                 this.collapseBtn.enableDisplayMode();
52947
52948                 if(c.showPin === true || this.showPin){
52949                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
52950                     this.stickBtn.enableDisplayMode();
52951                     this.stickBtn.on("click", this.expand, this);
52952                     this.stickBtn.hide();
52953                 }
52954             }
52955             /** This region's collapsed element
52956             * @type Roo.Element */
52957             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
52958                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
52959             ]}, true);
52960             if(c.floatable !== false){
52961                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
52962                this.collapsedEl.on("click", this.collapseClick, this);
52963             }
52964
52965             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
52966                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
52967                    id: "message", unselectable: "on", style:{"float":"left"}});
52968                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
52969              }
52970             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
52971             this.expandBtn.on("click", this.expand, this);
52972         }
52973         if(this.collapseBtn){
52974             this.collapseBtn.setVisible(c.collapsible == true);
52975         }
52976         this.cmargins = c.cmargins || this.cmargins ||
52977                          (this.position == "west" || this.position == "east" ?
52978                              {top: 0, left: 2, right:2, bottom: 0} :
52979                              {top: 2, left: 0, right:0, bottom: 2});
52980         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
52981         this.bottomTabs = c.tabPosition != "top";
52982         this.autoScroll = c.autoScroll || false;
52983         if(this.autoScroll){
52984             this.bodyEl.setStyle("overflow", "auto");
52985         }else{
52986             this.bodyEl.setStyle("overflow", "hidden");
52987         }
52988         //if(c.titlebar !== false){
52989             if((!c.titlebar && !c.title) || c.titlebar === false){
52990                 this.titleEl.hide();
52991             }else{
52992                 this.titleEl.show();
52993                 if(c.title){
52994                     this.titleTextEl.innerHTML = c.title;
52995                 }
52996             }
52997         //}
52998         this.duration = c.duration || .30;
52999         this.slideDuration = c.slideDuration || .45;
53000         this.config = c;
53001         if(c.collapsed){
53002             this.collapse(true);
53003         }
53004         if(c.hidden){
53005             this.hide();
53006         }
53007     },
53008     /**
53009      * Returns true if this region is currently visible.
53010      * @return {Boolean}
53011      */
53012     isVisible : function(){
53013         return this.visible;
53014     },
53015
53016     /**
53017      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
53018      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
53019      */
53020     setCollapsedTitle : function(title){
53021         title = title || "&#160;";
53022         if(this.collapsedTitleTextEl){
53023             this.collapsedTitleTextEl.innerHTML = title;
53024         }
53025     },
53026
53027     getBox : function(){
53028         var b;
53029         if(!this.collapsed){
53030             b = this.el.getBox(false, true);
53031         }else{
53032             b = this.collapsedEl.getBox(false, true);
53033         }
53034         return b;
53035     },
53036
53037     getMargins : function(){
53038         return this.collapsed ? this.cmargins : this.margins;
53039     },
53040
53041     highlight : function(){
53042         this.el.addClass("x-layout-panel-dragover");
53043     },
53044
53045     unhighlight : function(){
53046         this.el.removeClass("x-layout-panel-dragover");
53047     },
53048
53049     updateBox : function(box){
53050         this.box = box;
53051         if(!this.collapsed){
53052             this.el.dom.style.left = box.x + "px";
53053             this.el.dom.style.top = box.y + "px";
53054             this.updateBody(box.width, box.height);
53055         }else{
53056             this.collapsedEl.dom.style.left = box.x + "px";
53057             this.collapsedEl.dom.style.top = box.y + "px";
53058             this.collapsedEl.setSize(box.width, box.height);
53059         }
53060         if(this.tabs){
53061             this.tabs.autoSizeTabs();
53062         }
53063     },
53064
53065     updateBody : function(w, h){
53066         if(w !== null){
53067             this.el.setWidth(w);
53068             w -= this.el.getBorderWidth("rl");
53069             if(this.config.adjustments){
53070                 w += this.config.adjustments[0];
53071             }
53072         }
53073         if(h !== null){
53074             this.el.setHeight(h);
53075             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
53076             h -= this.el.getBorderWidth("tb");
53077             if(this.config.adjustments){
53078                 h += this.config.adjustments[1];
53079             }
53080             this.bodyEl.setHeight(h);
53081             if(this.tabs){
53082                 h = this.tabs.syncHeight(h);
53083             }
53084         }
53085         if(this.panelSize){
53086             w = w !== null ? w : this.panelSize.width;
53087             h = h !== null ? h : this.panelSize.height;
53088         }
53089         if(this.activePanel){
53090             var el = this.activePanel.getEl();
53091             w = w !== null ? w : el.getWidth();
53092             h = h !== null ? h : el.getHeight();
53093             this.panelSize = {width: w, height: h};
53094             this.activePanel.setSize(w, h);
53095         }
53096         if(Roo.isIE && this.tabs){
53097             this.tabs.el.repaint();
53098         }
53099     },
53100
53101     /**
53102      * Returns the container element for this region.
53103      * @return {Roo.Element}
53104      */
53105     getEl : function(){
53106         return this.el;
53107     },
53108
53109     /**
53110      * Hides this region.
53111      */
53112     hide : function(){
53113         if(!this.collapsed){
53114             this.el.dom.style.left = "-2000px";
53115             this.el.hide();
53116         }else{
53117             this.collapsedEl.dom.style.left = "-2000px";
53118             this.collapsedEl.hide();
53119         }
53120         this.visible = false;
53121         this.fireEvent("visibilitychange", this, false);
53122     },
53123
53124     /**
53125      * Shows this region if it was previously hidden.
53126      */
53127     show : function(){
53128         if(!this.collapsed){
53129             this.el.show();
53130         }else{
53131             this.collapsedEl.show();
53132         }
53133         this.visible = true;
53134         this.fireEvent("visibilitychange", this, true);
53135     },
53136
53137     closeClicked : function(){
53138         if(this.activePanel){
53139             this.remove(this.activePanel);
53140         }
53141     },
53142
53143     collapseClick : function(e){
53144         if(this.isSlid){
53145            e.stopPropagation();
53146            this.slideIn();
53147         }else{
53148            e.stopPropagation();
53149            this.slideOut();
53150         }
53151     },
53152
53153     /**
53154      * Collapses this region.
53155      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
53156      */
53157     collapse : function(skipAnim, skipCheck){
53158         if(this.collapsed) {
53159             return;
53160         }
53161         
53162         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
53163             
53164             this.collapsed = true;
53165             if(this.split){
53166                 this.split.el.hide();
53167             }
53168             if(this.config.animate && skipAnim !== true){
53169                 this.fireEvent("invalidated", this);
53170                 this.animateCollapse();
53171             }else{
53172                 this.el.setLocation(-20000,-20000);
53173                 this.el.hide();
53174                 this.collapsedEl.show();
53175                 this.fireEvent("collapsed", this);
53176                 this.fireEvent("invalidated", this);
53177             }
53178         }
53179         
53180     },
53181
53182     animateCollapse : function(){
53183         // overridden
53184     },
53185
53186     /**
53187      * Expands this region if it was previously collapsed.
53188      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
53189      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
53190      */
53191     expand : function(e, skipAnim){
53192         if(e) {
53193             e.stopPropagation();
53194         }
53195         if(!this.collapsed || this.el.hasActiveFx()) {
53196             return;
53197         }
53198         if(this.isSlid){
53199             this.afterSlideIn();
53200             skipAnim = true;
53201         }
53202         this.collapsed = false;
53203         if(this.config.animate && skipAnim !== true){
53204             this.animateExpand();
53205         }else{
53206             this.el.show();
53207             if(this.split){
53208                 this.split.el.show();
53209             }
53210             this.collapsedEl.setLocation(-2000,-2000);
53211             this.collapsedEl.hide();
53212             this.fireEvent("invalidated", this);
53213             this.fireEvent("expanded", this);
53214         }
53215     },
53216
53217     animateExpand : function(){
53218         // overridden
53219     },
53220
53221     initTabs : function()
53222     {
53223         this.bodyEl.setStyle("overflow", "hidden");
53224         var ts = new Roo.TabPanel(
53225                 this.bodyEl.dom,
53226                 {
53227                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
53228                     disableTooltips: this.config.disableTabTips,
53229                     toolbar : this.config.toolbar
53230                 }
53231         );
53232         if(this.config.hideTabs){
53233             ts.stripWrap.setDisplayed(false);
53234         }
53235         this.tabs = ts;
53236         ts.resizeTabs = this.config.resizeTabs === true;
53237         ts.minTabWidth = this.config.minTabWidth || 40;
53238         ts.maxTabWidth = this.config.maxTabWidth || 250;
53239         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
53240         ts.monitorResize = false;
53241         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
53242         ts.bodyEl.addClass('x-layout-tabs-body');
53243         this.panels.each(this.initPanelAsTab, this);
53244     },
53245
53246     initPanelAsTab : function(panel){
53247         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
53248                     this.config.closeOnTab && panel.isClosable());
53249         if(panel.tabTip !== undefined){
53250             ti.setTooltip(panel.tabTip);
53251         }
53252         ti.on("activate", function(){
53253               this.setActivePanel(panel);
53254         }, this);
53255         if(this.config.closeOnTab){
53256             ti.on("beforeclose", function(t, e){
53257                 e.cancel = true;
53258                 this.remove(panel);
53259             }, this);
53260         }
53261         return ti;
53262     },
53263
53264     updatePanelTitle : function(panel, title){
53265         if(this.activePanel == panel){
53266             this.updateTitle(title);
53267         }
53268         if(this.tabs){
53269             var ti = this.tabs.getTab(panel.getEl().id);
53270             ti.setText(title);
53271             if(panel.tabTip !== undefined){
53272                 ti.setTooltip(panel.tabTip);
53273             }
53274         }
53275     },
53276
53277     updateTitle : function(title){
53278         if(this.titleTextEl && !this.config.title){
53279             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
53280         }
53281     },
53282
53283     setActivePanel : function(panel){
53284         panel = this.getPanel(panel);
53285         if(this.activePanel && this.activePanel != panel){
53286             this.activePanel.setActiveState(false);
53287         }
53288         this.activePanel = panel;
53289         panel.setActiveState(true);
53290         if(this.panelSize){
53291             panel.setSize(this.panelSize.width, this.panelSize.height);
53292         }
53293         if(this.closeBtn){
53294             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
53295         }
53296         this.updateTitle(panel.getTitle());
53297         if(this.tabs){
53298             this.fireEvent("invalidated", this);
53299         }
53300         this.fireEvent("panelactivated", this, panel);
53301     },
53302
53303     /**
53304      * Shows the specified panel.
53305      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
53306      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
53307      */
53308     showPanel : function(panel)
53309     {
53310         panel = this.getPanel(panel);
53311         if(panel){
53312             if(this.tabs){
53313                 var tab = this.tabs.getTab(panel.getEl().id);
53314                 if(tab.isHidden()){
53315                     this.tabs.unhideTab(tab.id);
53316                 }
53317                 tab.activate();
53318             }else{
53319                 this.setActivePanel(panel);
53320             }
53321         }
53322         return panel;
53323     },
53324
53325     /**
53326      * Get the active panel for this region.
53327      * @return {Roo.ContentPanel} The active panel or null
53328      */
53329     getActivePanel : function(){
53330         return this.activePanel;
53331     },
53332
53333     validateVisibility : function(){
53334         if(this.panels.getCount() < 1){
53335             this.updateTitle("&#160;");
53336             this.closeBtn.hide();
53337             this.hide();
53338         }else{
53339             if(!this.isVisible()){
53340                 this.show();
53341             }
53342         }
53343     },
53344
53345     /**
53346      * Adds the passed ContentPanel(s) to this region.
53347      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
53348      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
53349      */
53350     add : function(panel){
53351         if(arguments.length > 1){
53352             for(var i = 0, len = arguments.length; i < len; i++) {
53353                 this.add(arguments[i]);
53354             }
53355             return null;
53356         }
53357         if(this.hasPanel(panel)){
53358             this.showPanel(panel);
53359             return panel;
53360         }
53361         panel.setRegion(this);
53362         this.panels.add(panel);
53363         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
53364             this.bodyEl.dom.appendChild(panel.getEl().dom);
53365             if(panel.background !== true){
53366                 this.setActivePanel(panel);
53367             }
53368             this.fireEvent("paneladded", this, panel);
53369             return panel;
53370         }
53371         if(!this.tabs){
53372             this.initTabs();
53373         }else{
53374             this.initPanelAsTab(panel);
53375         }
53376         if(panel.background !== true){
53377             this.tabs.activate(panel.getEl().id);
53378         }
53379         this.fireEvent("paneladded", this, panel);
53380         return panel;
53381     },
53382
53383     /**
53384      * Hides the tab for the specified panel.
53385      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
53386      */
53387     hidePanel : function(panel){
53388         if(this.tabs && (panel = this.getPanel(panel))){
53389             this.tabs.hideTab(panel.getEl().id);
53390         }
53391     },
53392
53393     /**
53394      * Unhides the tab for a previously hidden panel.
53395      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
53396      */
53397     unhidePanel : function(panel){
53398         if(this.tabs && (panel = this.getPanel(panel))){
53399             this.tabs.unhideTab(panel.getEl().id);
53400         }
53401     },
53402
53403     clearPanels : function(){
53404         while(this.panels.getCount() > 0){
53405              this.remove(this.panels.first());
53406         }
53407     },
53408
53409     /**
53410      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
53411      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
53412      * @param {Boolean} preservePanel Overrides the config preservePanel option
53413      * @return {Roo.ContentPanel} The panel that was removed
53414      */
53415     remove : function(panel, preservePanel){
53416         panel = this.getPanel(panel);
53417         if(!panel){
53418             return null;
53419         }
53420         var e = {};
53421         this.fireEvent("beforeremove", this, panel, e);
53422         if(e.cancel === true){
53423             return null;
53424         }
53425         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
53426         var panelId = panel.getId();
53427         this.panels.removeKey(panelId);
53428         if(preservePanel){
53429             document.body.appendChild(panel.getEl().dom);
53430         }
53431         if(this.tabs){
53432             this.tabs.removeTab(panel.getEl().id);
53433         }else if (!preservePanel){
53434             this.bodyEl.dom.removeChild(panel.getEl().dom);
53435         }
53436         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
53437             var p = this.panels.first();
53438             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
53439             tempEl.appendChild(p.getEl().dom);
53440             this.bodyEl.update("");
53441             this.bodyEl.dom.appendChild(p.getEl().dom);
53442             tempEl = null;
53443             this.updateTitle(p.getTitle());
53444             this.tabs = null;
53445             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
53446             this.setActivePanel(p);
53447         }
53448         panel.setRegion(null);
53449         if(this.activePanel == panel){
53450             this.activePanel = null;
53451         }
53452         if(this.config.autoDestroy !== false && preservePanel !== true){
53453             try{panel.destroy();}catch(e){}
53454         }
53455         this.fireEvent("panelremoved", this, panel);
53456         return panel;
53457     },
53458
53459     /**
53460      * Returns the TabPanel component used by this region
53461      * @return {Roo.TabPanel}
53462      */
53463     getTabs : function(){
53464         return this.tabs;
53465     },
53466
53467     createTool : function(parentEl, className){
53468         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
53469             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
53470         btn.addClassOnOver("x-layout-tools-button-over");
53471         return btn;
53472     }
53473 });/*
53474  * Based on:
53475  * Ext JS Library 1.1.1
53476  * Copyright(c) 2006-2007, Ext JS, LLC.
53477  *
53478  * Originally Released Under LGPL - original licence link has changed is not relivant.
53479  *
53480  * Fork - LGPL
53481  * <script type="text/javascript">
53482  */
53483  
53484
53485
53486 /**
53487  * @class Roo.SplitLayoutRegion
53488  * @extends Roo.LayoutRegion
53489  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
53490  */
53491 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
53492     this.cursor = cursor;
53493     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
53494 };
53495
53496 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
53497     splitTip : "Drag to resize.",
53498     collapsibleSplitTip : "Drag to resize. Double click to hide.",
53499     useSplitTips : false,
53500
53501     applyConfig : function(config){
53502         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
53503         if(config.split){
53504             if(!this.split){
53505                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
53506                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
53507                 /** The SplitBar for this region 
53508                 * @type Roo.SplitBar */
53509                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
53510                 this.split.on("moved", this.onSplitMove, this);
53511                 this.split.useShim = config.useShim === true;
53512                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
53513                 if(this.useSplitTips){
53514                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
53515                 }
53516                 if(config.collapsible){
53517                     this.split.el.on("dblclick", this.collapse,  this);
53518                 }
53519             }
53520             if(typeof config.minSize != "undefined"){
53521                 this.split.minSize = config.minSize;
53522             }
53523             if(typeof config.maxSize != "undefined"){
53524                 this.split.maxSize = config.maxSize;
53525             }
53526             if(config.hideWhenEmpty || config.hidden || config.collapsed){
53527                 this.hideSplitter();
53528             }
53529         }
53530     },
53531
53532     getHMaxSize : function(){
53533          var cmax = this.config.maxSize || 10000;
53534          var center = this.mgr.getRegion("center");
53535          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
53536     },
53537
53538     getVMaxSize : function(){
53539          var cmax = this.config.maxSize || 10000;
53540          var center = this.mgr.getRegion("center");
53541          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
53542     },
53543
53544     onSplitMove : function(split, newSize){
53545         this.fireEvent("resized", this, newSize);
53546     },
53547     
53548     /** 
53549      * Returns the {@link Roo.SplitBar} for this region.
53550      * @return {Roo.SplitBar}
53551      */
53552     getSplitBar : function(){
53553         return this.split;
53554     },
53555     
53556     hide : function(){
53557         this.hideSplitter();
53558         Roo.SplitLayoutRegion.superclass.hide.call(this);
53559     },
53560
53561     hideSplitter : function(){
53562         if(this.split){
53563             this.split.el.setLocation(-2000,-2000);
53564             this.split.el.hide();
53565         }
53566     },
53567
53568     show : function(){
53569         if(this.split){
53570             this.split.el.show();
53571         }
53572         Roo.SplitLayoutRegion.superclass.show.call(this);
53573     },
53574     
53575     beforeSlide: function(){
53576         if(Roo.isGecko){// firefox overflow auto bug workaround
53577             this.bodyEl.clip();
53578             if(this.tabs) {
53579                 this.tabs.bodyEl.clip();
53580             }
53581             if(this.activePanel){
53582                 this.activePanel.getEl().clip();
53583                 
53584                 if(this.activePanel.beforeSlide){
53585                     this.activePanel.beforeSlide();
53586                 }
53587             }
53588         }
53589     },
53590     
53591     afterSlide : function(){
53592         if(Roo.isGecko){// firefox overflow auto bug workaround
53593             this.bodyEl.unclip();
53594             if(this.tabs) {
53595                 this.tabs.bodyEl.unclip();
53596             }
53597             if(this.activePanel){
53598                 this.activePanel.getEl().unclip();
53599                 if(this.activePanel.afterSlide){
53600                     this.activePanel.afterSlide();
53601                 }
53602             }
53603         }
53604     },
53605
53606     initAutoHide : function(){
53607         if(this.autoHide !== false){
53608             if(!this.autoHideHd){
53609                 var st = new Roo.util.DelayedTask(this.slideIn, this);
53610                 this.autoHideHd = {
53611                     "mouseout": function(e){
53612                         if(!e.within(this.el, true)){
53613                             st.delay(500);
53614                         }
53615                     },
53616                     "mouseover" : function(e){
53617                         st.cancel();
53618                     },
53619                     scope : this
53620                 };
53621             }
53622             this.el.on(this.autoHideHd);
53623         }
53624     },
53625
53626     clearAutoHide : function(){
53627         if(this.autoHide !== false){
53628             this.el.un("mouseout", this.autoHideHd.mouseout);
53629             this.el.un("mouseover", this.autoHideHd.mouseover);
53630         }
53631     },
53632
53633     clearMonitor : function(){
53634         Roo.get(document).un("click", this.slideInIf, this);
53635     },
53636
53637     // these names are backwards but not changed for compat
53638     slideOut : function(){
53639         if(this.isSlid || this.el.hasActiveFx()){
53640             return;
53641         }
53642         this.isSlid = true;
53643         if(this.collapseBtn){
53644             this.collapseBtn.hide();
53645         }
53646         this.closeBtnState = this.closeBtn.getStyle('display');
53647         this.closeBtn.hide();
53648         if(this.stickBtn){
53649             this.stickBtn.show();
53650         }
53651         this.el.show();
53652         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
53653         this.beforeSlide();
53654         this.el.setStyle("z-index", 10001);
53655         this.el.slideIn(this.getSlideAnchor(), {
53656             callback: function(){
53657                 this.afterSlide();
53658                 this.initAutoHide();
53659                 Roo.get(document).on("click", this.slideInIf, this);
53660                 this.fireEvent("slideshow", this);
53661             },
53662             scope: this,
53663             block: true
53664         });
53665     },
53666
53667     afterSlideIn : function(){
53668         this.clearAutoHide();
53669         this.isSlid = false;
53670         this.clearMonitor();
53671         this.el.setStyle("z-index", "");
53672         if(this.collapseBtn){
53673             this.collapseBtn.show();
53674         }
53675         this.closeBtn.setStyle('display', this.closeBtnState);
53676         if(this.stickBtn){
53677             this.stickBtn.hide();
53678         }
53679         this.fireEvent("slidehide", this);
53680     },
53681
53682     slideIn : function(cb){
53683         if(!this.isSlid || this.el.hasActiveFx()){
53684             Roo.callback(cb);
53685             return;
53686         }
53687         this.isSlid = false;
53688         this.beforeSlide();
53689         this.el.slideOut(this.getSlideAnchor(), {
53690             callback: function(){
53691                 this.el.setLeftTop(-10000, -10000);
53692                 this.afterSlide();
53693                 this.afterSlideIn();
53694                 Roo.callback(cb);
53695             },
53696             scope: this,
53697             block: true
53698         });
53699     },
53700     
53701     slideInIf : function(e){
53702         if(!e.within(this.el)){
53703             this.slideIn();
53704         }
53705     },
53706
53707     animateCollapse : function(){
53708         this.beforeSlide();
53709         this.el.setStyle("z-index", 20000);
53710         var anchor = this.getSlideAnchor();
53711         this.el.slideOut(anchor, {
53712             callback : function(){
53713                 this.el.setStyle("z-index", "");
53714                 this.collapsedEl.slideIn(anchor, {duration:.3});
53715                 this.afterSlide();
53716                 this.el.setLocation(-10000,-10000);
53717                 this.el.hide();
53718                 this.fireEvent("collapsed", this);
53719             },
53720             scope: this,
53721             block: true
53722         });
53723     },
53724
53725     animateExpand : function(){
53726         this.beforeSlide();
53727         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
53728         this.el.setStyle("z-index", 20000);
53729         this.collapsedEl.hide({
53730             duration:.1
53731         });
53732         this.el.slideIn(this.getSlideAnchor(), {
53733             callback : function(){
53734                 this.el.setStyle("z-index", "");
53735                 this.afterSlide();
53736                 if(this.split){
53737                     this.split.el.show();
53738                 }
53739                 this.fireEvent("invalidated", this);
53740                 this.fireEvent("expanded", this);
53741             },
53742             scope: this,
53743             block: true
53744         });
53745     },
53746
53747     anchors : {
53748         "west" : "left",
53749         "east" : "right",
53750         "north" : "top",
53751         "south" : "bottom"
53752     },
53753
53754     sanchors : {
53755         "west" : "l",
53756         "east" : "r",
53757         "north" : "t",
53758         "south" : "b"
53759     },
53760
53761     canchors : {
53762         "west" : "tl-tr",
53763         "east" : "tr-tl",
53764         "north" : "tl-bl",
53765         "south" : "bl-tl"
53766     },
53767
53768     getAnchor : function(){
53769         return this.anchors[this.position];
53770     },
53771
53772     getCollapseAnchor : function(){
53773         return this.canchors[this.position];
53774     },
53775
53776     getSlideAnchor : function(){
53777         return this.sanchors[this.position];
53778     },
53779
53780     getAlignAdj : function(){
53781         var cm = this.cmargins;
53782         switch(this.position){
53783             case "west":
53784                 return [0, 0];
53785             break;
53786             case "east":
53787                 return [0, 0];
53788             break;
53789             case "north":
53790                 return [0, 0];
53791             break;
53792             case "south":
53793                 return [0, 0];
53794             break;
53795         }
53796     },
53797
53798     getExpandAdj : function(){
53799         var c = this.collapsedEl, cm = this.cmargins;
53800         switch(this.position){
53801             case "west":
53802                 return [-(cm.right+c.getWidth()+cm.left), 0];
53803             break;
53804             case "east":
53805                 return [cm.right+c.getWidth()+cm.left, 0];
53806             break;
53807             case "north":
53808                 return [0, -(cm.top+cm.bottom+c.getHeight())];
53809             break;
53810             case "south":
53811                 return [0, cm.top+cm.bottom+c.getHeight()];
53812             break;
53813         }
53814     }
53815 });/*
53816  * Based on:
53817  * Ext JS Library 1.1.1
53818  * Copyright(c) 2006-2007, Ext JS, LLC.
53819  *
53820  * Originally Released Under LGPL - original licence link has changed is not relivant.
53821  *
53822  * Fork - LGPL
53823  * <script type="text/javascript">
53824  */
53825 /*
53826  * These classes are private internal classes
53827  */
53828 Roo.CenterLayoutRegion = function(mgr, config){
53829     Roo.LayoutRegion.call(this, mgr, config, "center");
53830     this.visible = true;
53831     this.minWidth = config.minWidth || 20;
53832     this.minHeight = config.minHeight || 20;
53833 };
53834
53835 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
53836     hide : function(){
53837         // center panel can't be hidden
53838     },
53839     
53840     show : function(){
53841         // center panel can't be hidden
53842     },
53843     
53844     getMinWidth: function(){
53845         return this.minWidth;
53846     },
53847     
53848     getMinHeight: function(){
53849         return this.minHeight;
53850     }
53851 });
53852
53853
53854 Roo.NorthLayoutRegion = function(mgr, config){
53855     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
53856     if(this.split){
53857         this.split.placement = Roo.SplitBar.TOP;
53858         this.split.orientation = Roo.SplitBar.VERTICAL;
53859         this.split.el.addClass("x-layout-split-v");
53860     }
53861     var size = config.initialSize || config.height;
53862     if(typeof size != "undefined"){
53863         this.el.setHeight(size);
53864     }
53865 };
53866 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
53867     orientation: Roo.SplitBar.VERTICAL,
53868     getBox : function(){
53869         if(this.collapsed){
53870             return this.collapsedEl.getBox();
53871         }
53872         var box = this.el.getBox();
53873         if(this.split){
53874             box.height += this.split.el.getHeight();
53875         }
53876         return box;
53877     },
53878     
53879     updateBox : function(box){
53880         if(this.split && !this.collapsed){
53881             box.height -= this.split.el.getHeight();
53882             this.split.el.setLeft(box.x);
53883             this.split.el.setTop(box.y+box.height);
53884             this.split.el.setWidth(box.width);
53885         }
53886         if(this.collapsed){
53887             this.updateBody(box.width, null);
53888         }
53889         Roo.LayoutRegion.prototype.updateBox.call(this, box);
53890     }
53891 });
53892
53893 Roo.SouthLayoutRegion = function(mgr, config){
53894     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
53895     if(this.split){
53896         this.split.placement = Roo.SplitBar.BOTTOM;
53897         this.split.orientation = Roo.SplitBar.VERTICAL;
53898         this.split.el.addClass("x-layout-split-v");
53899     }
53900     var size = config.initialSize || config.height;
53901     if(typeof size != "undefined"){
53902         this.el.setHeight(size);
53903     }
53904 };
53905 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
53906     orientation: Roo.SplitBar.VERTICAL,
53907     getBox : function(){
53908         if(this.collapsed){
53909             return this.collapsedEl.getBox();
53910         }
53911         var box = this.el.getBox();
53912         if(this.split){
53913             var sh = this.split.el.getHeight();
53914             box.height += sh;
53915             box.y -= sh;
53916         }
53917         return box;
53918     },
53919     
53920     updateBox : function(box){
53921         if(this.split && !this.collapsed){
53922             var sh = this.split.el.getHeight();
53923             box.height -= sh;
53924             box.y += sh;
53925             this.split.el.setLeft(box.x);
53926             this.split.el.setTop(box.y-sh);
53927             this.split.el.setWidth(box.width);
53928         }
53929         if(this.collapsed){
53930             this.updateBody(box.width, null);
53931         }
53932         Roo.LayoutRegion.prototype.updateBox.call(this, box);
53933     }
53934 });
53935
53936 Roo.EastLayoutRegion = function(mgr, config){
53937     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
53938     if(this.split){
53939         this.split.placement = Roo.SplitBar.RIGHT;
53940         this.split.orientation = Roo.SplitBar.HORIZONTAL;
53941         this.split.el.addClass("x-layout-split-h");
53942     }
53943     var size = config.initialSize || config.width;
53944     if(typeof size != "undefined"){
53945         this.el.setWidth(size);
53946     }
53947 };
53948 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
53949     orientation: Roo.SplitBar.HORIZONTAL,
53950     getBox : function(){
53951         if(this.collapsed){
53952             return this.collapsedEl.getBox();
53953         }
53954         var box = this.el.getBox();
53955         if(this.split){
53956             var sw = this.split.el.getWidth();
53957             box.width += sw;
53958             box.x -= sw;
53959         }
53960         return box;
53961     },
53962
53963     updateBox : function(box){
53964         if(this.split && !this.collapsed){
53965             var sw = this.split.el.getWidth();
53966             box.width -= sw;
53967             this.split.el.setLeft(box.x);
53968             this.split.el.setTop(box.y);
53969             this.split.el.setHeight(box.height);
53970             box.x += sw;
53971         }
53972         if(this.collapsed){
53973             this.updateBody(null, box.height);
53974         }
53975         Roo.LayoutRegion.prototype.updateBox.call(this, box);
53976     }
53977 });
53978
53979 Roo.WestLayoutRegion = function(mgr, config){
53980     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
53981     if(this.split){
53982         this.split.placement = Roo.SplitBar.LEFT;
53983         this.split.orientation = Roo.SplitBar.HORIZONTAL;
53984         this.split.el.addClass("x-layout-split-h");
53985     }
53986     var size = config.initialSize || config.width;
53987     if(typeof size != "undefined"){
53988         this.el.setWidth(size);
53989     }
53990 };
53991 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
53992     orientation: Roo.SplitBar.HORIZONTAL,
53993     getBox : function(){
53994         if(this.collapsed){
53995             return this.collapsedEl.getBox();
53996         }
53997         var box = this.el.getBox();
53998         if(this.split){
53999             box.width += this.split.el.getWidth();
54000         }
54001         return box;
54002     },
54003     
54004     updateBox : function(box){
54005         if(this.split && !this.collapsed){
54006             var sw = this.split.el.getWidth();
54007             box.width -= sw;
54008             this.split.el.setLeft(box.x+box.width);
54009             this.split.el.setTop(box.y);
54010             this.split.el.setHeight(box.height);
54011         }
54012         if(this.collapsed){
54013             this.updateBody(null, box.height);
54014         }
54015         Roo.LayoutRegion.prototype.updateBox.call(this, box);
54016     }
54017 });
54018 /*
54019  * Based on:
54020  * Ext JS Library 1.1.1
54021  * Copyright(c) 2006-2007, Ext JS, LLC.
54022  *
54023  * Originally Released Under LGPL - original licence link has changed is not relivant.
54024  *
54025  * Fork - LGPL
54026  * <script type="text/javascript">
54027  */
54028  
54029  
54030 /*
54031  * Private internal class for reading and applying state
54032  */
54033 Roo.LayoutStateManager = function(layout){
54034      // default empty state
54035      this.state = {
54036         north: {},
54037         south: {},
54038         east: {},
54039         west: {}       
54040     };
54041 };
54042
54043 Roo.LayoutStateManager.prototype = {
54044     init : function(layout, provider){
54045         this.provider = provider;
54046         var state = provider.get(layout.id+"-layout-state");
54047         if(state){
54048             var wasUpdating = layout.isUpdating();
54049             if(!wasUpdating){
54050                 layout.beginUpdate();
54051             }
54052             for(var key in state){
54053                 if(typeof state[key] != "function"){
54054                     var rstate = state[key];
54055                     var r = layout.getRegion(key);
54056                     if(r && rstate){
54057                         if(rstate.size){
54058                             r.resizeTo(rstate.size);
54059                         }
54060                         if(rstate.collapsed == true){
54061                             r.collapse(true);
54062                         }else{
54063                             r.expand(null, true);
54064                         }
54065                     }
54066                 }
54067             }
54068             if(!wasUpdating){
54069                 layout.endUpdate();
54070             }
54071             this.state = state; 
54072         }
54073         this.layout = layout;
54074         layout.on("regionresized", this.onRegionResized, this);
54075         layout.on("regioncollapsed", this.onRegionCollapsed, this);
54076         layout.on("regionexpanded", this.onRegionExpanded, this);
54077     },
54078     
54079     storeState : function(){
54080         this.provider.set(this.layout.id+"-layout-state", this.state);
54081     },
54082     
54083     onRegionResized : function(region, newSize){
54084         this.state[region.getPosition()].size = newSize;
54085         this.storeState();
54086     },
54087     
54088     onRegionCollapsed : function(region){
54089         this.state[region.getPosition()].collapsed = true;
54090         this.storeState();
54091     },
54092     
54093     onRegionExpanded : function(region){
54094         this.state[region.getPosition()].collapsed = false;
54095         this.storeState();
54096     }
54097 };/*
54098  * Based on:
54099  * Ext JS Library 1.1.1
54100  * Copyright(c) 2006-2007, Ext JS, LLC.
54101  *
54102  * Originally Released Under LGPL - original licence link has changed is not relivant.
54103  *
54104  * Fork - LGPL
54105  * <script type="text/javascript">
54106  */
54107 /**
54108  * @class Roo.ContentPanel
54109  * @extends Roo.util.Observable
54110  * A basic ContentPanel element.
54111  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
54112  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
54113  * @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
54114  * @cfg {Boolean}   closable      True if the panel can be closed/removed
54115  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
54116  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
54117  * @cfg {Toolbar}   toolbar       A toolbar for this panel
54118  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
54119  * @cfg {String} title          The title for this panel
54120  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
54121  * @cfg {String} url            Calls {@link #setUrl} with this value
54122  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
54123  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
54124  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
54125  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
54126
54127  * @constructor
54128  * Create a new ContentPanel.
54129  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
54130  * @param {String/Object} config A string to set only the title or a config object
54131  * @param {String} content (optional) Set the HTML content for this panel
54132  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
54133  */
54134 Roo.ContentPanel = function(el, config, content){
54135     
54136      
54137     /*
54138     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
54139         config = el;
54140         el = Roo.id();
54141     }
54142     if (config && config.parentLayout) { 
54143         el = config.parentLayout.el.createChild(); 
54144     }
54145     */
54146     if(el.autoCreate){ // xtype is available if this is called from factory
54147         config = el;
54148         el = Roo.id();
54149     }
54150     this.el = Roo.get(el);
54151     if(!this.el && config && config.autoCreate){
54152         if(typeof config.autoCreate == "object"){
54153             if(!config.autoCreate.id){
54154                 config.autoCreate.id = config.id||el;
54155             }
54156             this.el = Roo.DomHelper.append(document.body,
54157                         config.autoCreate, true);
54158         }else{
54159             this.el = Roo.DomHelper.append(document.body,
54160                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
54161         }
54162     }
54163     this.closable = false;
54164     this.loaded = false;
54165     this.active = false;
54166     if(typeof config == "string"){
54167         this.title = config;
54168     }else{
54169         Roo.apply(this, config);
54170     }
54171     
54172     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
54173         this.wrapEl = this.el.wrap();
54174         this.toolbar.container = this.el.insertSibling(false, 'before');
54175         this.toolbar = new Roo.Toolbar(this.toolbar);
54176     }
54177     
54178     // xtype created footer. - not sure if will work as we normally have to render first..
54179     if (this.footer && !this.footer.el && this.footer.xtype) {
54180         if (!this.wrapEl) {
54181             this.wrapEl = this.el.wrap();
54182         }
54183     
54184         this.footer.container = this.wrapEl.createChild();
54185          
54186         this.footer = Roo.factory(this.footer, Roo);
54187         
54188     }
54189     
54190     if(this.resizeEl){
54191         this.resizeEl = Roo.get(this.resizeEl, true);
54192     }else{
54193         this.resizeEl = this.el;
54194     }
54195     // handle view.xtype
54196     
54197  
54198     
54199     
54200     this.addEvents({
54201         /**
54202          * @event activate
54203          * Fires when this panel is activated. 
54204          * @param {Roo.ContentPanel} this
54205          */
54206         "activate" : true,
54207         /**
54208          * @event deactivate
54209          * Fires when this panel is activated. 
54210          * @param {Roo.ContentPanel} this
54211          */
54212         "deactivate" : true,
54213
54214         /**
54215          * @event resize
54216          * Fires when this panel is resized if fitToFrame is true.
54217          * @param {Roo.ContentPanel} this
54218          * @param {Number} width The width after any component adjustments
54219          * @param {Number} height The height after any component adjustments
54220          */
54221         "resize" : true,
54222         
54223          /**
54224          * @event render
54225          * Fires when this tab is created
54226          * @param {Roo.ContentPanel} this
54227          */
54228         "render" : true
54229          
54230         
54231     });
54232     
54233
54234     
54235     
54236     if(this.autoScroll){
54237         this.resizeEl.setStyle("overflow", "auto");
54238     } else {
54239         // fix randome scrolling
54240         this.el.on('scroll', function() {
54241             Roo.log('fix random scolling');
54242             this.scrollTo('top',0); 
54243         });
54244     }
54245     content = content || this.content;
54246     if(content){
54247         this.setContent(content);
54248     }
54249     if(config && config.url){
54250         this.setUrl(this.url, this.params, this.loadOnce);
54251     }
54252     
54253     
54254     
54255     Roo.ContentPanel.superclass.constructor.call(this);
54256     
54257     if (this.view && typeof(this.view.xtype) != 'undefined') {
54258         this.view.el = this.el.appendChild(document.createElement("div"));
54259         this.view = Roo.factory(this.view); 
54260         this.view.render  &&  this.view.render(false, '');  
54261     }
54262     
54263     
54264     this.fireEvent('render', this);
54265 };
54266
54267 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
54268     tabTip:'',
54269     setRegion : function(region){
54270         this.region = region;
54271         if(region){
54272            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
54273         }else{
54274            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
54275         } 
54276     },
54277     
54278     /**
54279      * Returns the toolbar for this Panel if one was configured. 
54280      * @return {Roo.Toolbar} 
54281      */
54282     getToolbar : function(){
54283         return this.toolbar;
54284     },
54285     
54286     setActiveState : function(active){
54287         this.active = active;
54288         if(!active){
54289             this.fireEvent("deactivate", this);
54290         }else{
54291             this.fireEvent("activate", this);
54292         }
54293     },
54294     /**
54295      * Updates this panel's element
54296      * @param {String} content The new content
54297      * @param {Boolean} loadScripts (optional) true to look for and process scripts
54298     */
54299     setContent : function(content, loadScripts){
54300         this.el.update(content, loadScripts);
54301     },
54302
54303     ignoreResize : function(w, h){
54304         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
54305             return true;
54306         }else{
54307             this.lastSize = {width: w, height: h};
54308             return false;
54309         }
54310     },
54311     /**
54312      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
54313      * @return {Roo.UpdateManager} The UpdateManager
54314      */
54315     getUpdateManager : function(){
54316         return this.el.getUpdateManager();
54317     },
54318      /**
54319      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
54320      * @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:
54321 <pre><code>
54322 panel.load({
54323     url: "your-url.php",
54324     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
54325     callback: yourFunction,
54326     scope: yourObject, //(optional scope)
54327     discardUrl: false,
54328     nocache: false,
54329     text: "Loading...",
54330     timeout: 30,
54331     scripts: false
54332 });
54333 </code></pre>
54334      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
54335      * 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.
54336      * @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}
54337      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
54338      * @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.
54339      * @return {Roo.ContentPanel} this
54340      */
54341     load : function(){
54342         var um = this.el.getUpdateManager();
54343         um.update.apply(um, arguments);
54344         return this;
54345     },
54346
54347
54348     /**
54349      * 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.
54350      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
54351      * @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)
54352      * @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)
54353      * @return {Roo.UpdateManager} The UpdateManager
54354      */
54355     setUrl : function(url, params, loadOnce){
54356         if(this.refreshDelegate){
54357             this.removeListener("activate", this.refreshDelegate);
54358         }
54359         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
54360         this.on("activate", this.refreshDelegate);
54361         return this.el.getUpdateManager();
54362     },
54363     
54364     _handleRefresh : function(url, params, loadOnce){
54365         if(!loadOnce || !this.loaded){
54366             var updater = this.el.getUpdateManager();
54367             updater.update(url, params, this._setLoaded.createDelegate(this));
54368         }
54369     },
54370     
54371     _setLoaded : function(){
54372         this.loaded = true;
54373     }, 
54374     
54375     /**
54376      * Returns this panel's id
54377      * @return {String} 
54378      */
54379     getId : function(){
54380         return this.el.id;
54381     },
54382     
54383     /** 
54384      * Returns this panel's element - used by regiosn to add.
54385      * @return {Roo.Element} 
54386      */
54387     getEl : function(){
54388         return this.wrapEl || this.el;
54389     },
54390     
54391     adjustForComponents : function(width, height)
54392     {
54393         //Roo.log('adjustForComponents ');
54394         if(this.resizeEl != this.el){
54395             width -= this.el.getFrameWidth('lr');
54396             height -= this.el.getFrameWidth('tb');
54397         }
54398         if(this.toolbar){
54399             var te = this.toolbar.getEl();
54400             height -= te.getHeight();
54401             te.setWidth(width);
54402         }
54403         if(this.footer){
54404             var te = this.footer.getEl();
54405             //Roo.log("footer:" + te.getHeight());
54406             
54407             height -= te.getHeight();
54408             te.setWidth(width);
54409         }
54410         
54411         
54412         if(this.adjustments){
54413             width += this.adjustments[0];
54414             height += this.adjustments[1];
54415         }
54416         return {"width": width, "height": height};
54417     },
54418     
54419     setSize : function(width, height){
54420         if(this.fitToFrame && !this.ignoreResize(width, height)){
54421             if(this.fitContainer && this.resizeEl != this.el){
54422                 this.el.setSize(width, height);
54423             }
54424             var size = this.adjustForComponents(width, height);
54425             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
54426             this.fireEvent('resize', this, size.width, size.height);
54427         }
54428     },
54429     
54430     /**
54431      * Returns this panel's title
54432      * @return {String} 
54433      */
54434     getTitle : function(){
54435         return this.title;
54436     },
54437     
54438     /**
54439      * Set this panel's title
54440      * @param {String} title
54441      */
54442     setTitle : function(title){
54443         this.title = title;
54444         if(this.region){
54445             this.region.updatePanelTitle(this, title);
54446         }
54447     },
54448     
54449     /**
54450      * Returns true is this panel was configured to be closable
54451      * @return {Boolean} 
54452      */
54453     isClosable : function(){
54454         return this.closable;
54455     },
54456     
54457     beforeSlide : function(){
54458         this.el.clip();
54459         this.resizeEl.clip();
54460     },
54461     
54462     afterSlide : function(){
54463         this.el.unclip();
54464         this.resizeEl.unclip();
54465     },
54466     
54467     /**
54468      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
54469      *   Will fail silently if the {@link #setUrl} method has not been called.
54470      *   This does not activate the panel, just updates its content.
54471      */
54472     refresh : function(){
54473         if(this.refreshDelegate){
54474            this.loaded = false;
54475            this.refreshDelegate();
54476         }
54477     },
54478     
54479     /**
54480      * Destroys this panel
54481      */
54482     destroy : function(){
54483         this.el.removeAllListeners();
54484         var tempEl = document.createElement("span");
54485         tempEl.appendChild(this.el.dom);
54486         tempEl.innerHTML = "";
54487         this.el.remove();
54488         this.el = null;
54489     },
54490     
54491     /**
54492      * form - if the content panel contains a form - this is a reference to it.
54493      * @type {Roo.form.Form}
54494      */
54495     form : false,
54496     /**
54497      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
54498      *    This contains a reference to it.
54499      * @type {Roo.View}
54500      */
54501     view : false,
54502     
54503       /**
54504      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
54505      * <pre><code>
54506
54507 layout.addxtype({
54508        xtype : 'Form',
54509        items: [ .... ]
54510    }
54511 );
54512
54513 </code></pre>
54514      * @param {Object} cfg Xtype definition of item to add.
54515      */
54516     
54517     addxtype : function(cfg) {
54518         // add form..
54519         if (cfg.xtype.match(/^Form$/)) {
54520             
54521             var el;
54522             //if (this.footer) {
54523             //    el = this.footer.container.insertSibling(false, 'before');
54524             //} else {
54525                 el = this.el.createChild();
54526             //}
54527
54528             this.form = new  Roo.form.Form(cfg);
54529             
54530             
54531             if ( this.form.allItems.length) {
54532                 this.form.render(el.dom);
54533             }
54534             return this.form;
54535         }
54536         // should only have one of theses..
54537         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
54538             // views.. should not be just added - used named prop 'view''
54539             
54540             cfg.el = this.el.appendChild(document.createElement("div"));
54541             // factory?
54542             
54543             var ret = new Roo.factory(cfg);
54544              
54545              ret.render && ret.render(false, ''); // render blank..
54546             this.view = ret;
54547             return ret;
54548         }
54549         return false;
54550     }
54551 });
54552
54553 /**
54554  * @class Roo.GridPanel
54555  * @extends Roo.ContentPanel
54556  * @constructor
54557  * Create a new GridPanel.
54558  * @param {Roo.grid.Grid} grid The grid for this panel
54559  * @param {String/Object} config A string to set only the panel's title, or a config object
54560  */
54561 Roo.GridPanel = function(grid, config){
54562     
54563   
54564     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
54565         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
54566         
54567     this.wrapper.dom.appendChild(grid.getGridEl().dom);
54568     
54569     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
54570     
54571     if(this.toolbar){
54572         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
54573     }
54574     // xtype created footer. - not sure if will work as we normally have to render first..
54575     if (this.footer && !this.footer.el && this.footer.xtype) {
54576         
54577         this.footer.container = this.grid.getView().getFooterPanel(true);
54578         this.footer.dataSource = this.grid.dataSource;
54579         this.footer = Roo.factory(this.footer, Roo);
54580         
54581     }
54582     
54583     grid.monitorWindowResize = false; // turn off autosizing
54584     grid.autoHeight = false;
54585     grid.autoWidth = false;
54586     this.grid = grid;
54587     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
54588 };
54589
54590 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
54591     getId : function(){
54592         return this.grid.id;
54593     },
54594     
54595     /**
54596      * Returns the grid for this panel
54597      * @return {Roo.grid.Grid} 
54598      */
54599     getGrid : function(){
54600         return this.grid;    
54601     },
54602     
54603     setSize : function(width, height){
54604         if(!this.ignoreResize(width, height)){
54605             var grid = this.grid;
54606             var size = this.adjustForComponents(width, height);
54607             grid.getGridEl().setSize(size.width, size.height);
54608             grid.autoSize();
54609         }
54610     },
54611     
54612     beforeSlide : function(){
54613         this.grid.getView().scroller.clip();
54614     },
54615     
54616     afterSlide : function(){
54617         this.grid.getView().scroller.unclip();
54618     },
54619     
54620     destroy : function(){
54621         this.grid.destroy();
54622         delete this.grid;
54623         Roo.GridPanel.superclass.destroy.call(this); 
54624     }
54625 });
54626
54627
54628 /**
54629  * @class Roo.NestedLayoutPanel
54630  * @extends Roo.ContentPanel
54631  * @constructor
54632  * Create a new NestedLayoutPanel.
54633  * 
54634  * 
54635  * @param {Roo.BorderLayout} layout The layout for this panel
54636  * @param {String/Object} config A string to set only the title or a config object
54637  */
54638 Roo.NestedLayoutPanel = function(layout, config)
54639 {
54640     // construct with only one argument..
54641     /* FIXME - implement nicer consturctors
54642     if (layout.layout) {
54643         config = layout;
54644         layout = config.layout;
54645         delete config.layout;
54646     }
54647     if (layout.xtype && !layout.getEl) {
54648         // then layout needs constructing..
54649         layout = Roo.factory(layout, Roo);
54650     }
54651     */
54652     
54653     
54654     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
54655     
54656     layout.monitorWindowResize = false; // turn off autosizing
54657     this.layout = layout;
54658     this.layout.getEl().addClass("x-layout-nested-layout");
54659     
54660     
54661     
54662     
54663 };
54664
54665 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
54666
54667     setSize : function(width, height){
54668         if(!this.ignoreResize(width, height)){
54669             var size = this.adjustForComponents(width, height);
54670             var el = this.layout.getEl();
54671             el.setSize(size.width, size.height);
54672             var touch = el.dom.offsetWidth;
54673             this.layout.layout();
54674             // ie requires a double layout on the first pass
54675             if(Roo.isIE && !this.initialized){
54676                 this.initialized = true;
54677                 this.layout.layout();
54678             }
54679         }
54680     },
54681     
54682     // activate all subpanels if not currently active..
54683     
54684     setActiveState : function(active){
54685         this.active = active;
54686         if(!active){
54687             this.fireEvent("deactivate", this);
54688             return;
54689         }
54690         
54691         this.fireEvent("activate", this);
54692         // not sure if this should happen before or after..
54693         if (!this.layout) {
54694             return; // should not happen..
54695         }
54696         var reg = false;
54697         for (var r in this.layout.regions) {
54698             reg = this.layout.getRegion(r);
54699             if (reg.getActivePanel()) {
54700                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
54701                 reg.setActivePanel(reg.getActivePanel());
54702                 continue;
54703             }
54704             if (!reg.panels.length) {
54705                 continue;
54706             }
54707             reg.showPanel(reg.getPanel(0));
54708         }
54709         
54710         
54711         
54712         
54713     },
54714     
54715     /**
54716      * Returns the nested BorderLayout for this panel
54717      * @return {Roo.BorderLayout} 
54718      */
54719     getLayout : function(){
54720         return this.layout;
54721     },
54722     
54723      /**
54724      * Adds a xtype elements to the layout of the nested panel
54725      * <pre><code>
54726
54727 panel.addxtype({
54728        xtype : 'ContentPanel',
54729        region: 'west',
54730        items: [ .... ]
54731    }
54732 );
54733
54734 panel.addxtype({
54735         xtype : 'NestedLayoutPanel',
54736         region: 'west',
54737         layout: {
54738            center: { },
54739            west: { }   
54740         },
54741         items : [ ... list of content panels or nested layout panels.. ]
54742    }
54743 );
54744 </code></pre>
54745      * @param {Object} cfg Xtype definition of item to add.
54746      */
54747     addxtype : function(cfg) {
54748         return this.layout.addxtype(cfg);
54749     
54750     }
54751 });
54752
54753 Roo.ScrollPanel = function(el, config, content){
54754     config = config || {};
54755     config.fitToFrame = true;
54756     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
54757     
54758     this.el.dom.style.overflow = "hidden";
54759     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
54760     this.el.removeClass("x-layout-inactive-content");
54761     this.el.on("mousewheel", this.onWheel, this);
54762
54763     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
54764     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
54765     up.unselectable(); down.unselectable();
54766     up.on("click", this.scrollUp, this);
54767     down.on("click", this.scrollDown, this);
54768     up.addClassOnOver("x-scroller-btn-over");
54769     down.addClassOnOver("x-scroller-btn-over");
54770     up.addClassOnClick("x-scroller-btn-click");
54771     down.addClassOnClick("x-scroller-btn-click");
54772     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
54773
54774     this.resizeEl = this.el;
54775     this.el = wrap; this.up = up; this.down = down;
54776 };
54777
54778 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
54779     increment : 100,
54780     wheelIncrement : 5,
54781     scrollUp : function(){
54782         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
54783     },
54784
54785     scrollDown : function(){
54786         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
54787     },
54788
54789     afterScroll : function(){
54790         var el = this.resizeEl;
54791         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
54792         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
54793         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
54794     },
54795
54796     setSize : function(){
54797         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
54798         this.afterScroll();
54799     },
54800
54801     onWheel : function(e){
54802         var d = e.getWheelDelta();
54803         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
54804         this.afterScroll();
54805         e.stopEvent();
54806     },
54807
54808     setContent : function(content, loadScripts){
54809         this.resizeEl.update(content, loadScripts);
54810     }
54811
54812 });
54813
54814
54815
54816
54817
54818
54819
54820
54821
54822 /**
54823  * @class Roo.TreePanel
54824  * @extends Roo.ContentPanel
54825  * @constructor
54826  * Create a new TreePanel. - defaults to fit/scoll contents.
54827  * @param {String/Object} config A string to set only the panel's title, or a config object
54828  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
54829  */
54830 Roo.TreePanel = function(config){
54831     var el = config.el;
54832     var tree = config.tree;
54833     delete config.tree; 
54834     delete config.el; // hopefull!
54835     
54836     // wrapper for IE7 strict & safari scroll issue
54837     
54838     var treeEl = el.createChild();
54839     config.resizeEl = treeEl;
54840     
54841     
54842     
54843     Roo.TreePanel.superclass.constructor.call(this, el, config);
54844  
54845  
54846     this.tree = new Roo.tree.TreePanel(treeEl , tree);
54847     //console.log(tree);
54848     this.on('activate', function()
54849     {
54850         if (this.tree.rendered) {
54851             return;
54852         }
54853         //console.log('render tree');
54854         this.tree.render();
54855     });
54856     // this should not be needed.. - it's actually the 'el' that resizes?
54857     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
54858     
54859     //this.on('resize',  function (cp, w, h) {
54860     //        this.tree.innerCt.setWidth(w);
54861     //        this.tree.innerCt.setHeight(h);
54862     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
54863     //});
54864
54865         
54866     
54867 };
54868
54869 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
54870     fitToFrame : true,
54871     autoScroll : true
54872 });
54873
54874
54875
54876
54877
54878
54879
54880
54881
54882
54883
54884 /*
54885  * Based on:
54886  * Ext JS Library 1.1.1
54887  * Copyright(c) 2006-2007, Ext JS, LLC.
54888  *
54889  * Originally Released Under LGPL - original licence link has changed is not relivant.
54890  *
54891  * Fork - LGPL
54892  * <script type="text/javascript">
54893  */
54894  
54895
54896 /**
54897  * @class Roo.ReaderLayout
54898  * @extends Roo.BorderLayout
54899  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
54900  * center region containing two nested regions (a top one for a list view and one for item preview below),
54901  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
54902  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
54903  * expedites the setup of the overall layout and regions for this common application style.
54904  * Example:
54905  <pre><code>
54906 var reader = new Roo.ReaderLayout();
54907 var CP = Roo.ContentPanel;  // shortcut for adding
54908
54909 reader.beginUpdate();
54910 reader.add("north", new CP("north", "North"));
54911 reader.add("west", new CP("west", {title: "West"}));
54912 reader.add("east", new CP("east", {title: "East"}));
54913
54914 reader.regions.listView.add(new CP("listView", "List"));
54915 reader.regions.preview.add(new CP("preview", "Preview"));
54916 reader.endUpdate();
54917 </code></pre>
54918 * @constructor
54919 * Create a new ReaderLayout
54920 * @param {Object} config Configuration options
54921 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
54922 * document.body if omitted)
54923 */
54924 Roo.ReaderLayout = function(config, renderTo){
54925     var c = config || {size:{}};
54926     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
54927         north: c.north !== false ? Roo.apply({
54928             split:false,
54929             initialSize: 32,
54930             titlebar: false
54931         }, c.north) : false,
54932         west: c.west !== false ? Roo.apply({
54933             split:true,
54934             initialSize: 200,
54935             minSize: 175,
54936             maxSize: 400,
54937             titlebar: true,
54938             collapsible: true,
54939             animate: true,
54940             margins:{left:5,right:0,bottom:5,top:5},
54941             cmargins:{left:5,right:5,bottom:5,top:5}
54942         }, c.west) : false,
54943         east: c.east !== false ? Roo.apply({
54944             split:true,
54945             initialSize: 200,
54946             minSize: 175,
54947             maxSize: 400,
54948             titlebar: true,
54949             collapsible: true,
54950             animate: true,
54951             margins:{left:0,right:5,bottom:5,top:5},
54952             cmargins:{left:5,right:5,bottom:5,top:5}
54953         }, c.east) : false,
54954         center: Roo.apply({
54955             tabPosition: 'top',
54956             autoScroll:false,
54957             closeOnTab: true,
54958             titlebar:false,
54959             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
54960         }, c.center)
54961     });
54962
54963     this.el.addClass('x-reader');
54964
54965     this.beginUpdate();
54966
54967     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
54968         south: c.preview !== false ? Roo.apply({
54969             split:true,
54970             initialSize: 200,
54971             minSize: 100,
54972             autoScroll:true,
54973             collapsible:true,
54974             titlebar: true,
54975             cmargins:{top:5,left:0, right:0, bottom:0}
54976         }, c.preview) : false,
54977         center: Roo.apply({
54978             autoScroll:false,
54979             titlebar:false,
54980             minHeight:200
54981         }, c.listView)
54982     });
54983     this.add('center', new Roo.NestedLayoutPanel(inner,
54984             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
54985
54986     this.endUpdate();
54987
54988     this.regions.preview = inner.getRegion('south');
54989     this.regions.listView = inner.getRegion('center');
54990 };
54991
54992 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
54993  * Based on:
54994  * Ext JS Library 1.1.1
54995  * Copyright(c) 2006-2007, Ext JS, LLC.
54996  *
54997  * Originally Released Under LGPL - original licence link has changed is not relivant.
54998  *
54999  * Fork - LGPL
55000  * <script type="text/javascript">
55001  */
55002  
55003 /**
55004  * @class Roo.grid.Grid
55005  * @extends Roo.util.Observable
55006  * This class represents the primary interface of a component based grid control.
55007  * <br><br>Usage:<pre><code>
55008  var grid = new Roo.grid.Grid("my-container-id", {
55009      ds: myDataStore,
55010      cm: myColModel,
55011      selModel: mySelectionModel,
55012      autoSizeColumns: true,
55013      monitorWindowResize: false,
55014      trackMouseOver: true
55015  });
55016  // set any options
55017  grid.render();
55018  * </code></pre>
55019  * <b>Common Problems:</b><br/>
55020  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
55021  * element will correct this<br/>
55022  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
55023  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
55024  * are unpredictable.<br/>
55025  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
55026  * grid to calculate dimensions/offsets.<br/>
55027   * @constructor
55028  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
55029  * The container MUST have some type of size defined for the grid to fill. The container will be
55030  * automatically set to position relative if it isn't already.
55031  * @param {Object} config A config object that sets properties on this grid.
55032  */
55033 Roo.grid.Grid = function(container, config){
55034         // initialize the container
55035         this.container = Roo.get(container);
55036         this.container.update("");
55037         this.container.setStyle("overflow", "hidden");
55038     this.container.addClass('x-grid-container');
55039
55040     this.id = this.container.id;
55041
55042     Roo.apply(this, config);
55043     // check and correct shorthanded configs
55044     if(this.ds){
55045         this.dataSource = this.ds;
55046         delete this.ds;
55047     }
55048     if(this.cm){
55049         this.colModel = this.cm;
55050         delete this.cm;
55051     }
55052     if(this.sm){
55053         this.selModel = this.sm;
55054         delete this.sm;
55055     }
55056
55057     if (this.selModel) {
55058         this.selModel = Roo.factory(this.selModel, Roo.grid);
55059         this.sm = this.selModel;
55060         this.sm.xmodule = this.xmodule || false;
55061     }
55062     if (typeof(this.colModel.config) == 'undefined') {
55063         this.colModel = new Roo.grid.ColumnModel(this.colModel);
55064         this.cm = this.colModel;
55065         this.cm.xmodule = this.xmodule || false;
55066     }
55067     if (this.dataSource) {
55068         this.dataSource= Roo.factory(this.dataSource, Roo.data);
55069         this.ds = this.dataSource;
55070         this.ds.xmodule = this.xmodule || false;
55071          
55072     }
55073     
55074     
55075     
55076     if(this.width){
55077         this.container.setWidth(this.width);
55078     }
55079
55080     if(this.height){
55081         this.container.setHeight(this.height);
55082     }
55083     /** @private */
55084         this.addEvents({
55085         // raw events
55086         /**
55087          * @event click
55088          * The raw click event for the entire grid.
55089          * @param {Roo.EventObject} e
55090          */
55091         "click" : true,
55092         /**
55093          * @event dblclick
55094          * The raw dblclick event for the entire grid.
55095          * @param {Roo.EventObject} e
55096          */
55097         "dblclick" : true,
55098         /**
55099          * @event contextmenu
55100          * The raw contextmenu event for the entire grid.
55101          * @param {Roo.EventObject} e
55102          */
55103         "contextmenu" : true,
55104         /**
55105          * @event mousedown
55106          * The raw mousedown event for the entire grid.
55107          * @param {Roo.EventObject} e
55108          */
55109         "mousedown" : true,
55110         /**
55111          * @event mouseup
55112          * The raw mouseup event for the entire grid.
55113          * @param {Roo.EventObject} e
55114          */
55115         "mouseup" : true,
55116         /**
55117          * @event mouseover
55118          * The raw mouseover event for the entire grid.
55119          * @param {Roo.EventObject} e
55120          */
55121         "mouseover" : true,
55122         /**
55123          * @event mouseout
55124          * The raw mouseout event for the entire grid.
55125          * @param {Roo.EventObject} e
55126          */
55127         "mouseout" : true,
55128         /**
55129          * @event keypress
55130          * The raw keypress event for the entire grid.
55131          * @param {Roo.EventObject} e
55132          */
55133         "keypress" : true,
55134         /**
55135          * @event keydown
55136          * The raw keydown event for the entire grid.
55137          * @param {Roo.EventObject} e
55138          */
55139         "keydown" : true,
55140
55141         // custom events
55142
55143         /**
55144          * @event cellclick
55145          * Fires when a cell is clicked
55146          * @param {Grid} this
55147          * @param {Number} rowIndex
55148          * @param {Number} columnIndex
55149          * @param {Roo.EventObject} e
55150          */
55151         "cellclick" : true,
55152         /**
55153          * @event celldblclick
55154          * Fires when a cell is double clicked
55155          * @param {Grid} this
55156          * @param {Number} rowIndex
55157          * @param {Number} columnIndex
55158          * @param {Roo.EventObject} e
55159          */
55160         "celldblclick" : true,
55161         /**
55162          * @event rowclick
55163          * Fires when a row is clicked
55164          * @param {Grid} this
55165          * @param {Number} rowIndex
55166          * @param {Roo.EventObject} e
55167          */
55168         "rowclick" : true,
55169         /**
55170          * @event rowdblclick
55171          * Fires when a row is double clicked
55172          * @param {Grid} this
55173          * @param {Number} rowIndex
55174          * @param {Roo.EventObject} e
55175          */
55176         "rowdblclick" : true,
55177         /**
55178          * @event headerclick
55179          * Fires when a header is clicked
55180          * @param {Grid} this
55181          * @param {Number} columnIndex
55182          * @param {Roo.EventObject} e
55183          */
55184         "headerclick" : true,
55185         /**
55186          * @event headerdblclick
55187          * Fires when a header cell is double clicked
55188          * @param {Grid} this
55189          * @param {Number} columnIndex
55190          * @param {Roo.EventObject} e
55191          */
55192         "headerdblclick" : true,
55193         /**
55194          * @event rowcontextmenu
55195          * Fires when a row is right clicked
55196          * @param {Grid} this
55197          * @param {Number} rowIndex
55198          * @param {Roo.EventObject} e
55199          */
55200         "rowcontextmenu" : true,
55201         /**
55202          * @event cellcontextmenu
55203          * Fires when a cell is right clicked
55204          * @param {Grid} this
55205          * @param {Number} rowIndex
55206          * @param {Number} cellIndex
55207          * @param {Roo.EventObject} e
55208          */
55209          "cellcontextmenu" : true,
55210         /**
55211          * @event headercontextmenu
55212          * Fires when a header is right clicked
55213          * @param {Grid} this
55214          * @param {Number} columnIndex
55215          * @param {Roo.EventObject} e
55216          */
55217         "headercontextmenu" : true,
55218         /**
55219          * @event bodyscroll
55220          * Fires when the body element is scrolled
55221          * @param {Number} scrollLeft
55222          * @param {Number} scrollTop
55223          */
55224         "bodyscroll" : true,
55225         /**
55226          * @event columnresize
55227          * Fires when the user resizes a column
55228          * @param {Number} columnIndex
55229          * @param {Number} newSize
55230          */
55231         "columnresize" : true,
55232         /**
55233          * @event columnmove
55234          * Fires when the user moves a column
55235          * @param {Number} oldIndex
55236          * @param {Number} newIndex
55237          */
55238         "columnmove" : true,
55239         /**
55240          * @event startdrag
55241          * Fires when row(s) start being dragged
55242          * @param {Grid} this
55243          * @param {Roo.GridDD} dd The drag drop object
55244          * @param {event} e The raw browser event
55245          */
55246         "startdrag" : true,
55247         /**
55248          * @event enddrag
55249          * Fires when a drag operation is complete
55250          * @param {Grid} this
55251          * @param {Roo.GridDD} dd The drag drop object
55252          * @param {event} e The raw browser event
55253          */
55254         "enddrag" : true,
55255         /**
55256          * @event dragdrop
55257          * Fires when dragged row(s) are dropped on a valid DD target
55258          * @param {Grid} this
55259          * @param {Roo.GridDD} dd The drag drop object
55260          * @param {String} targetId The target drag drop object
55261          * @param {event} e The raw browser event
55262          */
55263         "dragdrop" : true,
55264         /**
55265          * @event dragover
55266          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
55267          * @param {Grid} this
55268          * @param {Roo.GridDD} dd The drag drop object
55269          * @param {String} targetId The target drag drop object
55270          * @param {event} e The raw browser event
55271          */
55272         "dragover" : true,
55273         /**
55274          * @event dragenter
55275          *  Fires when the dragged row(s) first cross another DD target while being dragged
55276          * @param {Grid} this
55277          * @param {Roo.GridDD} dd The drag drop object
55278          * @param {String} targetId The target drag drop object
55279          * @param {event} e The raw browser event
55280          */
55281         "dragenter" : true,
55282         /**
55283          * @event dragout
55284          * Fires when the dragged row(s) leave another DD target while being dragged
55285          * @param {Grid} this
55286          * @param {Roo.GridDD} dd The drag drop object
55287          * @param {String} targetId The target drag drop object
55288          * @param {event} e The raw browser event
55289          */
55290         "dragout" : true,
55291         /**
55292          * @event rowclass
55293          * Fires when a row is rendered, so you can change add a style to it.
55294          * @param {GridView} gridview   The grid view
55295          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
55296          */
55297         'rowclass' : true,
55298
55299         /**
55300          * @event render
55301          * Fires when the grid is rendered
55302          * @param {Grid} grid
55303          */
55304         'render' : true
55305     });
55306
55307     Roo.grid.Grid.superclass.constructor.call(this);
55308 };
55309 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
55310     
55311     /**
55312      * @cfg {String} ddGroup - drag drop group.
55313      */
55314
55315     /**
55316      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
55317      */
55318     minColumnWidth : 25,
55319
55320     /**
55321      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
55322      * <b>on initial render.</b> It is more efficient to explicitly size the columns
55323      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
55324      */
55325     autoSizeColumns : false,
55326
55327     /**
55328      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
55329      */
55330     autoSizeHeaders : true,
55331
55332     /**
55333      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
55334      */
55335     monitorWindowResize : true,
55336
55337     /**
55338      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
55339      * rows measured to get a columns size. Default is 0 (all rows).
55340      */
55341     maxRowsToMeasure : 0,
55342
55343     /**
55344      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
55345      */
55346     trackMouseOver : true,
55347
55348     /**
55349     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
55350     */
55351     
55352     /**
55353     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
55354     */
55355     enableDragDrop : false,
55356     
55357     /**
55358     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
55359     */
55360     enableColumnMove : true,
55361     
55362     /**
55363     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
55364     */
55365     enableColumnHide : true,
55366     
55367     /**
55368     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
55369     */
55370     enableRowHeightSync : false,
55371     
55372     /**
55373     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
55374     */
55375     stripeRows : true,
55376     
55377     /**
55378     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
55379     */
55380     autoHeight : false,
55381
55382     /**
55383      * @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.
55384      */
55385     autoExpandColumn : false,
55386
55387     /**
55388     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
55389     * Default is 50.
55390     */
55391     autoExpandMin : 50,
55392
55393     /**
55394     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
55395     */
55396     autoExpandMax : 1000,
55397
55398     /**
55399     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
55400     */
55401     view : null,
55402
55403     /**
55404     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
55405     */
55406     loadMask : false,
55407     /**
55408     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
55409     */
55410     dropTarget: false,
55411     
55412    
55413     
55414     // private
55415     rendered : false,
55416
55417     /**
55418     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
55419     * of a fixed width. Default is false.
55420     */
55421     /**
55422     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
55423     */
55424     /**
55425      * Called once after all setup has been completed and the grid is ready to be rendered.
55426      * @return {Roo.grid.Grid} this
55427      */
55428     render : function()
55429     {
55430         var c = this.container;
55431         // try to detect autoHeight/width mode
55432         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
55433             this.autoHeight = true;
55434         }
55435         var view = this.getView();
55436         view.init(this);
55437
55438         c.on("click", this.onClick, this);
55439         c.on("dblclick", this.onDblClick, this);
55440         c.on("contextmenu", this.onContextMenu, this);
55441         c.on("keydown", this.onKeyDown, this);
55442         if (Roo.isTouch) {
55443             c.on("touchstart", this.onTouchStart, this);
55444         }
55445
55446         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
55447
55448         this.getSelectionModel().init(this);
55449
55450         view.render();
55451
55452         if(this.loadMask){
55453             this.loadMask = new Roo.LoadMask(this.container,
55454                     Roo.apply({store:this.dataSource}, this.loadMask));
55455         }
55456         
55457         
55458         if (this.toolbar && this.toolbar.xtype) {
55459             this.toolbar.container = this.getView().getHeaderPanel(true);
55460             this.toolbar = new Roo.Toolbar(this.toolbar);
55461         }
55462         if (this.footer && this.footer.xtype) {
55463             this.footer.dataSource = this.getDataSource();
55464             this.footer.container = this.getView().getFooterPanel(true);
55465             this.footer = Roo.factory(this.footer, Roo);
55466         }
55467         if (this.dropTarget && this.dropTarget.xtype) {
55468             delete this.dropTarget.xtype;
55469             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
55470         }
55471         
55472         
55473         this.rendered = true;
55474         this.fireEvent('render', this);
55475         return this;
55476     },
55477
55478     /**
55479      * Reconfigures the grid to use a different Store and Column Model.
55480      * The View will be bound to the new objects and refreshed.
55481      * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
55482      * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
55483      */
55484     reconfigure : function(dataSource, colModel){
55485         if(this.loadMask){
55486             this.loadMask.destroy();
55487             this.loadMask = new Roo.LoadMask(this.container,
55488                     Roo.apply({store:dataSource}, this.loadMask));
55489         }
55490         this.view.bind(dataSource, colModel);
55491         this.dataSource = dataSource;
55492         this.colModel = colModel;
55493         this.view.refresh(true);
55494     },
55495     /**
55496      * addColumns
55497      * Add's a column, default at the end..
55498      
55499      * @param {int} position to add (default end)
55500      * @param {Array} of objects of column configuration see {@link Roo.grid.ColumnModel} 
55501      */
55502     addColumns : function(pos, ar)
55503     {
55504         
55505         for (var i =0;i< ar.length;i++) {
55506             var cfg = ar[i];
55507             cfg.id = typeof(cfg.id) == 'undefined' ? Roo.id() : cfg.id; // don't normally use this..
55508             this.cm.lookup[cfg.id] = cfg;
55509         }
55510         
55511         
55512         if (typeof(pos) == 'undefined' || pos >= this.cm.config.length) {
55513             pos = this.cm.config.length; //this.cm.config.push(cfg);
55514         } 
55515         pos = Math.max(0,pos);
55516         ar.unshift(0);
55517         ar.unshift(pos);
55518         this.cm.config.splice.apply(this.cm.config, ar);
55519         
55520         
55521         
55522         this.view.generateRules(this.cm);
55523         this.view.refresh(true);
55524         
55525     },
55526     
55527     
55528     
55529     
55530     // private
55531     onKeyDown : function(e){
55532         this.fireEvent("keydown", e);
55533     },
55534
55535     /**
55536      * Destroy this grid.
55537      * @param {Boolean} removeEl True to remove the element
55538      */
55539     destroy : function(removeEl, keepListeners){
55540         if(this.loadMask){
55541             this.loadMask.destroy();
55542         }
55543         var c = this.container;
55544         c.removeAllListeners();
55545         this.view.destroy();
55546         this.colModel.purgeListeners();
55547         if(!keepListeners){
55548             this.purgeListeners();
55549         }
55550         c.update("");
55551         if(removeEl === true){
55552             c.remove();
55553         }
55554     },
55555
55556     // private
55557     processEvent : function(name, e){
55558         // does this fire select???
55559         //Roo.log('grid:processEvent '  + name);
55560         
55561         if (name != 'touchstart' ) {
55562             this.fireEvent(name, e);    
55563         }
55564         
55565         var t = e.getTarget();
55566         var v = this.view;
55567         var header = v.findHeaderIndex(t);
55568         if(header !== false){
55569             var ename = name == 'touchstart' ? 'click' : name;
55570              
55571             this.fireEvent("header" + ename, this, header, e);
55572         }else{
55573             var row = v.findRowIndex(t);
55574             var cell = v.findCellIndex(t);
55575             if (name == 'touchstart') {
55576                 // first touch is always a click.
55577                 // hopefull this happens after selection is updated.?
55578                 name = false;
55579                 
55580                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
55581                     var cs = this.selModel.getSelectedCell();
55582                     if (row == cs[0] && cell == cs[1]){
55583                         name = 'dblclick';
55584                     }
55585                 }
55586                 if (typeof(this.selModel.getSelections) != 'undefined') {
55587                     var cs = this.selModel.getSelections();
55588                     var ds = this.dataSource;
55589                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
55590                         name = 'dblclick';
55591                     }
55592                 }
55593                 if (!name) {
55594                     return;
55595                 }
55596             }
55597             
55598             
55599             if(row !== false){
55600                 this.fireEvent("row" + name, this, row, e);
55601                 if(cell !== false){
55602                     this.fireEvent("cell" + name, this, row, cell, e);
55603                 }
55604             }
55605         }
55606     },
55607
55608     // private
55609     onClick : function(e){
55610         this.processEvent("click", e);
55611     },
55612    // private
55613     onTouchStart : function(e){
55614         this.processEvent("touchstart", e);
55615     },
55616
55617     // private
55618     onContextMenu : function(e, t){
55619         this.processEvent("contextmenu", e);
55620     },
55621
55622     // private
55623     onDblClick : function(e){
55624         this.processEvent("dblclick", e);
55625     },
55626
55627     // private
55628     walkCells : function(row, col, step, fn, scope){
55629         var cm = this.colModel, clen = cm.getColumnCount();
55630         var ds = this.dataSource, rlen = ds.getCount(), first = true;
55631         if(step < 0){
55632             if(col < 0){
55633                 row--;
55634                 first = false;
55635             }
55636             while(row >= 0){
55637                 if(!first){
55638                     col = clen-1;
55639                 }
55640                 first = false;
55641                 while(col >= 0){
55642                     if(fn.call(scope || this, row, col, cm) === true){
55643                         return [row, col];
55644                     }
55645                     col--;
55646                 }
55647                 row--;
55648             }
55649         } else {
55650             if(col >= clen){
55651                 row++;
55652                 first = false;
55653             }
55654             while(row < rlen){
55655                 if(!first){
55656                     col = 0;
55657                 }
55658                 first = false;
55659                 while(col < clen){
55660                     if(fn.call(scope || this, row, col, cm) === true){
55661                         return [row, col];
55662                     }
55663                     col++;
55664                 }
55665                 row++;
55666             }
55667         }
55668         return null;
55669     },
55670
55671     // private
55672     getSelections : function(){
55673         return this.selModel.getSelections();
55674     },
55675
55676     /**
55677      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
55678      * but if manual update is required this method will initiate it.
55679      */
55680     autoSize : function(){
55681         if(this.rendered){
55682             this.view.layout();
55683             if(this.view.adjustForScroll){
55684                 this.view.adjustForScroll();
55685             }
55686         }
55687     },
55688
55689     /**
55690      * Returns the grid's underlying element.
55691      * @return {Element} The element
55692      */
55693     getGridEl : function(){
55694         return this.container;
55695     },
55696
55697     // private for compatibility, overridden by editor grid
55698     stopEditing : function(){},
55699
55700     /**
55701      * Returns the grid's SelectionModel.
55702      * @return {SelectionModel}
55703      */
55704     getSelectionModel : function(){
55705         if(!this.selModel){
55706             this.selModel = new Roo.grid.RowSelectionModel();
55707         }
55708         return this.selModel;
55709     },
55710
55711     /**
55712      * Returns the grid's DataSource.
55713      * @return {DataSource}
55714      */
55715     getDataSource : function(){
55716         return this.dataSource;
55717     },
55718
55719     /**
55720      * Returns the grid's ColumnModel.
55721      * @return {ColumnModel}
55722      */
55723     getColumnModel : function(){
55724         return this.colModel;
55725     },
55726
55727     /**
55728      * Returns the grid's GridView object.
55729      * @return {GridView}
55730      */
55731     getView : function(){
55732         if(!this.view){
55733             this.view = new Roo.grid.GridView(this.viewConfig);
55734         }
55735         return this.view;
55736     },
55737     /**
55738      * Called to get grid's drag proxy text, by default returns this.ddText.
55739      * @return {String}
55740      */
55741     getDragDropText : function(){
55742         var count = this.selModel.getCount();
55743         return String.format(this.ddText, count, count == 1 ? '' : 's');
55744     }
55745 });
55746 /**
55747  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
55748  * %0 is replaced with the number of selected rows.
55749  * @type String
55750  */
55751 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
55752  * Based on:
55753  * Ext JS Library 1.1.1
55754  * Copyright(c) 2006-2007, Ext JS, LLC.
55755  *
55756  * Originally Released Under LGPL - original licence link has changed is not relivant.
55757  *
55758  * Fork - LGPL
55759  * <script type="text/javascript">
55760  */
55761  
55762 Roo.grid.AbstractGridView = function(){
55763         this.grid = null;
55764         
55765         this.events = {
55766             "beforerowremoved" : true,
55767             "beforerowsinserted" : true,
55768             "beforerefresh" : true,
55769             "rowremoved" : true,
55770             "rowsinserted" : true,
55771             "rowupdated" : true,
55772             "refresh" : true
55773         };
55774     Roo.grid.AbstractGridView.superclass.constructor.call(this);
55775 };
55776
55777 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
55778     rowClass : "x-grid-row",
55779     cellClass : "x-grid-cell",
55780     tdClass : "x-grid-td",
55781     hdClass : "x-grid-hd",
55782     splitClass : "x-grid-hd-split",
55783     
55784     init: function(grid){
55785         this.grid = grid;
55786                 var cid = this.grid.getGridEl().id;
55787         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
55788         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
55789         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
55790         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
55791         },
55792         
55793     getColumnRenderers : function(){
55794         var renderers = [];
55795         var cm = this.grid.colModel;
55796         var colCount = cm.getColumnCount();
55797         for(var i = 0; i < colCount; i++){
55798             renderers[i] = cm.getRenderer(i);
55799         }
55800         return renderers;
55801     },
55802     
55803     getColumnIds : function(){
55804         var ids = [];
55805         var cm = this.grid.colModel;
55806         var colCount = cm.getColumnCount();
55807         for(var i = 0; i < colCount; i++){
55808             ids[i] = cm.getColumnId(i);
55809         }
55810         return ids;
55811     },
55812     
55813     getDataIndexes : function(){
55814         if(!this.indexMap){
55815             this.indexMap = this.buildIndexMap();
55816         }
55817         return this.indexMap.colToData;
55818     },
55819     
55820     getColumnIndexByDataIndex : function(dataIndex){
55821         if(!this.indexMap){
55822             this.indexMap = this.buildIndexMap();
55823         }
55824         return this.indexMap.dataToCol[dataIndex];
55825     },
55826     
55827     /**
55828      * Set a css style for a column dynamically. 
55829      * @param {Number} colIndex The index of the column
55830      * @param {String} name The css property name
55831      * @param {String} value The css value
55832      */
55833     setCSSStyle : function(colIndex, name, value){
55834         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
55835         Roo.util.CSS.updateRule(selector, name, value);
55836     },
55837     
55838     generateRules : function(cm){
55839         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
55840         Roo.util.CSS.removeStyleSheet(rulesId);
55841         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
55842             var cid = cm.getColumnId(i);
55843             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
55844                          this.tdSelector, cid, " {\n}\n",
55845                          this.hdSelector, cid, " {\n}\n",
55846                          this.splitSelector, cid, " {\n}\n");
55847         }
55848         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
55849     }
55850 });/*
55851  * Based on:
55852  * Ext JS Library 1.1.1
55853  * Copyright(c) 2006-2007, Ext JS, LLC.
55854  *
55855  * Originally Released Under LGPL - original licence link has changed is not relivant.
55856  *
55857  * Fork - LGPL
55858  * <script type="text/javascript">
55859  */
55860
55861 // private
55862 // This is a support class used internally by the Grid components
55863 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
55864     this.grid = grid;
55865     this.view = grid.getView();
55866     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
55867     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
55868     if(hd2){
55869         this.setHandleElId(Roo.id(hd));
55870         this.setOuterHandleElId(Roo.id(hd2));
55871     }
55872     this.scroll = false;
55873 };
55874 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
55875     maxDragWidth: 120,
55876     getDragData : function(e){
55877         var t = Roo.lib.Event.getTarget(e);
55878         var h = this.view.findHeaderCell(t);
55879         if(h){
55880             return {ddel: h.firstChild, header:h};
55881         }
55882         return false;
55883     },
55884
55885     onInitDrag : function(e){
55886         this.view.headersDisabled = true;
55887         var clone = this.dragData.ddel.cloneNode(true);
55888         clone.id = Roo.id();
55889         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
55890         this.proxy.update(clone);
55891         return true;
55892     },
55893
55894     afterValidDrop : function(){
55895         var v = this.view;
55896         setTimeout(function(){
55897             v.headersDisabled = false;
55898         }, 50);
55899     },
55900
55901     afterInvalidDrop : function(){
55902         var v = this.view;
55903         setTimeout(function(){
55904             v.headersDisabled = false;
55905         }, 50);
55906     }
55907 });
55908 /*
55909  * Based on:
55910  * Ext JS Library 1.1.1
55911  * Copyright(c) 2006-2007, Ext JS, LLC.
55912  *
55913  * Originally Released Under LGPL - original licence link has changed is not relivant.
55914  *
55915  * Fork - LGPL
55916  * <script type="text/javascript">
55917  */
55918 // private
55919 // This is a support class used internally by the Grid components
55920 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
55921     this.grid = grid;
55922     this.view = grid.getView();
55923     // split the proxies so they don't interfere with mouse events
55924     this.proxyTop = Roo.DomHelper.append(document.body, {
55925         cls:"col-move-top", html:"&#160;"
55926     }, true);
55927     this.proxyBottom = Roo.DomHelper.append(document.body, {
55928         cls:"col-move-bottom", html:"&#160;"
55929     }, true);
55930     this.proxyTop.hide = this.proxyBottom.hide = function(){
55931         this.setLeftTop(-100,-100);
55932         this.setStyle("visibility", "hidden");
55933     };
55934     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
55935     // temporarily disabled
55936     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
55937     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
55938 };
55939 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
55940     proxyOffsets : [-4, -9],
55941     fly: Roo.Element.fly,
55942
55943     getTargetFromEvent : function(e){
55944         var t = Roo.lib.Event.getTarget(e);
55945         var cindex = this.view.findCellIndex(t);
55946         if(cindex !== false){
55947             return this.view.getHeaderCell(cindex);
55948         }
55949         return null;
55950     },
55951
55952     nextVisible : function(h){
55953         var v = this.view, cm = this.grid.colModel;
55954         h = h.nextSibling;
55955         while(h){
55956             if(!cm.isHidden(v.getCellIndex(h))){
55957                 return h;
55958             }
55959             h = h.nextSibling;
55960         }
55961         return null;
55962     },
55963
55964     prevVisible : function(h){
55965         var v = this.view, cm = this.grid.colModel;
55966         h = h.prevSibling;
55967         while(h){
55968             if(!cm.isHidden(v.getCellIndex(h))){
55969                 return h;
55970             }
55971             h = h.prevSibling;
55972         }
55973         return null;
55974     },
55975
55976     positionIndicator : function(h, n, e){
55977         var x = Roo.lib.Event.getPageX(e);
55978         var r = Roo.lib.Dom.getRegion(n.firstChild);
55979         var px, pt, py = r.top + this.proxyOffsets[1];
55980         if((r.right - x) <= (r.right-r.left)/2){
55981             px = r.right+this.view.borderWidth;
55982             pt = "after";
55983         }else{
55984             px = r.left;
55985             pt = "before";
55986         }
55987         var oldIndex = this.view.getCellIndex(h);
55988         var newIndex = this.view.getCellIndex(n);
55989
55990         if(this.grid.colModel.isFixed(newIndex)){
55991             return false;
55992         }
55993
55994         var locked = this.grid.colModel.isLocked(newIndex);
55995
55996         if(pt == "after"){
55997             newIndex++;
55998         }
55999         if(oldIndex < newIndex){
56000             newIndex--;
56001         }
56002         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
56003             return false;
56004         }
56005         px +=  this.proxyOffsets[0];
56006         this.proxyTop.setLeftTop(px, py);
56007         this.proxyTop.show();
56008         if(!this.bottomOffset){
56009             this.bottomOffset = this.view.mainHd.getHeight();
56010         }
56011         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
56012         this.proxyBottom.show();
56013         return pt;
56014     },
56015
56016     onNodeEnter : function(n, dd, e, data){
56017         if(data.header != n){
56018             this.positionIndicator(data.header, n, e);
56019         }
56020     },
56021
56022     onNodeOver : function(n, dd, e, data){
56023         var result = false;
56024         if(data.header != n){
56025             result = this.positionIndicator(data.header, n, e);
56026         }
56027         if(!result){
56028             this.proxyTop.hide();
56029             this.proxyBottom.hide();
56030         }
56031         return result ? this.dropAllowed : this.dropNotAllowed;
56032     },
56033
56034     onNodeOut : function(n, dd, e, data){
56035         this.proxyTop.hide();
56036         this.proxyBottom.hide();
56037     },
56038
56039     onNodeDrop : function(n, dd, e, data){
56040         var h = data.header;
56041         if(h != n){
56042             var cm = this.grid.colModel;
56043             var x = Roo.lib.Event.getPageX(e);
56044             var r = Roo.lib.Dom.getRegion(n.firstChild);
56045             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
56046             var oldIndex = this.view.getCellIndex(h);
56047             var newIndex = this.view.getCellIndex(n);
56048             var locked = cm.isLocked(newIndex);
56049             if(pt == "after"){
56050                 newIndex++;
56051             }
56052             if(oldIndex < newIndex){
56053                 newIndex--;
56054             }
56055             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
56056                 return false;
56057             }
56058             cm.setLocked(oldIndex, locked, true);
56059             cm.moveColumn(oldIndex, newIndex);
56060             this.grid.fireEvent("columnmove", oldIndex, newIndex);
56061             return true;
56062         }
56063         return false;
56064     }
56065 });
56066 /*
56067  * Based on:
56068  * Ext JS Library 1.1.1
56069  * Copyright(c) 2006-2007, Ext JS, LLC.
56070  *
56071  * Originally Released Under LGPL - original licence link has changed is not relivant.
56072  *
56073  * Fork - LGPL
56074  * <script type="text/javascript">
56075  */
56076   
56077 /**
56078  * @class Roo.grid.GridView
56079  * @extends Roo.util.Observable
56080  *
56081  * @constructor
56082  * @param {Object} config
56083  */
56084 Roo.grid.GridView = function(config){
56085     Roo.grid.GridView.superclass.constructor.call(this);
56086     this.el = null;
56087
56088     Roo.apply(this, config);
56089 };
56090
56091 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
56092
56093     unselectable :  'unselectable="on"',
56094     unselectableCls :  'x-unselectable',
56095     
56096     
56097     rowClass : "x-grid-row",
56098
56099     cellClass : "x-grid-col",
56100
56101     tdClass : "x-grid-td",
56102
56103     hdClass : "x-grid-hd",
56104
56105     splitClass : "x-grid-split",
56106
56107     sortClasses : ["sort-asc", "sort-desc"],
56108
56109     enableMoveAnim : false,
56110
56111     hlColor: "C3DAF9",
56112
56113     dh : Roo.DomHelper,
56114
56115     fly : Roo.Element.fly,
56116
56117     css : Roo.util.CSS,
56118
56119     borderWidth: 1,
56120
56121     splitOffset: 3,
56122
56123     scrollIncrement : 22,
56124
56125     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
56126
56127     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
56128
56129     bind : function(ds, cm){
56130         if(this.ds){
56131             this.ds.un("load", this.onLoad, this);
56132             this.ds.un("datachanged", this.onDataChange, this);
56133             this.ds.un("add", this.onAdd, this);
56134             this.ds.un("remove", this.onRemove, this);
56135             this.ds.un("update", this.onUpdate, this);
56136             this.ds.un("clear", this.onClear, this);
56137         }
56138         if(ds){
56139             ds.on("load", this.onLoad, this);
56140             ds.on("datachanged", this.onDataChange, this);
56141             ds.on("add", this.onAdd, this);
56142             ds.on("remove", this.onRemove, this);
56143             ds.on("update", this.onUpdate, this);
56144             ds.on("clear", this.onClear, this);
56145         }
56146         this.ds = ds;
56147
56148         if(this.cm){
56149             this.cm.un("widthchange", this.onColWidthChange, this);
56150             this.cm.un("headerchange", this.onHeaderChange, this);
56151             this.cm.un("hiddenchange", this.onHiddenChange, this);
56152             this.cm.un("columnmoved", this.onColumnMove, this);
56153             this.cm.un("columnlockchange", this.onColumnLock, this);
56154         }
56155         if(cm){
56156             this.generateRules(cm);
56157             cm.on("widthchange", this.onColWidthChange, this);
56158             cm.on("headerchange", this.onHeaderChange, this);
56159             cm.on("hiddenchange", this.onHiddenChange, this);
56160             cm.on("columnmoved", this.onColumnMove, this);
56161             cm.on("columnlockchange", this.onColumnLock, this);
56162         }
56163         this.cm = cm;
56164     },
56165
56166     init: function(grid){
56167         Roo.grid.GridView.superclass.init.call(this, grid);
56168
56169         this.bind(grid.dataSource, grid.colModel);
56170
56171         grid.on("headerclick", this.handleHeaderClick, this);
56172
56173         if(grid.trackMouseOver){
56174             grid.on("mouseover", this.onRowOver, this);
56175             grid.on("mouseout", this.onRowOut, this);
56176         }
56177         grid.cancelTextSelection = function(){};
56178         this.gridId = grid.id;
56179
56180         var tpls = this.templates || {};
56181
56182         if(!tpls.master){
56183             tpls.master = new Roo.Template(
56184                '<div class="x-grid" hidefocus="true">',
56185                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
56186                   '<div class="x-grid-topbar"></div>',
56187                   '<div class="x-grid-scroller"><div></div></div>',
56188                   '<div class="x-grid-locked">',
56189                       '<div class="x-grid-header">{lockedHeader}</div>',
56190                       '<div class="x-grid-body">{lockedBody}</div>',
56191                   "</div>",
56192                   '<div class="x-grid-viewport">',
56193                       '<div class="x-grid-header">{header}</div>',
56194                       '<div class="x-grid-body">{body}</div>',
56195                   "</div>",
56196                   '<div class="x-grid-bottombar"></div>',
56197                  
56198                   '<div class="x-grid-resize-proxy">&#160;</div>',
56199                "</div>"
56200             );
56201             tpls.master.disableformats = true;
56202         }
56203
56204         if(!tpls.header){
56205             tpls.header = new Roo.Template(
56206                '<table border="0" cellspacing="0" cellpadding="0">',
56207                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
56208                "</table>{splits}"
56209             );
56210             tpls.header.disableformats = true;
56211         }
56212         tpls.header.compile();
56213
56214         if(!tpls.hcell){
56215             tpls.hcell = new Roo.Template(
56216                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
56217                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
56218                 "</div></td>"
56219              );
56220              tpls.hcell.disableFormats = true;
56221         }
56222         tpls.hcell.compile();
56223
56224         if(!tpls.hsplit){
56225             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
56226                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
56227             tpls.hsplit.disableFormats = true;
56228         }
56229         tpls.hsplit.compile();
56230
56231         if(!tpls.body){
56232             tpls.body = new Roo.Template(
56233                '<table border="0" cellspacing="0" cellpadding="0">',
56234                "<tbody>{rows}</tbody>",
56235                "</table>"
56236             );
56237             tpls.body.disableFormats = true;
56238         }
56239         tpls.body.compile();
56240
56241         if(!tpls.row){
56242             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
56243             tpls.row.disableFormats = true;
56244         }
56245         tpls.row.compile();
56246
56247         if(!tpls.cell){
56248             tpls.cell = new Roo.Template(
56249                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
56250                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
56251                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
56252                 "</td>"
56253             );
56254             tpls.cell.disableFormats = true;
56255         }
56256         tpls.cell.compile();
56257
56258         this.templates = tpls;
56259     },
56260
56261     // remap these for backwards compat
56262     onColWidthChange : function(){
56263         this.updateColumns.apply(this, arguments);
56264     },
56265     onHeaderChange : function(){
56266         this.updateHeaders.apply(this, arguments);
56267     }, 
56268     onHiddenChange : function(){
56269         this.handleHiddenChange.apply(this, arguments);
56270     },
56271     onColumnMove : function(){
56272         this.handleColumnMove.apply(this, arguments);
56273     },
56274     onColumnLock : function(){
56275         this.handleLockChange.apply(this, arguments);
56276     },
56277
56278     onDataChange : function(){
56279         this.refresh();
56280         this.updateHeaderSortState();
56281     },
56282
56283     onClear : function(){
56284         this.refresh();
56285     },
56286
56287     onUpdate : function(ds, record){
56288         this.refreshRow(record);
56289     },
56290
56291     refreshRow : function(record){
56292         var ds = this.ds, index;
56293         if(typeof record == 'number'){
56294             index = record;
56295             record = ds.getAt(index);
56296         }else{
56297             index = ds.indexOf(record);
56298         }
56299         this.insertRows(ds, index, index, true);
56300         this.onRemove(ds, record, index+1, true);
56301         this.syncRowHeights(index, index);
56302         this.layout();
56303         this.fireEvent("rowupdated", this, index, record);
56304     },
56305
56306     onAdd : function(ds, records, index){
56307         this.insertRows(ds, index, index + (records.length-1));
56308     },
56309
56310     onRemove : function(ds, record, index, isUpdate){
56311         if(isUpdate !== true){
56312             this.fireEvent("beforerowremoved", this, index, record);
56313         }
56314         var bt = this.getBodyTable(), lt = this.getLockedTable();
56315         if(bt.rows[index]){
56316             bt.firstChild.removeChild(bt.rows[index]);
56317         }
56318         if(lt.rows[index]){
56319             lt.firstChild.removeChild(lt.rows[index]);
56320         }
56321         if(isUpdate !== true){
56322             this.stripeRows(index);
56323             this.syncRowHeights(index, index);
56324             this.layout();
56325             this.fireEvent("rowremoved", this, index, record);
56326         }
56327     },
56328
56329     onLoad : function(){
56330         this.scrollToTop();
56331     },
56332
56333     /**
56334      * Scrolls the grid to the top
56335      */
56336     scrollToTop : function(){
56337         if(this.scroller){
56338             this.scroller.dom.scrollTop = 0;
56339             this.syncScroll();
56340         }
56341     },
56342
56343     /**
56344      * Gets a panel in the header of the grid that can be used for toolbars etc.
56345      * After modifying the contents of this panel a call to grid.autoSize() may be
56346      * required to register any changes in size.
56347      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
56348      * @return Roo.Element
56349      */
56350     getHeaderPanel : function(doShow){
56351         if(doShow){
56352             this.headerPanel.show();
56353         }
56354         return this.headerPanel;
56355     },
56356
56357     /**
56358      * Gets a panel in the footer of the grid that can be used for toolbars etc.
56359      * After modifying the contents of this panel a call to grid.autoSize() may be
56360      * required to register any changes in size.
56361      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
56362      * @return Roo.Element
56363      */
56364     getFooterPanel : function(doShow){
56365         if(doShow){
56366             this.footerPanel.show();
56367         }
56368         return this.footerPanel;
56369     },
56370
56371     initElements : function(){
56372         var E = Roo.Element;
56373         var el = this.grid.getGridEl().dom.firstChild;
56374         var cs = el.childNodes;
56375
56376         this.el = new E(el);
56377         
56378          this.focusEl = new E(el.firstChild);
56379         this.focusEl.swallowEvent("click", true);
56380         
56381         this.headerPanel = new E(cs[1]);
56382         this.headerPanel.enableDisplayMode("block");
56383
56384         this.scroller = new E(cs[2]);
56385         this.scrollSizer = new E(this.scroller.dom.firstChild);
56386
56387         this.lockedWrap = new E(cs[3]);
56388         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
56389         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
56390
56391         this.mainWrap = new E(cs[4]);
56392         this.mainHd = new E(this.mainWrap.dom.firstChild);
56393         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
56394
56395         this.footerPanel = new E(cs[5]);
56396         this.footerPanel.enableDisplayMode("block");
56397
56398         this.resizeProxy = new E(cs[6]);
56399
56400         this.headerSelector = String.format(
56401            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
56402            this.lockedHd.id, this.mainHd.id
56403         );
56404
56405         this.splitterSelector = String.format(
56406            '#{0} div.x-grid-split, #{1} div.x-grid-split',
56407            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
56408         );
56409     },
56410     idToCssName : function(s)
56411     {
56412         return s.replace(/[^a-z0-9]+/ig, '-');
56413     },
56414
56415     getHeaderCell : function(index){
56416         return Roo.DomQuery.select(this.headerSelector)[index];
56417     },
56418
56419     getHeaderCellMeasure : function(index){
56420         return this.getHeaderCell(index).firstChild;
56421     },
56422
56423     getHeaderCellText : function(index){
56424         return this.getHeaderCell(index).firstChild.firstChild;
56425     },
56426
56427     getLockedTable : function(){
56428         return this.lockedBody.dom.firstChild;
56429     },
56430
56431     getBodyTable : function(){
56432         return this.mainBody.dom.firstChild;
56433     },
56434
56435     getLockedRow : function(index){
56436         return this.getLockedTable().rows[index];
56437     },
56438
56439     getRow : function(index){
56440         return this.getBodyTable().rows[index];
56441     },
56442
56443     getRowComposite : function(index){
56444         if(!this.rowEl){
56445             this.rowEl = new Roo.CompositeElementLite();
56446         }
56447         var els = [], lrow, mrow;
56448         if(lrow = this.getLockedRow(index)){
56449             els.push(lrow);
56450         }
56451         if(mrow = this.getRow(index)){
56452             els.push(mrow);
56453         }
56454         this.rowEl.elements = els;
56455         return this.rowEl;
56456     },
56457     /**
56458      * Gets the 'td' of the cell
56459      * 
56460      * @param {Integer} rowIndex row to select
56461      * @param {Integer} colIndex column to select
56462      * 
56463      * @return {Object} 
56464      */
56465     getCell : function(rowIndex, colIndex){
56466         var locked = this.cm.getLockedCount();
56467         var source;
56468         if(colIndex < locked){
56469             source = this.lockedBody.dom.firstChild;
56470         }else{
56471             source = this.mainBody.dom.firstChild;
56472             colIndex -= locked;
56473         }
56474         return source.rows[rowIndex].childNodes[colIndex];
56475     },
56476
56477     getCellText : function(rowIndex, colIndex){
56478         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
56479     },
56480
56481     getCellBox : function(cell){
56482         var b = this.fly(cell).getBox();
56483         if(Roo.isOpera){ // opera fails to report the Y
56484             b.y = cell.offsetTop + this.mainBody.getY();
56485         }
56486         return b;
56487     },
56488
56489     getCellIndex : function(cell){
56490         var id = String(cell.className).match(this.cellRE);
56491         if(id){
56492             return parseInt(id[1], 10);
56493         }
56494         return 0;
56495     },
56496
56497     findHeaderIndex : function(n){
56498         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
56499         return r ? this.getCellIndex(r) : false;
56500     },
56501
56502     findHeaderCell : function(n){
56503         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
56504         return r ? r : false;
56505     },
56506
56507     findRowIndex : function(n){
56508         if(!n){
56509             return false;
56510         }
56511         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
56512         return r ? r.rowIndex : false;
56513     },
56514
56515     findCellIndex : function(node){
56516         var stop = this.el.dom;
56517         while(node && node != stop){
56518             if(this.findRE.test(node.className)){
56519                 return this.getCellIndex(node);
56520             }
56521             node = node.parentNode;
56522         }
56523         return false;
56524     },
56525
56526     getColumnId : function(index){
56527         return this.cm.getColumnId(index);
56528     },
56529
56530     getSplitters : function()
56531     {
56532         if(this.splitterSelector){
56533            return Roo.DomQuery.select(this.splitterSelector);
56534         }else{
56535             return null;
56536       }
56537     },
56538
56539     getSplitter : function(index){
56540         return this.getSplitters()[index];
56541     },
56542
56543     onRowOver : function(e, t){
56544         var row;
56545         if((row = this.findRowIndex(t)) !== false){
56546             this.getRowComposite(row).addClass("x-grid-row-over");
56547         }
56548     },
56549
56550     onRowOut : function(e, t){
56551         var row;
56552         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
56553             this.getRowComposite(row).removeClass("x-grid-row-over");
56554         }
56555     },
56556
56557     renderHeaders : function(){
56558         var cm = this.cm;
56559         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
56560         var cb = [], lb = [], sb = [], lsb = [], p = {};
56561         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
56562             p.cellId = "x-grid-hd-0-" + i;
56563             p.splitId = "x-grid-csplit-0-" + i;
56564             p.id = cm.getColumnId(i);
56565             p.value = cm.getColumnHeader(i) || "";
56566             p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</)  ? '' :  p.value  || "";
56567             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
56568             if(!cm.isLocked(i)){
56569                 cb[cb.length] = ct.apply(p);
56570                 sb[sb.length] = st.apply(p);
56571             }else{
56572                 lb[lb.length] = ct.apply(p);
56573                 lsb[lsb.length] = st.apply(p);
56574             }
56575         }
56576         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
56577                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
56578     },
56579
56580     updateHeaders : function(){
56581         var html = this.renderHeaders();
56582         this.lockedHd.update(html[0]);
56583         this.mainHd.update(html[1]);
56584     },
56585
56586     /**
56587      * Focuses the specified row.
56588      * @param {Number} row The row index
56589      */
56590     focusRow : function(row)
56591     {
56592         //Roo.log('GridView.focusRow');
56593         var x = this.scroller.dom.scrollLeft;
56594         this.focusCell(row, 0, false);
56595         this.scroller.dom.scrollLeft = x;
56596     },
56597
56598     /**
56599      * Focuses the specified cell.
56600      * @param {Number} row The row index
56601      * @param {Number} col The column index
56602      * @param {Boolean} hscroll false to disable horizontal scrolling
56603      */
56604     focusCell : function(row, col, hscroll)
56605     {
56606         //Roo.log('GridView.focusCell');
56607         var el = this.ensureVisible(row, col, hscroll);
56608         this.focusEl.alignTo(el, "tl-tl");
56609         if(Roo.isGecko){
56610             this.focusEl.focus();
56611         }else{
56612             this.focusEl.focus.defer(1, this.focusEl);
56613         }
56614     },
56615
56616     /**
56617      * Scrolls the specified cell into view
56618      * @param {Number} row The row index
56619      * @param {Number} col The column index
56620      * @param {Boolean} hscroll false to disable horizontal scrolling
56621      */
56622     ensureVisible : function(row, col, hscroll)
56623     {
56624         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
56625         //return null; //disable for testing.
56626         if(typeof row != "number"){
56627             row = row.rowIndex;
56628         }
56629         if(row < 0 && row >= this.ds.getCount()){
56630             return  null;
56631         }
56632         col = (col !== undefined ? col : 0);
56633         var cm = this.grid.colModel;
56634         while(cm.isHidden(col)){
56635             col++;
56636         }
56637
56638         var el = this.getCell(row, col);
56639         if(!el){
56640             return null;
56641         }
56642         var c = this.scroller.dom;
56643
56644         var ctop = parseInt(el.offsetTop, 10);
56645         var cleft = parseInt(el.offsetLeft, 10);
56646         var cbot = ctop + el.offsetHeight;
56647         var cright = cleft + el.offsetWidth;
56648         
56649         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
56650         var stop = parseInt(c.scrollTop, 10);
56651         var sleft = parseInt(c.scrollLeft, 10);
56652         var sbot = stop + ch;
56653         var sright = sleft + c.clientWidth;
56654         /*
56655         Roo.log('GridView.ensureVisible:' +
56656                 ' ctop:' + ctop +
56657                 ' c.clientHeight:' + c.clientHeight +
56658                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
56659                 ' stop:' + stop +
56660                 ' cbot:' + cbot +
56661                 ' sbot:' + sbot +
56662                 ' ch:' + ch  
56663                 );
56664         */
56665         if(ctop < stop){
56666              c.scrollTop = ctop;
56667             //Roo.log("set scrolltop to ctop DISABLE?");
56668         }else if(cbot > sbot){
56669             //Roo.log("set scrolltop to cbot-ch");
56670             c.scrollTop = cbot-ch;
56671         }
56672         
56673         if(hscroll !== false){
56674             if(cleft < sleft){
56675                 c.scrollLeft = cleft;
56676             }else if(cright > sright){
56677                 c.scrollLeft = cright-c.clientWidth;
56678             }
56679         }
56680          
56681         return el;
56682     },
56683
56684     updateColumns : function(){
56685         this.grid.stopEditing();
56686         var cm = this.grid.colModel, colIds = this.getColumnIds();
56687         //var totalWidth = cm.getTotalWidth();
56688         var pos = 0;
56689         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
56690             //if(cm.isHidden(i)) continue;
56691             var w = cm.getColumnWidth(i);
56692             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
56693             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
56694         }
56695         this.updateSplitters();
56696     },
56697
56698     generateRules : function(cm){
56699         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
56700         Roo.util.CSS.removeStyleSheet(rulesId);
56701         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
56702             var cid = cm.getColumnId(i);
56703             var align = '';
56704             if(cm.config[i].align){
56705                 align = 'text-align:'+cm.config[i].align+';';
56706             }
56707             var hidden = '';
56708             if(cm.isHidden(i)){
56709                 hidden = 'display:none;';
56710             }
56711             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
56712             ruleBuf.push(
56713                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
56714                     this.hdSelector, cid, " {\n", align, width, "}\n",
56715                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
56716                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
56717         }
56718         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
56719     },
56720
56721     updateSplitters : function(){
56722         var cm = this.cm, s = this.getSplitters();
56723         if(s){ // splitters not created yet
56724             var pos = 0, locked = true;
56725             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
56726                 if(cm.isHidden(i)) {
56727                     continue;
56728                 }
56729                 var w = cm.getColumnWidth(i); // make sure it's a number
56730                 if(!cm.isLocked(i) && locked){
56731                     pos = 0;
56732                     locked = false;
56733                 }
56734                 pos += w;
56735                 s[i].style.left = (pos-this.splitOffset) + "px";
56736             }
56737         }
56738     },
56739
56740     handleHiddenChange : function(colModel, colIndex, hidden){
56741         if(hidden){
56742             this.hideColumn(colIndex);
56743         }else{
56744             this.unhideColumn(colIndex);
56745         }
56746     },
56747
56748     hideColumn : function(colIndex){
56749         var cid = this.getColumnId(colIndex);
56750         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
56751         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
56752         if(Roo.isSafari){
56753             this.updateHeaders();
56754         }
56755         this.updateSplitters();
56756         this.layout();
56757     },
56758
56759     unhideColumn : function(colIndex){
56760         var cid = this.getColumnId(colIndex);
56761         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
56762         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
56763
56764         if(Roo.isSafari){
56765             this.updateHeaders();
56766         }
56767         this.updateSplitters();
56768         this.layout();
56769     },
56770
56771     insertRows : function(dm, firstRow, lastRow, isUpdate){
56772         if(firstRow == 0 && lastRow == dm.getCount()-1){
56773             this.refresh();
56774         }else{
56775             if(!isUpdate){
56776                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
56777             }
56778             var s = this.getScrollState();
56779             var markup = this.renderRows(firstRow, lastRow);
56780             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
56781             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
56782             this.restoreScroll(s);
56783             if(!isUpdate){
56784                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
56785                 this.syncRowHeights(firstRow, lastRow);
56786                 this.stripeRows(firstRow);
56787                 this.layout();
56788             }
56789         }
56790     },
56791
56792     bufferRows : function(markup, target, index){
56793         var before = null, trows = target.rows, tbody = target.tBodies[0];
56794         if(index < trows.length){
56795             before = trows[index];
56796         }
56797         var b = document.createElement("div");
56798         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
56799         var rows = b.firstChild.rows;
56800         for(var i = 0, len = rows.length; i < len; i++){
56801             if(before){
56802                 tbody.insertBefore(rows[0], before);
56803             }else{
56804                 tbody.appendChild(rows[0]);
56805             }
56806         }
56807         b.innerHTML = "";
56808         b = null;
56809     },
56810
56811     deleteRows : function(dm, firstRow, lastRow){
56812         if(dm.getRowCount()<1){
56813             this.fireEvent("beforerefresh", this);
56814             this.mainBody.update("");
56815             this.lockedBody.update("");
56816             this.fireEvent("refresh", this);
56817         }else{
56818             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
56819             var bt = this.getBodyTable();
56820             var tbody = bt.firstChild;
56821             var rows = bt.rows;
56822             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
56823                 tbody.removeChild(rows[firstRow]);
56824             }
56825             this.stripeRows(firstRow);
56826             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
56827         }
56828     },
56829
56830     updateRows : function(dataSource, firstRow, lastRow){
56831         var s = this.getScrollState();
56832         this.refresh();
56833         this.restoreScroll(s);
56834     },
56835
56836     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
56837         if(!noRefresh){
56838            this.refresh();
56839         }
56840         this.updateHeaderSortState();
56841     },
56842
56843     getScrollState : function(){
56844         
56845         var sb = this.scroller.dom;
56846         return {left: sb.scrollLeft, top: sb.scrollTop};
56847     },
56848
56849     stripeRows : function(startRow){
56850         if(!this.grid.stripeRows || this.ds.getCount() < 1){
56851             return;
56852         }
56853         startRow = startRow || 0;
56854         var rows = this.getBodyTable().rows;
56855         var lrows = this.getLockedTable().rows;
56856         var cls = ' x-grid-row-alt ';
56857         for(var i = startRow, len = rows.length; i < len; i++){
56858             var row = rows[i], lrow = lrows[i];
56859             var isAlt = ((i+1) % 2 == 0);
56860             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
56861             if(isAlt == hasAlt){
56862                 continue;
56863             }
56864             if(isAlt){
56865                 row.className += " x-grid-row-alt";
56866             }else{
56867                 row.className = row.className.replace("x-grid-row-alt", "");
56868             }
56869             if(lrow){
56870                 lrow.className = row.className;
56871             }
56872         }
56873     },
56874
56875     restoreScroll : function(state){
56876         //Roo.log('GridView.restoreScroll');
56877         var sb = this.scroller.dom;
56878         sb.scrollLeft = state.left;
56879         sb.scrollTop = state.top;
56880         this.syncScroll();
56881     },
56882
56883     syncScroll : function(){
56884         //Roo.log('GridView.syncScroll');
56885         var sb = this.scroller.dom;
56886         var sh = this.mainHd.dom;
56887         var bs = this.mainBody.dom;
56888         var lv = this.lockedBody.dom;
56889         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
56890         lv.scrollTop = bs.scrollTop = sb.scrollTop;
56891     },
56892
56893     handleScroll : function(e){
56894         this.syncScroll();
56895         var sb = this.scroller.dom;
56896         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
56897         e.stopEvent();
56898     },
56899
56900     handleWheel : function(e){
56901         var d = e.getWheelDelta();
56902         this.scroller.dom.scrollTop -= d*22;
56903         // set this here to prevent jumpy scrolling on large tables
56904         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
56905         e.stopEvent();
56906     },
56907
56908     renderRows : function(startRow, endRow){
56909         // pull in all the crap needed to render rows
56910         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
56911         var colCount = cm.getColumnCount();
56912
56913         if(ds.getCount() < 1){
56914             return ["", ""];
56915         }
56916
56917         // build a map for all the columns
56918         var cs = [];
56919         for(var i = 0; i < colCount; i++){
56920             var name = cm.getDataIndex(i);
56921             cs[i] = {
56922                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
56923                 renderer : cm.getRenderer(i),
56924                 id : cm.getColumnId(i),
56925                 locked : cm.isLocked(i),
56926                 has_editor : cm.isCellEditable(i)
56927             };
56928         }
56929
56930         startRow = startRow || 0;
56931         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
56932
56933         // records to render
56934         var rs = ds.getRange(startRow, endRow);
56935
56936         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
56937     },
56938
56939     // As much as I hate to duplicate code, this was branched because FireFox really hates
56940     // [].join("") on strings. The performance difference was substantial enough to
56941     // branch this function
56942     doRender : Roo.isGecko ?
56943             function(cs, rs, ds, startRow, colCount, stripe){
56944                 var ts = this.templates, ct = ts.cell, rt = ts.row;
56945                 // buffers
56946                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
56947                 
56948                 var hasListener = this.grid.hasListener('rowclass');
56949                 var rowcfg = {};
56950                 for(var j = 0, len = rs.length; j < len; j++){
56951                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
56952                     for(var i = 0; i < colCount; i++){
56953                         c = cs[i];
56954                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
56955                         p.id = c.id;
56956                         p.css = p.attr = "";
56957                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
56958                         if(p.value == undefined || p.value === "") {
56959                             p.value = "&#160;";
56960                         }
56961                         if(c.has_editor){
56962                             p.css += ' x-grid-editable-cell';
56963                         }
56964                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
56965                             p.css +=  ' x-grid-dirty-cell';
56966                         }
56967                         var markup = ct.apply(p);
56968                         if(!c.locked){
56969                             cb+= markup;
56970                         }else{
56971                             lcb+= markup;
56972                         }
56973                     }
56974                     var alt = [];
56975                     if(stripe && ((rowIndex+1) % 2 == 0)){
56976                         alt.push("x-grid-row-alt")
56977                     }
56978                     if(r.dirty){
56979                         alt.push(  " x-grid-dirty-row");
56980                     }
56981                     rp.cells = lcb;
56982                     if(this.getRowClass){
56983                         alt.push(this.getRowClass(r, rowIndex));
56984                     }
56985                     if (hasListener) {
56986                         rowcfg = {
56987                              
56988                             record: r,
56989                             rowIndex : rowIndex,
56990                             rowClass : ''
56991                         };
56992                         this.grid.fireEvent('rowclass', this, rowcfg);
56993                         alt.push(rowcfg.rowClass);
56994                     }
56995                     rp.alt = alt.join(" ");
56996                     lbuf+= rt.apply(rp);
56997                     rp.cells = cb;
56998                     buf+=  rt.apply(rp);
56999                 }
57000                 return [lbuf, buf];
57001             } :
57002             function(cs, rs, ds, startRow, colCount, stripe){
57003                 var ts = this.templates, ct = ts.cell, rt = ts.row;
57004                 // buffers
57005                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
57006                 var hasListener = this.grid.hasListener('rowclass');
57007  
57008                 var rowcfg = {};
57009                 for(var j = 0, len = rs.length; j < len; j++){
57010                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
57011                     for(var i = 0; i < colCount; i++){
57012                         c = cs[i];
57013                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
57014                         p.id = c.id;
57015                         p.css = p.attr = "";
57016                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
57017                         if(p.value == undefined || p.value === "") {
57018                             p.value = "&#160;";
57019                         }
57020                         //Roo.log(c);
57021                          if(c.has_editor){
57022                             p.css += ' x-grid-editable-cell';
57023                         }
57024                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
57025                             p.css += ' x-grid-dirty-cell' 
57026                         }
57027                         
57028                         var markup = ct.apply(p);
57029                         if(!c.locked){
57030                             cb[cb.length] = markup;
57031                         }else{
57032                             lcb[lcb.length] = markup;
57033                         }
57034                     }
57035                     var alt = [];
57036                     if(stripe && ((rowIndex+1) % 2 == 0)){
57037                         alt.push( "x-grid-row-alt");
57038                     }
57039                     if(r.dirty){
57040                         alt.push(" x-grid-dirty-row");
57041                     }
57042                     rp.cells = lcb;
57043                     if(this.getRowClass){
57044                         alt.push( this.getRowClass(r, rowIndex));
57045                     }
57046                     if (hasListener) {
57047                         rowcfg = {
57048                              
57049                             record: r,
57050                             rowIndex : rowIndex,
57051                             rowClass : ''
57052                         };
57053                         this.grid.fireEvent('rowclass', this, rowcfg);
57054                         alt.push(rowcfg.rowClass);
57055                     }
57056                     
57057                     rp.alt = alt.join(" ");
57058                     rp.cells = lcb.join("");
57059                     lbuf[lbuf.length] = rt.apply(rp);
57060                     rp.cells = cb.join("");
57061                     buf[buf.length] =  rt.apply(rp);
57062                 }
57063                 return [lbuf.join(""), buf.join("")];
57064             },
57065
57066     renderBody : function(){
57067         var markup = this.renderRows();
57068         var bt = this.templates.body;
57069         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
57070     },
57071
57072     /**
57073      * Refreshes the grid
57074      * @param {Boolean} headersToo
57075      */
57076     refresh : function(headersToo){
57077         this.fireEvent("beforerefresh", this);
57078         this.grid.stopEditing();
57079         var result = this.renderBody();
57080         this.lockedBody.update(result[0]);
57081         this.mainBody.update(result[1]);
57082         if(headersToo === true){
57083             this.updateHeaders();
57084             this.updateColumns();
57085             this.updateSplitters();
57086             this.updateHeaderSortState();
57087         }
57088         this.syncRowHeights();
57089         this.layout();
57090         this.fireEvent("refresh", this);
57091     },
57092
57093     handleColumnMove : function(cm, oldIndex, newIndex){
57094         this.indexMap = null;
57095         var s = this.getScrollState();
57096         this.refresh(true);
57097         this.restoreScroll(s);
57098         this.afterMove(newIndex);
57099     },
57100
57101     afterMove : function(colIndex){
57102         if(this.enableMoveAnim && Roo.enableFx){
57103             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
57104         }
57105         // if multisort - fix sortOrder, and reload..
57106         if (this.grid.dataSource.multiSort) {
57107             // the we can call sort again..
57108             var dm = this.grid.dataSource;
57109             var cm = this.grid.colModel;
57110             var so = [];
57111             for(var i = 0; i < cm.config.length; i++ ) {
57112                 
57113                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
57114                     continue; // dont' bother, it's not in sort list or being set.
57115                 }
57116                 
57117                 so.push(cm.config[i].dataIndex);
57118             };
57119             dm.sortOrder = so;
57120             dm.load(dm.lastOptions);
57121             
57122             
57123         }
57124         
57125     },
57126
57127     updateCell : function(dm, rowIndex, dataIndex){
57128         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
57129         if(typeof colIndex == "undefined"){ // not present in grid
57130             return;
57131         }
57132         var cm = this.grid.colModel;
57133         var cell = this.getCell(rowIndex, colIndex);
57134         var cellText = this.getCellText(rowIndex, colIndex);
57135
57136         var p = {
57137             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
57138             id : cm.getColumnId(colIndex),
57139             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
57140         };
57141         var renderer = cm.getRenderer(colIndex);
57142         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
57143         if(typeof val == "undefined" || val === "") {
57144             val = "&#160;";
57145         }
57146         cellText.innerHTML = val;
57147         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
57148         this.syncRowHeights(rowIndex, rowIndex);
57149     },
57150
57151     calcColumnWidth : function(colIndex, maxRowsToMeasure){
57152         var maxWidth = 0;
57153         if(this.grid.autoSizeHeaders){
57154             var h = this.getHeaderCellMeasure(colIndex);
57155             maxWidth = Math.max(maxWidth, h.scrollWidth);
57156         }
57157         var tb, index;
57158         if(this.cm.isLocked(colIndex)){
57159             tb = this.getLockedTable();
57160             index = colIndex;
57161         }else{
57162             tb = this.getBodyTable();
57163             index = colIndex - this.cm.getLockedCount();
57164         }
57165         if(tb && tb.rows){
57166             var rows = tb.rows;
57167             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
57168             for(var i = 0; i < stopIndex; i++){
57169                 var cell = rows[i].childNodes[index].firstChild;
57170                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
57171             }
57172         }
57173         return maxWidth + /*margin for error in IE*/ 5;
57174     },
57175     /**
57176      * Autofit a column to its content.
57177      * @param {Number} colIndex
57178      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
57179      */
57180      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
57181          if(this.cm.isHidden(colIndex)){
57182              return; // can't calc a hidden column
57183          }
57184         if(forceMinSize){
57185             var cid = this.cm.getColumnId(colIndex);
57186             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
57187            if(this.grid.autoSizeHeaders){
57188                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
57189            }
57190         }
57191         var newWidth = this.calcColumnWidth(colIndex);
57192         this.cm.setColumnWidth(colIndex,
57193             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
57194         if(!suppressEvent){
57195             this.grid.fireEvent("columnresize", colIndex, newWidth);
57196         }
57197     },
57198
57199     /**
57200      * Autofits all columns to their content and then expands to fit any extra space in the grid
57201      */
57202      autoSizeColumns : function(){
57203         var cm = this.grid.colModel;
57204         var colCount = cm.getColumnCount();
57205         for(var i = 0; i < colCount; i++){
57206             this.autoSizeColumn(i, true, true);
57207         }
57208         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
57209             this.fitColumns();
57210         }else{
57211             this.updateColumns();
57212             this.layout();
57213         }
57214     },
57215
57216     /**
57217      * Autofits all columns to the grid's width proportionate with their current size
57218      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
57219      */
57220     fitColumns : function(reserveScrollSpace){
57221         var cm = this.grid.colModel;
57222         var colCount = cm.getColumnCount();
57223         var cols = [];
57224         var width = 0;
57225         var i, w;
57226         for (i = 0; i < colCount; i++){
57227             if(!cm.isHidden(i) && !cm.isFixed(i)){
57228                 w = cm.getColumnWidth(i);
57229                 cols.push(i);
57230                 cols.push(w);
57231                 width += w;
57232             }
57233         }
57234         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
57235         if(reserveScrollSpace){
57236             avail -= 17;
57237         }
57238         var frac = (avail - cm.getTotalWidth())/width;
57239         while (cols.length){
57240             w = cols.pop();
57241             i = cols.pop();
57242             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
57243         }
57244         this.updateColumns();
57245         this.layout();
57246     },
57247
57248     onRowSelect : function(rowIndex){
57249         var row = this.getRowComposite(rowIndex);
57250         row.addClass("x-grid-row-selected");
57251     },
57252
57253     onRowDeselect : function(rowIndex){
57254         var row = this.getRowComposite(rowIndex);
57255         row.removeClass("x-grid-row-selected");
57256     },
57257
57258     onCellSelect : function(row, col){
57259         var cell = this.getCell(row, col);
57260         if(cell){
57261             Roo.fly(cell).addClass("x-grid-cell-selected");
57262         }
57263     },
57264
57265     onCellDeselect : function(row, col){
57266         var cell = this.getCell(row, col);
57267         if(cell){
57268             Roo.fly(cell).removeClass("x-grid-cell-selected");
57269         }
57270     },
57271
57272     updateHeaderSortState : function(){
57273         
57274         // sort state can be single { field: xxx, direction : yyy}
57275         // or   { xxx=>ASC , yyy : DESC ..... }
57276         
57277         var mstate = {};
57278         if (!this.ds.multiSort) { 
57279             var state = this.ds.getSortState();
57280             if(!state){
57281                 return;
57282             }
57283             mstate[state.field] = state.direction;
57284             // FIXME... - this is not used here.. but might be elsewhere..
57285             this.sortState = state;
57286             
57287         } else {
57288             mstate = this.ds.sortToggle;
57289         }
57290         //remove existing sort classes..
57291         
57292         var sc = this.sortClasses;
57293         var hds = this.el.select(this.headerSelector).removeClass(sc);
57294         
57295         for(var f in mstate) {
57296         
57297             var sortColumn = this.cm.findColumnIndex(f);
57298             
57299             if(sortColumn != -1){
57300                 var sortDir = mstate[f];        
57301                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
57302             }
57303         }
57304         
57305          
57306         
57307     },
57308
57309
57310     handleHeaderClick : function(g, index,e){
57311         
57312         Roo.log("header click");
57313         
57314         if (Roo.isTouch) {
57315             // touch events on header are handled by context
57316             this.handleHdCtx(g,index,e);
57317             return;
57318         }
57319         
57320         
57321         if(this.headersDisabled){
57322             return;
57323         }
57324         var dm = g.dataSource, cm = g.colModel;
57325         if(!cm.isSortable(index)){
57326             return;
57327         }
57328         g.stopEditing();
57329         
57330         if (dm.multiSort) {
57331             // update the sortOrder
57332             var so = [];
57333             for(var i = 0; i < cm.config.length; i++ ) {
57334                 
57335                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
57336                     continue; // dont' bother, it's not in sort list or being set.
57337                 }
57338                 
57339                 so.push(cm.config[i].dataIndex);
57340             };
57341             dm.sortOrder = so;
57342         }
57343         
57344         
57345         dm.sort(cm.getDataIndex(index));
57346     },
57347
57348
57349     destroy : function(){
57350         if(this.colMenu){
57351             this.colMenu.removeAll();
57352             Roo.menu.MenuMgr.unregister(this.colMenu);
57353             this.colMenu.getEl().remove();
57354             delete this.colMenu;
57355         }
57356         if(this.hmenu){
57357             this.hmenu.removeAll();
57358             Roo.menu.MenuMgr.unregister(this.hmenu);
57359             this.hmenu.getEl().remove();
57360             delete this.hmenu;
57361         }
57362         if(this.grid.enableColumnMove){
57363             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
57364             if(dds){
57365                 for(var dd in dds){
57366                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
57367                         var elid = dds[dd].dragElId;
57368                         dds[dd].unreg();
57369                         Roo.get(elid).remove();
57370                     } else if(dds[dd].config.isTarget){
57371                         dds[dd].proxyTop.remove();
57372                         dds[dd].proxyBottom.remove();
57373                         dds[dd].unreg();
57374                     }
57375                     if(Roo.dd.DDM.locationCache[dd]){
57376                         delete Roo.dd.DDM.locationCache[dd];
57377                     }
57378                 }
57379                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
57380             }
57381         }
57382         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
57383         this.bind(null, null);
57384         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
57385     },
57386
57387     handleLockChange : function(){
57388         this.refresh(true);
57389     },
57390
57391     onDenyColumnLock : function(){
57392
57393     },
57394
57395     onDenyColumnHide : function(){
57396
57397     },
57398
57399     handleHdMenuClick : function(item){
57400         var index = this.hdCtxIndex;
57401         var cm = this.cm, ds = this.ds;
57402         switch(item.id){
57403             case "asc":
57404                 ds.sort(cm.getDataIndex(index), "ASC");
57405                 break;
57406             case "desc":
57407                 ds.sort(cm.getDataIndex(index), "DESC");
57408                 break;
57409             case "lock":
57410                 var lc = cm.getLockedCount();
57411                 if(cm.getColumnCount(true) <= lc+1){
57412                     this.onDenyColumnLock();
57413                     return;
57414                 }
57415                 if(lc != index){
57416                     cm.setLocked(index, true, true);
57417                     cm.moveColumn(index, lc);
57418                     this.grid.fireEvent("columnmove", index, lc);
57419                 }else{
57420                     cm.setLocked(index, true);
57421                 }
57422             break;
57423             case "unlock":
57424                 var lc = cm.getLockedCount();
57425                 if((lc-1) != index){
57426                     cm.setLocked(index, false, true);
57427                     cm.moveColumn(index, lc-1);
57428                     this.grid.fireEvent("columnmove", index, lc-1);
57429                 }else{
57430                     cm.setLocked(index, false);
57431                 }
57432             break;
57433             case 'wider': // used to expand cols on touch..
57434             case 'narrow':
57435                 var cw = cm.getColumnWidth(index);
57436                 cw += (item.id == 'wider' ? 1 : -1) * 50;
57437                 cw = Math.max(0, cw);
57438                 cw = Math.min(cw,4000);
57439                 cm.setColumnWidth(index, cw);
57440                 break;
57441                 
57442             default:
57443                 index = cm.getIndexById(item.id.substr(4));
57444                 if(index != -1){
57445                     if(item.checked && cm.getColumnCount(true) <= 1){
57446                         this.onDenyColumnHide();
57447                         return false;
57448                     }
57449                     cm.setHidden(index, item.checked);
57450                 }
57451         }
57452         return true;
57453     },
57454
57455     beforeColMenuShow : function(){
57456         var cm = this.cm,  colCount = cm.getColumnCount();
57457         this.colMenu.removeAll();
57458         for(var i = 0; i < colCount; i++){
57459             this.colMenu.add(new Roo.menu.CheckItem({
57460                 id: "col-"+cm.getColumnId(i),
57461                 text: cm.getColumnHeader(i),
57462                 checked: !cm.isHidden(i),
57463                 hideOnClick:false
57464             }));
57465         }
57466     },
57467
57468     handleHdCtx : function(g, index, e){
57469         e.stopEvent();
57470         var hd = this.getHeaderCell(index);
57471         this.hdCtxIndex = index;
57472         var ms = this.hmenu.items, cm = this.cm;
57473         ms.get("asc").setDisabled(!cm.isSortable(index));
57474         ms.get("desc").setDisabled(!cm.isSortable(index));
57475         if(this.grid.enableColLock !== false){
57476             ms.get("lock").setDisabled(cm.isLocked(index));
57477             ms.get("unlock").setDisabled(!cm.isLocked(index));
57478         }
57479         this.hmenu.show(hd, "tl-bl");
57480     },
57481
57482     handleHdOver : function(e){
57483         var hd = this.findHeaderCell(e.getTarget());
57484         if(hd && !this.headersDisabled){
57485             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
57486                this.fly(hd).addClass("x-grid-hd-over");
57487             }
57488         }
57489     },
57490
57491     handleHdOut : function(e){
57492         var hd = this.findHeaderCell(e.getTarget());
57493         if(hd){
57494             this.fly(hd).removeClass("x-grid-hd-over");
57495         }
57496     },
57497
57498     handleSplitDblClick : function(e, t){
57499         var i = this.getCellIndex(t);
57500         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
57501             this.autoSizeColumn(i, true);
57502             this.layout();
57503         }
57504     },
57505
57506     render : function(){
57507
57508         var cm = this.cm;
57509         var colCount = cm.getColumnCount();
57510
57511         if(this.grid.monitorWindowResize === true){
57512             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
57513         }
57514         var header = this.renderHeaders();
57515         var body = this.templates.body.apply({rows:""});
57516         var html = this.templates.master.apply({
57517             lockedBody: body,
57518             body: body,
57519             lockedHeader: header[0],
57520             header: header[1]
57521         });
57522
57523         //this.updateColumns();
57524
57525         this.grid.getGridEl().dom.innerHTML = html;
57526
57527         this.initElements();
57528         
57529         // a kludge to fix the random scolling effect in webkit
57530         this.el.on("scroll", function() {
57531             this.el.dom.scrollTop=0; // hopefully not recursive..
57532         },this);
57533
57534         this.scroller.on("scroll", this.handleScroll, this);
57535         this.lockedBody.on("mousewheel", this.handleWheel, this);
57536         this.mainBody.on("mousewheel", this.handleWheel, this);
57537
57538         this.mainHd.on("mouseover", this.handleHdOver, this);
57539         this.mainHd.on("mouseout", this.handleHdOut, this);
57540         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
57541                 {delegate: "."+this.splitClass});
57542
57543         this.lockedHd.on("mouseover", this.handleHdOver, this);
57544         this.lockedHd.on("mouseout", this.handleHdOut, this);
57545         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
57546                 {delegate: "."+this.splitClass});
57547
57548         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
57549             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
57550         }
57551
57552         this.updateSplitters();
57553
57554         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
57555             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
57556             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
57557         }
57558
57559         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
57560             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
57561             this.hmenu.add(
57562                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
57563                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
57564             );
57565             if(this.grid.enableColLock !== false){
57566                 this.hmenu.add('-',
57567                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
57568                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
57569                 );
57570             }
57571             if (Roo.isTouch) {
57572                  this.hmenu.add('-',
57573                     {id:"wider", text: this.columnsWiderText},
57574                     {id:"narrow", text: this.columnsNarrowText }
57575                 );
57576                 
57577                  
57578             }
57579             
57580             if(this.grid.enableColumnHide !== false){
57581
57582                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
57583                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
57584                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
57585
57586                 this.hmenu.add('-',
57587                     {id:"columns", text: this.columnsText, menu: this.colMenu}
57588                 );
57589             }
57590             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
57591
57592             this.grid.on("headercontextmenu", this.handleHdCtx, this);
57593         }
57594
57595         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
57596             this.dd = new Roo.grid.GridDragZone(this.grid, {
57597                 ddGroup : this.grid.ddGroup || 'GridDD'
57598             });
57599             
57600         }
57601
57602         /*
57603         for(var i = 0; i < colCount; i++){
57604             if(cm.isHidden(i)){
57605                 this.hideColumn(i);
57606             }
57607             if(cm.config[i].align){
57608                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
57609                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
57610             }
57611         }*/
57612         
57613         this.updateHeaderSortState();
57614
57615         this.beforeInitialResize();
57616         this.layout(true);
57617
57618         // two part rendering gives faster view to the user
57619         this.renderPhase2.defer(1, this);
57620     },
57621
57622     renderPhase2 : function(){
57623         // render the rows now
57624         this.refresh();
57625         if(this.grid.autoSizeColumns){
57626             this.autoSizeColumns();
57627         }
57628     },
57629
57630     beforeInitialResize : function(){
57631
57632     },
57633
57634     onColumnSplitterMoved : function(i, w){
57635         this.userResized = true;
57636         var cm = this.grid.colModel;
57637         cm.setColumnWidth(i, w, true);
57638         var cid = cm.getColumnId(i);
57639         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
57640         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
57641         this.updateSplitters();
57642         this.layout();
57643         this.grid.fireEvent("columnresize", i, w);
57644     },
57645
57646     syncRowHeights : function(startIndex, endIndex){
57647         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
57648             startIndex = startIndex || 0;
57649             var mrows = this.getBodyTable().rows;
57650             var lrows = this.getLockedTable().rows;
57651             var len = mrows.length-1;
57652             endIndex = Math.min(endIndex || len, len);
57653             for(var i = startIndex; i <= endIndex; i++){
57654                 var m = mrows[i], l = lrows[i];
57655                 var h = Math.max(m.offsetHeight, l.offsetHeight);
57656                 m.style.height = l.style.height = h + "px";
57657             }
57658         }
57659     },
57660
57661     layout : function(initialRender, is2ndPass){
57662         var g = this.grid;
57663         var auto = g.autoHeight;
57664         var scrollOffset = 16;
57665         var c = g.getGridEl(), cm = this.cm,
57666                 expandCol = g.autoExpandColumn,
57667                 gv = this;
57668         //c.beginMeasure();
57669
57670         if(!c.dom.offsetWidth){ // display:none?
57671             if(initialRender){
57672                 this.lockedWrap.show();
57673                 this.mainWrap.show();
57674             }
57675             return;
57676         }
57677
57678         var hasLock = this.cm.isLocked(0);
57679
57680         var tbh = this.headerPanel.getHeight();
57681         var bbh = this.footerPanel.getHeight();
57682
57683         if(auto){
57684             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
57685             var newHeight = ch + c.getBorderWidth("tb");
57686             if(g.maxHeight){
57687                 newHeight = Math.min(g.maxHeight, newHeight);
57688             }
57689             c.setHeight(newHeight);
57690         }
57691
57692         if(g.autoWidth){
57693             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
57694         }
57695
57696         var s = this.scroller;
57697
57698         var csize = c.getSize(true);
57699
57700         this.el.setSize(csize.width, csize.height);
57701
57702         this.headerPanel.setWidth(csize.width);
57703         this.footerPanel.setWidth(csize.width);
57704
57705         var hdHeight = this.mainHd.getHeight();
57706         var vw = csize.width;
57707         var vh = csize.height - (tbh + bbh);
57708
57709         s.setSize(vw, vh);
57710
57711         var bt = this.getBodyTable();
57712         
57713         if(cm.getLockedCount() == cm.config.length){
57714             bt = this.getLockedTable();
57715         }
57716         
57717         var ltWidth = hasLock ?
57718                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
57719
57720         var scrollHeight = bt.offsetHeight;
57721         var scrollWidth = ltWidth + bt.offsetWidth;
57722         var vscroll = false, hscroll = false;
57723
57724         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
57725
57726         var lw = this.lockedWrap, mw = this.mainWrap;
57727         var lb = this.lockedBody, mb = this.mainBody;
57728
57729         setTimeout(function(){
57730             var t = s.dom.offsetTop;
57731             var w = s.dom.clientWidth,
57732                 h = s.dom.clientHeight;
57733
57734             lw.setTop(t);
57735             lw.setSize(ltWidth, h);
57736
57737             mw.setLeftTop(ltWidth, t);
57738             mw.setSize(w-ltWidth, h);
57739
57740             lb.setHeight(h-hdHeight);
57741             mb.setHeight(h-hdHeight);
57742
57743             if(is2ndPass !== true && !gv.userResized && expandCol){
57744                 // high speed resize without full column calculation
57745                 
57746                 var ci = cm.getIndexById(expandCol);
57747                 if (ci < 0) {
57748                     ci = cm.findColumnIndex(expandCol);
57749                 }
57750                 ci = Math.max(0, ci); // make sure it's got at least the first col.
57751                 var expandId = cm.getColumnId(ci);
57752                 var  tw = cm.getTotalWidth(false);
57753                 var currentWidth = cm.getColumnWidth(ci);
57754                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
57755                 if(currentWidth != cw){
57756                     cm.setColumnWidth(ci, cw, true);
57757                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
57758                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
57759                     gv.updateSplitters();
57760                     gv.layout(false, true);
57761                 }
57762             }
57763
57764             if(initialRender){
57765                 lw.show();
57766                 mw.show();
57767             }
57768             //c.endMeasure();
57769         }, 10);
57770     },
57771
57772     onWindowResize : function(){
57773         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
57774             return;
57775         }
57776         this.layout();
57777     },
57778
57779     appendFooter : function(parentEl){
57780         return null;
57781     },
57782
57783     sortAscText : "Sort Ascending",
57784     sortDescText : "Sort Descending",
57785     lockText : "Lock Column",
57786     unlockText : "Unlock Column",
57787     columnsText : "Columns",
57788  
57789     columnsWiderText : "Wider",
57790     columnsNarrowText : "Thinner"
57791 });
57792
57793
57794 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
57795     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
57796     this.proxy.el.addClass('x-grid3-col-dd');
57797 };
57798
57799 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
57800     handleMouseDown : function(e){
57801
57802     },
57803
57804     callHandleMouseDown : function(e){
57805         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
57806     }
57807 });
57808 /*
57809  * Based on:
57810  * Ext JS Library 1.1.1
57811  * Copyright(c) 2006-2007, Ext JS, LLC.
57812  *
57813  * Originally Released Under LGPL - original licence link has changed is not relivant.
57814  *
57815  * Fork - LGPL
57816  * <script type="text/javascript">
57817  */
57818  
57819 // private
57820 // This is a support class used internally by the Grid components
57821 Roo.grid.SplitDragZone = function(grid, hd, hd2){
57822     this.grid = grid;
57823     this.view = grid.getView();
57824     this.proxy = this.view.resizeProxy;
57825     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
57826         "gridSplitters" + this.grid.getGridEl().id, {
57827         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
57828     });
57829     this.setHandleElId(Roo.id(hd));
57830     this.setOuterHandleElId(Roo.id(hd2));
57831     this.scroll = false;
57832 };
57833 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
57834     fly: Roo.Element.fly,
57835
57836     b4StartDrag : function(x, y){
57837         this.view.headersDisabled = true;
57838         this.proxy.setHeight(this.view.mainWrap.getHeight());
57839         var w = this.cm.getColumnWidth(this.cellIndex);
57840         var minw = Math.max(w-this.grid.minColumnWidth, 0);
57841         this.resetConstraints();
57842         this.setXConstraint(minw, 1000);
57843         this.setYConstraint(0, 0);
57844         this.minX = x - minw;
57845         this.maxX = x + 1000;
57846         this.startPos = x;
57847         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
57848     },
57849
57850
57851     handleMouseDown : function(e){
57852         ev = Roo.EventObject.setEvent(e);
57853         var t = this.fly(ev.getTarget());
57854         if(t.hasClass("x-grid-split")){
57855             this.cellIndex = this.view.getCellIndex(t.dom);
57856             this.split = t.dom;
57857             this.cm = this.grid.colModel;
57858             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
57859                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
57860             }
57861         }
57862     },
57863
57864     endDrag : function(e){
57865         this.view.headersDisabled = false;
57866         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
57867         var diff = endX - this.startPos;
57868         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
57869     },
57870
57871     autoOffset : function(){
57872         this.setDelta(0,0);
57873     }
57874 });/*
57875  * Based on:
57876  * Ext JS Library 1.1.1
57877  * Copyright(c) 2006-2007, Ext JS, LLC.
57878  *
57879  * Originally Released Under LGPL - original licence link has changed is not relivant.
57880  *
57881  * Fork - LGPL
57882  * <script type="text/javascript">
57883  */
57884  
57885 // private
57886 // This is a support class used internally by the Grid components
57887 Roo.grid.GridDragZone = function(grid, config){
57888     this.view = grid.getView();
57889     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
57890     if(this.view.lockedBody){
57891         this.setHandleElId(Roo.id(this.view.mainBody.dom));
57892         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
57893     }
57894     this.scroll = false;
57895     this.grid = grid;
57896     this.ddel = document.createElement('div');
57897     this.ddel.className = 'x-grid-dd-wrap';
57898 };
57899
57900 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
57901     ddGroup : "GridDD",
57902
57903     getDragData : function(e){
57904         var t = Roo.lib.Event.getTarget(e);
57905         var rowIndex = this.view.findRowIndex(t);
57906         var sm = this.grid.selModel;
57907             
57908         //Roo.log(rowIndex);
57909         
57910         if (sm.getSelectedCell) {
57911             // cell selection..
57912             if (!sm.getSelectedCell()) {
57913                 return false;
57914             }
57915             if (rowIndex != sm.getSelectedCell()[0]) {
57916                 return false;
57917             }
57918         
57919         }
57920         
57921         if(rowIndex !== false){
57922             
57923             // if editorgrid.. 
57924             
57925             
57926             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
57927                
57928             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
57929               //  
57930             //}
57931             if (e.hasModifier()){
57932                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
57933             }
57934             
57935             Roo.log("getDragData");
57936             
57937             return {
57938                 grid: this.grid,
57939                 ddel: this.ddel,
57940                 rowIndex: rowIndex,
57941                 selections:sm.getSelections ? sm.getSelections() : (
57942                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
57943                 )
57944             };
57945         }
57946         return false;
57947     },
57948
57949     onInitDrag : function(e){
57950         var data = this.dragData;
57951         this.ddel.innerHTML = this.grid.getDragDropText();
57952         this.proxy.update(this.ddel);
57953         // fire start drag?
57954     },
57955
57956     afterRepair : function(){
57957         this.dragging = false;
57958     },
57959
57960     getRepairXY : function(e, data){
57961         return false;
57962     },
57963
57964     onEndDrag : function(data, e){
57965         // fire end drag?
57966     },
57967
57968     onValidDrop : function(dd, e, id){
57969         // fire drag drop?
57970         this.hideProxy();
57971     },
57972
57973     beforeInvalidDrop : function(e, id){
57974
57975     }
57976 });/*
57977  * Based on:
57978  * Ext JS Library 1.1.1
57979  * Copyright(c) 2006-2007, Ext JS, LLC.
57980  *
57981  * Originally Released Under LGPL - original licence link has changed is not relivant.
57982  *
57983  * Fork - LGPL
57984  * <script type="text/javascript">
57985  */
57986  
57987
57988 /**
57989  * @class Roo.grid.ColumnModel
57990  * @extends Roo.util.Observable
57991  * This is the default implementation of a ColumnModel used by the Grid. It defines
57992  * the columns in the grid.
57993  * <br>Usage:<br>
57994  <pre><code>
57995  var colModel = new Roo.grid.ColumnModel([
57996         {header: "Ticker", width: 60, sortable: true, locked: true},
57997         {header: "Company Name", width: 150, sortable: true},
57998         {header: "Market Cap.", width: 100, sortable: true},
57999         {header: "$ Sales", width: 100, sortable: true, renderer: money},
58000         {header: "Employees", width: 100, sortable: true, resizable: false}
58001  ]);
58002  </code></pre>
58003  * <p>
58004  
58005  * The config options listed for this class are options which may appear in each
58006  * individual column definition.
58007  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
58008  * @constructor
58009  * @param {Object} config An Array of column config objects. See this class's
58010  * config objects for details.
58011 */
58012 Roo.grid.ColumnModel = function(config){
58013         /**
58014      * The config passed into the constructor
58015      */
58016     this.config = config;
58017     this.lookup = {};
58018
58019     // if no id, create one
58020     // if the column does not have a dataIndex mapping,
58021     // map it to the order it is in the config
58022     for(var i = 0, len = config.length; i < len; i++){
58023         var c = config[i];
58024         if(typeof c.dataIndex == "undefined"){
58025             c.dataIndex = i;
58026         }
58027         if(typeof c.renderer == "string"){
58028             c.renderer = Roo.util.Format[c.renderer];
58029         }
58030         if(typeof c.id == "undefined"){
58031             c.id = Roo.id();
58032         }
58033         if(c.editor && c.editor.xtype){
58034             c.editor  = Roo.factory(c.editor, Roo.grid);
58035         }
58036         if(c.editor && c.editor.isFormField){
58037             c.editor = new Roo.grid.GridEditor(c.editor);
58038         }
58039         this.lookup[c.id] = c;
58040     }
58041
58042     /**
58043      * The width of columns which have no width specified (defaults to 100)
58044      * @type Number
58045      */
58046     this.defaultWidth = 100;
58047
58048     /**
58049      * Default sortable of columns which have no sortable specified (defaults to false)
58050      * @type Boolean
58051      */
58052     this.defaultSortable = false;
58053
58054     this.addEvents({
58055         /**
58056              * @event widthchange
58057              * Fires when the width of a column changes.
58058              * @param {ColumnModel} this
58059              * @param {Number} columnIndex The column index
58060              * @param {Number} newWidth The new width
58061              */
58062             "widthchange": true,
58063         /**
58064              * @event headerchange
58065              * Fires when the text of a header changes.
58066              * @param {ColumnModel} this
58067              * @param {Number} columnIndex The column index
58068              * @param {Number} newText The new header text
58069              */
58070             "headerchange": true,
58071         /**
58072              * @event hiddenchange
58073              * Fires when a column is hidden or "unhidden".
58074              * @param {ColumnModel} this
58075              * @param {Number} columnIndex The column index
58076              * @param {Boolean} hidden true if hidden, false otherwise
58077              */
58078             "hiddenchange": true,
58079             /**
58080          * @event columnmoved
58081          * Fires when a column is moved.
58082          * @param {ColumnModel} this
58083          * @param {Number} oldIndex
58084          * @param {Number} newIndex
58085          */
58086         "columnmoved" : true,
58087         /**
58088          * @event columlockchange
58089          * Fires when a column's locked state is changed
58090          * @param {ColumnModel} this
58091          * @param {Number} colIndex
58092          * @param {Boolean} locked true if locked
58093          */
58094         "columnlockchange" : true
58095     });
58096     Roo.grid.ColumnModel.superclass.constructor.call(this);
58097 };
58098 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
58099     /**
58100      * @cfg {String} header The header text to display in the Grid view.
58101      */
58102     /**
58103      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
58104      * {@link Roo.data.Record} definition from which to draw the column's value. If not
58105      * specified, the column's index is used as an index into the Record's data Array.
58106      */
58107     /**
58108      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
58109      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
58110      */
58111     /**
58112      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
58113      * Defaults to the value of the {@link #defaultSortable} property.
58114      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
58115      */
58116     /**
58117      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
58118      */
58119     /**
58120      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
58121      */
58122     /**
58123      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
58124      */
58125     /**
58126      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
58127      */
58128     /**
58129      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
58130      * given the cell's data value. See {@link #setRenderer}. If not specified, the
58131      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
58132      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
58133      */
58134        /**
58135      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
58136      */
58137     /**
58138      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
58139      */
58140     /**
58141      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
58142      */
58143     /**
58144      * @cfg {String} cursor (Optional)
58145      */
58146     /**
58147      * @cfg {String} tooltip (Optional)
58148      */
58149     /**
58150      * @cfg {Number} xs (Optional)
58151      */
58152     /**
58153      * @cfg {Number} sm (Optional)
58154      */
58155     /**
58156      * @cfg {Number} md (Optional)
58157      */
58158     /**
58159      * @cfg {Number} lg (Optional)
58160      */
58161     /**
58162      * Returns the id of the column at the specified index.
58163      * @param {Number} index The column index
58164      * @return {String} the id
58165      */
58166     getColumnId : function(index){
58167         return this.config[index].id;
58168     },
58169
58170     /**
58171      * Returns the column for a specified id.
58172      * @param {String} id The column id
58173      * @return {Object} the column
58174      */
58175     getColumnById : function(id){
58176         return this.lookup[id];
58177     },
58178
58179     
58180     /**
58181      * Returns the column for a specified dataIndex.
58182      * @param {String} dataIndex The column dataIndex
58183      * @return {Object|Boolean} the column or false if not found
58184      */
58185     getColumnByDataIndex: function(dataIndex){
58186         var index = this.findColumnIndex(dataIndex);
58187         return index > -1 ? this.config[index] : false;
58188     },
58189     
58190     /**
58191      * Returns the index for a specified column id.
58192      * @param {String} id The column id
58193      * @return {Number} the index, or -1 if not found
58194      */
58195     getIndexById : function(id){
58196         for(var i = 0, len = this.config.length; i < len; i++){
58197             if(this.config[i].id == id){
58198                 return i;
58199             }
58200         }
58201         return -1;
58202     },
58203     
58204     /**
58205      * Returns the index for a specified column dataIndex.
58206      * @param {String} dataIndex The column dataIndex
58207      * @return {Number} the index, or -1 if not found
58208      */
58209     
58210     findColumnIndex : function(dataIndex){
58211         for(var i = 0, len = this.config.length; i < len; i++){
58212             if(this.config[i].dataIndex == dataIndex){
58213                 return i;
58214             }
58215         }
58216         return -1;
58217     },
58218     
58219     
58220     moveColumn : function(oldIndex, newIndex){
58221         var c = this.config[oldIndex];
58222         this.config.splice(oldIndex, 1);
58223         this.config.splice(newIndex, 0, c);
58224         this.dataMap = null;
58225         this.fireEvent("columnmoved", this, oldIndex, newIndex);
58226     },
58227
58228     isLocked : function(colIndex){
58229         return this.config[colIndex].locked === true;
58230     },
58231
58232     setLocked : function(colIndex, value, suppressEvent){
58233         if(this.isLocked(colIndex) == value){
58234             return;
58235         }
58236         this.config[colIndex].locked = value;
58237         if(!suppressEvent){
58238             this.fireEvent("columnlockchange", this, colIndex, value);
58239         }
58240     },
58241
58242     getTotalLockedWidth : function(){
58243         var totalWidth = 0;
58244         for(var i = 0; i < this.config.length; i++){
58245             if(this.isLocked(i) && !this.isHidden(i)){
58246                 this.totalWidth += this.getColumnWidth(i);
58247             }
58248         }
58249         return totalWidth;
58250     },
58251
58252     getLockedCount : function(){
58253         for(var i = 0, len = this.config.length; i < len; i++){
58254             if(!this.isLocked(i)){
58255                 return i;
58256             }
58257         }
58258         
58259         return this.config.length;
58260     },
58261
58262     /**
58263      * Returns the number of columns.
58264      * @return {Number}
58265      */
58266     getColumnCount : function(visibleOnly){
58267         if(visibleOnly === true){
58268             var c = 0;
58269             for(var i = 0, len = this.config.length; i < len; i++){
58270                 if(!this.isHidden(i)){
58271                     c++;
58272                 }
58273             }
58274             return c;
58275         }
58276         return this.config.length;
58277     },
58278
58279     /**
58280      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
58281      * @param {Function} fn
58282      * @param {Object} scope (optional)
58283      * @return {Array} result
58284      */
58285     getColumnsBy : function(fn, scope){
58286         var r = [];
58287         for(var i = 0, len = this.config.length; i < len; i++){
58288             var c = this.config[i];
58289             if(fn.call(scope||this, c, i) === true){
58290                 r[r.length] = c;
58291             }
58292         }
58293         return r;
58294     },
58295
58296     /**
58297      * Returns true if the specified column is sortable.
58298      * @param {Number} col The column index
58299      * @return {Boolean}
58300      */
58301     isSortable : function(col){
58302         if(typeof this.config[col].sortable == "undefined"){
58303             return this.defaultSortable;
58304         }
58305         return this.config[col].sortable;
58306     },
58307
58308     /**
58309      * Returns the rendering (formatting) function defined for the column.
58310      * @param {Number} col The column index.
58311      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
58312      */
58313     getRenderer : function(col){
58314         if(!this.config[col].renderer){
58315             return Roo.grid.ColumnModel.defaultRenderer;
58316         }
58317         return this.config[col].renderer;
58318     },
58319
58320     /**
58321      * Sets the rendering (formatting) function for a column.
58322      * @param {Number} col The column index
58323      * @param {Function} fn The function to use to process the cell's raw data
58324      * to return HTML markup for the grid view. The render function is called with
58325      * the following parameters:<ul>
58326      * <li>Data value.</li>
58327      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
58328      * <li>css A CSS style string to apply to the table cell.</li>
58329      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
58330      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
58331      * <li>Row index</li>
58332      * <li>Column index</li>
58333      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
58334      */
58335     setRenderer : function(col, fn){
58336         this.config[col].renderer = fn;
58337     },
58338
58339     /**
58340      * Returns the width for the specified column.
58341      * @param {Number} col The column index
58342      * @return {Number}
58343      */
58344     getColumnWidth : function(col){
58345         return this.config[col].width * 1 || this.defaultWidth;
58346     },
58347
58348     /**
58349      * Sets the width for a column.
58350      * @param {Number} col The column index
58351      * @param {Number} width The new width
58352      */
58353     setColumnWidth : function(col, width, suppressEvent){
58354         this.config[col].width = width;
58355         this.totalWidth = null;
58356         if(!suppressEvent){
58357              this.fireEvent("widthchange", this, col, width);
58358         }
58359     },
58360
58361     /**
58362      * Returns the total width of all columns.
58363      * @param {Boolean} includeHidden True to include hidden column widths
58364      * @return {Number}
58365      */
58366     getTotalWidth : function(includeHidden){
58367         if(!this.totalWidth){
58368             this.totalWidth = 0;
58369             for(var i = 0, len = this.config.length; i < len; i++){
58370                 if(includeHidden || !this.isHidden(i)){
58371                     this.totalWidth += this.getColumnWidth(i);
58372                 }
58373             }
58374         }
58375         return this.totalWidth;
58376     },
58377
58378     /**
58379      * Returns the header for the specified column.
58380      * @param {Number} col The column index
58381      * @return {String}
58382      */
58383     getColumnHeader : function(col){
58384         return this.config[col].header;
58385     },
58386
58387     /**
58388      * Sets the header for a column.
58389      * @param {Number} col The column index
58390      * @param {String} header The new header
58391      */
58392     setColumnHeader : function(col, header){
58393         this.config[col].header = header;
58394         this.fireEvent("headerchange", this, col, header);
58395     },
58396
58397     /**
58398      * Returns the tooltip for the specified column.
58399      * @param {Number} col The column index
58400      * @return {String}
58401      */
58402     getColumnTooltip : function(col){
58403             return this.config[col].tooltip;
58404     },
58405     /**
58406      * Sets the tooltip for a column.
58407      * @param {Number} col The column index
58408      * @param {String} tooltip The new tooltip
58409      */
58410     setColumnTooltip : function(col, tooltip){
58411             this.config[col].tooltip = tooltip;
58412     },
58413
58414     /**
58415      * Returns the dataIndex for the specified column.
58416      * @param {Number} col The column index
58417      * @return {Number}
58418      */
58419     getDataIndex : function(col){
58420         return this.config[col].dataIndex;
58421     },
58422
58423     /**
58424      * Sets the dataIndex for a column.
58425      * @param {Number} col The column index
58426      * @param {Number} dataIndex The new dataIndex
58427      */
58428     setDataIndex : function(col, dataIndex){
58429         this.config[col].dataIndex = dataIndex;
58430     },
58431
58432     
58433     
58434     /**
58435      * Returns true if the cell is editable.
58436      * @param {Number} colIndex The column index
58437      * @param {Number} rowIndex The row index - this is nto actually used..?
58438      * @return {Boolean}
58439      */
58440     isCellEditable : function(colIndex, rowIndex){
58441         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
58442     },
58443
58444     /**
58445      * Returns the editor defined for the cell/column.
58446      * return false or null to disable editing.
58447      * @param {Number} colIndex The column index
58448      * @param {Number} rowIndex The row index
58449      * @return {Object}
58450      */
58451     getCellEditor : function(colIndex, rowIndex){
58452         return this.config[colIndex].editor;
58453     },
58454
58455     /**
58456      * Sets if a column is editable.
58457      * @param {Number} col The column index
58458      * @param {Boolean} editable True if the column is editable
58459      */
58460     setEditable : function(col, editable){
58461         this.config[col].editable = editable;
58462     },
58463
58464
58465     /**
58466      * Returns true if the column is hidden.
58467      * @param {Number} colIndex The column index
58468      * @return {Boolean}
58469      */
58470     isHidden : function(colIndex){
58471         return this.config[colIndex].hidden;
58472     },
58473
58474
58475     /**
58476      * Returns true if the column width cannot be changed
58477      */
58478     isFixed : function(colIndex){
58479         return this.config[colIndex].fixed;
58480     },
58481
58482     /**
58483      * Returns true if the column can be resized
58484      * @return {Boolean}
58485      */
58486     isResizable : function(colIndex){
58487         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
58488     },
58489     /**
58490      * Sets if a column is hidden.
58491      * @param {Number} colIndex The column index
58492      * @param {Boolean} hidden True if the column is hidden
58493      */
58494     setHidden : function(colIndex, hidden){
58495         this.config[colIndex].hidden = hidden;
58496         this.totalWidth = null;
58497         this.fireEvent("hiddenchange", this, colIndex, hidden);
58498     },
58499
58500     /**
58501      * Sets the editor for a column.
58502      * @param {Number} col The column index
58503      * @param {Object} editor The editor object
58504      */
58505     setEditor : function(col, editor){
58506         this.config[col].editor = editor;
58507     }
58508 });
58509
58510 Roo.grid.ColumnModel.defaultRenderer = function(value)
58511 {
58512     if(typeof value == "object") {
58513         return value;
58514     }
58515         if(typeof value == "string" && value.length < 1){
58516             return "&#160;";
58517         }
58518     
58519         return String.format("{0}", value);
58520 };
58521
58522 // Alias for backwards compatibility
58523 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
58524 /*
58525  * Based on:
58526  * Ext JS Library 1.1.1
58527  * Copyright(c) 2006-2007, Ext JS, LLC.
58528  *
58529  * Originally Released Under LGPL - original licence link has changed is not relivant.
58530  *
58531  * Fork - LGPL
58532  * <script type="text/javascript">
58533  */
58534
58535 /**
58536  * @class Roo.grid.AbstractSelectionModel
58537  * @extends Roo.util.Observable
58538  * Abstract base class for grid SelectionModels.  It provides the interface that should be
58539  * implemented by descendant classes.  This class should not be directly instantiated.
58540  * @constructor
58541  */
58542 Roo.grid.AbstractSelectionModel = function(){
58543     this.locked = false;
58544     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
58545 };
58546
58547 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
58548     /** @ignore Called by the grid automatically. Do not call directly. */
58549     init : function(grid){
58550         this.grid = grid;
58551         this.initEvents();
58552     },
58553
58554     /**
58555      * Locks the selections.
58556      */
58557     lock : function(){
58558         this.locked = true;
58559     },
58560
58561     /**
58562      * Unlocks the selections.
58563      */
58564     unlock : function(){
58565         this.locked = false;
58566     },
58567
58568     /**
58569      * Returns true if the selections are locked.
58570      * @return {Boolean}
58571      */
58572     isLocked : function(){
58573         return this.locked;
58574     }
58575 });/*
58576  * Based on:
58577  * Ext JS Library 1.1.1
58578  * Copyright(c) 2006-2007, Ext JS, LLC.
58579  *
58580  * Originally Released Under LGPL - original licence link has changed is not relivant.
58581  *
58582  * Fork - LGPL
58583  * <script type="text/javascript">
58584  */
58585 /**
58586  * @extends Roo.grid.AbstractSelectionModel
58587  * @class Roo.grid.RowSelectionModel
58588  * The default SelectionModel used by {@link Roo.grid.Grid}.
58589  * It supports multiple selections and keyboard selection/navigation. 
58590  * @constructor
58591  * @param {Object} config
58592  */
58593 Roo.grid.RowSelectionModel = function(config){
58594     Roo.apply(this, config);
58595     this.selections = new Roo.util.MixedCollection(false, function(o){
58596         return o.id;
58597     });
58598
58599     this.last = false;
58600     this.lastActive = false;
58601
58602     this.addEvents({
58603         /**
58604              * @event selectionchange
58605              * Fires when the selection changes
58606              * @param {SelectionModel} this
58607              */
58608             "selectionchange" : true,
58609         /**
58610              * @event afterselectionchange
58611              * Fires after the selection changes (eg. by key press or clicking)
58612              * @param {SelectionModel} this
58613              */
58614             "afterselectionchange" : true,
58615         /**
58616              * @event beforerowselect
58617              * Fires when a row is selected being selected, return false to cancel.
58618              * @param {SelectionModel} this
58619              * @param {Number} rowIndex The selected index
58620              * @param {Boolean} keepExisting False if other selections will be cleared
58621              */
58622             "beforerowselect" : true,
58623         /**
58624              * @event rowselect
58625              * Fires when a row is selected.
58626              * @param {SelectionModel} this
58627              * @param {Number} rowIndex The selected index
58628              * @param {Roo.data.Record} r The record
58629              */
58630             "rowselect" : true,
58631         /**
58632              * @event rowdeselect
58633              * Fires when a row is deselected.
58634              * @param {SelectionModel} this
58635              * @param {Number} rowIndex The selected index
58636              */
58637         "rowdeselect" : true
58638     });
58639     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
58640     this.locked = false;
58641 };
58642
58643 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
58644     /**
58645      * @cfg {Boolean} singleSelect
58646      * True to allow selection of only one row at a time (defaults to false)
58647      */
58648     singleSelect : false,
58649
58650     // private
58651     initEvents : function(){
58652
58653         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
58654             this.grid.on("mousedown", this.handleMouseDown, this);
58655         }else{ // allow click to work like normal
58656             this.grid.on("rowclick", this.handleDragableRowClick, this);
58657         }
58658
58659         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
58660             "up" : function(e){
58661                 if(!e.shiftKey){
58662                     this.selectPrevious(e.shiftKey);
58663                 }else if(this.last !== false && this.lastActive !== false){
58664                     var last = this.last;
58665                     this.selectRange(this.last,  this.lastActive-1);
58666                     this.grid.getView().focusRow(this.lastActive);
58667                     if(last !== false){
58668                         this.last = last;
58669                     }
58670                 }else{
58671                     this.selectFirstRow();
58672                 }
58673                 this.fireEvent("afterselectionchange", this);
58674             },
58675             "down" : function(e){
58676                 if(!e.shiftKey){
58677                     this.selectNext(e.shiftKey);
58678                 }else if(this.last !== false && this.lastActive !== false){
58679                     var last = this.last;
58680                     this.selectRange(this.last,  this.lastActive+1);
58681                     this.grid.getView().focusRow(this.lastActive);
58682                     if(last !== false){
58683                         this.last = last;
58684                     }
58685                 }else{
58686                     this.selectFirstRow();
58687                 }
58688                 this.fireEvent("afterselectionchange", this);
58689             },
58690             scope: this
58691         });
58692
58693         var view = this.grid.view;
58694         view.on("refresh", this.onRefresh, this);
58695         view.on("rowupdated", this.onRowUpdated, this);
58696         view.on("rowremoved", this.onRemove, this);
58697     },
58698
58699     // private
58700     onRefresh : function(){
58701         var ds = this.grid.dataSource, i, v = this.grid.view;
58702         var s = this.selections;
58703         s.each(function(r){
58704             if((i = ds.indexOfId(r.id)) != -1){
58705                 v.onRowSelect(i);
58706                 s.add(ds.getAt(i)); // updating the selection relate data
58707             }else{
58708                 s.remove(r);
58709             }
58710         });
58711     },
58712
58713     // private
58714     onRemove : function(v, index, r){
58715         this.selections.remove(r);
58716     },
58717
58718     // private
58719     onRowUpdated : function(v, index, r){
58720         if(this.isSelected(r)){
58721             v.onRowSelect(index);
58722         }
58723     },
58724
58725     /**
58726      * Select records.
58727      * @param {Array} records The records to select
58728      * @param {Boolean} keepExisting (optional) True to keep existing selections
58729      */
58730     selectRecords : function(records, keepExisting){
58731         if(!keepExisting){
58732             this.clearSelections();
58733         }
58734         var ds = this.grid.dataSource;
58735         for(var i = 0, len = records.length; i < len; i++){
58736             this.selectRow(ds.indexOf(records[i]), true);
58737         }
58738     },
58739
58740     /**
58741      * Gets the number of selected rows.
58742      * @return {Number}
58743      */
58744     getCount : function(){
58745         return this.selections.length;
58746     },
58747
58748     /**
58749      * Selects the first row in the grid.
58750      */
58751     selectFirstRow : function(){
58752         this.selectRow(0);
58753     },
58754
58755     /**
58756      * Select the last row.
58757      * @param {Boolean} keepExisting (optional) True to keep existing selections
58758      */
58759     selectLastRow : function(keepExisting){
58760         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
58761     },
58762
58763     /**
58764      * Selects the row immediately following the last selected row.
58765      * @param {Boolean} keepExisting (optional) True to keep existing selections
58766      */
58767     selectNext : function(keepExisting){
58768         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
58769             this.selectRow(this.last+1, keepExisting);
58770             this.grid.getView().focusRow(this.last);
58771         }
58772     },
58773
58774     /**
58775      * Selects the row that precedes the last selected row.
58776      * @param {Boolean} keepExisting (optional) True to keep existing selections
58777      */
58778     selectPrevious : function(keepExisting){
58779         if(this.last){
58780             this.selectRow(this.last-1, keepExisting);
58781             this.grid.getView().focusRow(this.last);
58782         }
58783     },
58784
58785     /**
58786      * Returns the selected records
58787      * @return {Array} Array of selected records
58788      */
58789     getSelections : function(){
58790         return [].concat(this.selections.items);
58791     },
58792
58793     /**
58794      * Returns the first selected record.
58795      * @return {Record}
58796      */
58797     getSelected : function(){
58798         return this.selections.itemAt(0);
58799     },
58800
58801
58802     /**
58803      * Clears all selections.
58804      */
58805     clearSelections : function(fast){
58806         if(this.locked) {
58807             return;
58808         }
58809         if(fast !== true){
58810             var ds = this.grid.dataSource;
58811             var s = this.selections;
58812             s.each(function(r){
58813                 this.deselectRow(ds.indexOfId(r.id));
58814             }, this);
58815             s.clear();
58816         }else{
58817             this.selections.clear();
58818         }
58819         this.last = false;
58820     },
58821
58822
58823     /**
58824      * Selects all rows.
58825      */
58826     selectAll : function(){
58827         if(this.locked) {
58828             return;
58829         }
58830         this.selections.clear();
58831         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
58832             this.selectRow(i, true);
58833         }
58834     },
58835
58836     /**
58837      * Returns True if there is a selection.
58838      * @return {Boolean}
58839      */
58840     hasSelection : function(){
58841         return this.selections.length > 0;
58842     },
58843
58844     /**
58845      * Returns True if the specified row is selected.
58846      * @param {Number/Record} record The record or index of the record to check
58847      * @return {Boolean}
58848      */
58849     isSelected : function(index){
58850         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
58851         return (r && this.selections.key(r.id) ? true : false);
58852     },
58853
58854     /**
58855      * Returns True if the specified record id is selected.
58856      * @param {String} id The id of record to check
58857      * @return {Boolean}
58858      */
58859     isIdSelected : function(id){
58860         return (this.selections.key(id) ? true : false);
58861     },
58862
58863     // private
58864     handleMouseDown : function(e, t){
58865         var view = this.grid.getView(), rowIndex;
58866         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
58867             return;
58868         };
58869         if(e.shiftKey && this.last !== false){
58870             var last = this.last;
58871             this.selectRange(last, rowIndex, e.ctrlKey);
58872             this.last = last; // reset the last
58873             view.focusRow(rowIndex);
58874         }else{
58875             var isSelected = this.isSelected(rowIndex);
58876             if(e.button !== 0 && isSelected){
58877                 view.focusRow(rowIndex);
58878             }else if(e.ctrlKey && isSelected){
58879                 this.deselectRow(rowIndex);
58880             }else if(!isSelected){
58881                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
58882                 view.focusRow(rowIndex);
58883             }
58884         }
58885         this.fireEvent("afterselectionchange", this);
58886     },
58887     // private
58888     handleDragableRowClick :  function(grid, rowIndex, e) 
58889     {
58890         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
58891             this.selectRow(rowIndex, false);
58892             grid.view.focusRow(rowIndex);
58893              this.fireEvent("afterselectionchange", this);
58894         }
58895     },
58896     
58897     /**
58898      * Selects multiple rows.
58899      * @param {Array} rows Array of the indexes of the row to select
58900      * @param {Boolean} keepExisting (optional) True to keep existing selections
58901      */
58902     selectRows : function(rows, keepExisting){
58903         if(!keepExisting){
58904             this.clearSelections();
58905         }
58906         for(var i = 0, len = rows.length; i < len; i++){
58907             this.selectRow(rows[i], true);
58908         }
58909     },
58910
58911     /**
58912      * Selects a range of rows. All rows in between startRow and endRow are also selected.
58913      * @param {Number} startRow The index of the first row in the range
58914      * @param {Number} endRow The index of the last row in the range
58915      * @param {Boolean} keepExisting (optional) True to retain existing selections
58916      */
58917     selectRange : function(startRow, endRow, keepExisting){
58918         if(this.locked) {
58919             return;
58920         }
58921         if(!keepExisting){
58922             this.clearSelections();
58923         }
58924         if(startRow <= endRow){
58925             for(var i = startRow; i <= endRow; i++){
58926                 this.selectRow(i, true);
58927             }
58928         }else{
58929             for(var i = startRow; i >= endRow; i--){
58930                 this.selectRow(i, true);
58931             }
58932         }
58933     },
58934
58935     /**
58936      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
58937      * @param {Number} startRow The index of the first row in the range
58938      * @param {Number} endRow The index of the last row in the range
58939      */
58940     deselectRange : function(startRow, endRow, preventViewNotify){
58941         if(this.locked) {
58942             return;
58943         }
58944         for(var i = startRow; i <= endRow; i++){
58945             this.deselectRow(i, preventViewNotify);
58946         }
58947     },
58948
58949     /**
58950      * Selects a row.
58951      * @param {Number} row The index of the row to select
58952      * @param {Boolean} keepExisting (optional) True to keep existing selections
58953      */
58954     selectRow : function(index, keepExisting, preventViewNotify){
58955         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
58956             return;
58957         }
58958         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
58959             if(!keepExisting || this.singleSelect){
58960                 this.clearSelections();
58961             }
58962             var r = this.grid.dataSource.getAt(index);
58963             this.selections.add(r);
58964             this.last = this.lastActive = index;
58965             if(!preventViewNotify){
58966                 this.grid.getView().onRowSelect(index);
58967             }
58968             this.fireEvent("rowselect", this, index, r);
58969             this.fireEvent("selectionchange", this);
58970         }
58971     },
58972
58973     /**
58974      * Deselects a row.
58975      * @param {Number} row The index of the row to deselect
58976      */
58977     deselectRow : function(index, preventViewNotify){
58978         if(this.locked) {
58979             return;
58980         }
58981         if(this.last == index){
58982             this.last = false;
58983         }
58984         if(this.lastActive == index){
58985             this.lastActive = false;
58986         }
58987         var r = this.grid.dataSource.getAt(index);
58988         this.selections.remove(r);
58989         if(!preventViewNotify){
58990             this.grid.getView().onRowDeselect(index);
58991         }
58992         this.fireEvent("rowdeselect", this, index);
58993         this.fireEvent("selectionchange", this);
58994     },
58995
58996     // private
58997     restoreLast : function(){
58998         if(this._last){
58999             this.last = this._last;
59000         }
59001     },
59002
59003     // private
59004     acceptsNav : function(row, col, cm){
59005         return !cm.isHidden(col) && cm.isCellEditable(col, row);
59006     },
59007
59008     // private
59009     onEditorKey : function(field, e){
59010         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
59011         if(k == e.TAB){
59012             e.stopEvent();
59013             ed.completeEdit();
59014             if(e.shiftKey){
59015                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
59016             }else{
59017                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
59018             }
59019         }else if(k == e.ENTER && !e.ctrlKey){
59020             e.stopEvent();
59021             ed.completeEdit();
59022             if(e.shiftKey){
59023                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
59024             }else{
59025                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
59026             }
59027         }else if(k == e.ESC){
59028             ed.cancelEdit();
59029         }
59030         if(newCell){
59031             g.startEditing(newCell[0], newCell[1]);
59032         }
59033     }
59034 });/*
59035  * Based on:
59036  * Ext JS Library 1.1.1
59037  * Copyright(c) 2006-2007, Ext JS, LLC.
59038  *
59039  * Originally Released Under LGPL - original licence link has changed is not relivant.
59040  *
59041  * Fork - LGPL
59042  * <script type="text/javascript">
59043  */
59044 /**
59045  * @class Roo.grid.CellSelectionModel
59046  * @extends Roo.grid.AbstractSelectionModel
59047  * This class provides the basic implementation for cell selection in a grid.
59048  * @constructor
59049  * @param {Object} config The object containing the configuration of this model.
59050  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
59051  */
59052 Roo.grid.CellSelectionModel = function(config){
59053     Roo.apply(this, config);
59054
59055     this.selection = null;
59056
59057     this.addEvents({
59058         /**
59059              * @event beforerowselect
59060              * Fires before a cell is selected.
59061              * @param {SelectionModel} this
59062              * @param {Number} rowIndex The selected row index
59063              * @param {Number} colIndex The selected cell index
59064              */
59065             "beforecellselect" : true,
59066         /**
59067              * @event cellselect
59068              * Fires when a cell is selected.
59069              * @param {SelectionModel} this
59070              * @param {Number} rowIndex The selected row index
59071              * @param {Number} colIndex The selected cell index
59072              */
59073             "cellselect" : true,
59074         /**
59075              * @event selectionchange
59076              * Fires when the active selection changes.
59077              * @param {SelectionModel} this
59078              * @param {Object} selection null for no selection or an object (o) with two properties
59079                 <ul>
59080                 <li>o.record: the record object for the row the selection is in</li>
59081                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
59082                 </ul>
59083              */
59084             "selectionchange" : true,
59085         /**
59086              * @event tabend
59087              * Fires when the tab (or enter) was pressed on the last editable cell
59088              * You can use this to trigger add new row.
59089              * @param {SelectionModel} this
59090              */
59091             "tabend" : true,
59092          /**
59093              * @event beforeeditnext
59094              * Fires before the next editable sell is made active
59095              * You can use this to skip to another cell or fire the tabend
59096              *    if you set cell to false
59097              * @param {Object} eventdata object : { cell : [ row, col ] } 
59098              */
59099             "beforeeditnext" : true
59100     });
59101     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
59102 };
59103
59104 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
59105     
59106     enter_is_tab: false,
59107
59108     /** @ignore */
59109     initEvents : function(){
59110         this.grid.on("mousedown", this.handleMouseDown, this);
59111         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
59112         var view = this.grid.view;
59113         view.on("refresh", this.onViewChange, this);
59114         view.on("rowupdated", this.onRowUpdated, this);
59115         view.on("beforerowremoved", this.clearSelections, this);
59116         view.on("beforerowsinserted", this.clearSelections, this);
59117         if(this.grid.isEditor){
59118             this.grid.on("beforeedit", this.beforeEdit,  this);
59119         }
59120     },
59121
59122         //private
59123     beforeEdit : function(e){
59124         this.select(e.row, e.column, false, true, e.record);
59125     },
59126
59127         //private
59128     onRowUpdated : function(v, index, r){
59129         if(this.selection && this.selection.record == r){
59130             v.onCellSelect(index, this.selection.cell[1]);
59131         }
59132     },
59133
59134         //private
59135     onViewChange : function(){
59136         this.clearSelections(true);
59137     },
59138
59139         /**
59140          * Returns the currently selected cell,.
59141          * @return {Array} The selected cell (row, column) or null if none selected.
59142          */
59143     getSelectedCell : function(){
59144         return this.selection ? this.selection.cell : null;
59145     },
59146
59147     /**
59148      * Clears all selections.
59149      * @param {Boolean} true to prevent the gridview from being notified about the change.
59150      */
59151     clearSelections : function(preventNotify){
59152         var s = this.selection;
59153         if(s){
59154             if(preventNotify !== true){
59155                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
59156             }
59157             this.selection = null;
59158             this.fireEvent("selectionchange", this, null);
59159         }
59160     },
59161
59162     /**
59163      * Returns true if there is a selection.
59164      * @return {Boolean}
59165      */
59166     hasSelection : function(){
59167         return this.selection ? true : false;
59168     },
59169
59170     /** @ignore */
59171     handleMouseDown : function(e, t){
59172         var v = this.grid.getView();
59173         if(this.isLocked()){
59174             return;
59175         };
59176         var row = v.findRowIndex(t);
59177         var cell = v.findCellIndex(t);
59178         if(row !== false && cell !== false){
59179             this.select(row, cell);
59180         }
59181     },
59182
59183     /**
59184      * Selects a cell.
59185      * @param {Number} rowIndex
59186      * @param {Number} collIndex
59187      */
59188     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
59189         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
59190             this.clearSelections();
59191             r = r || this.grid.dataSource.getAt(rowIndex);
59192             this.selection = {
59193                 record : r,
59194                 cell : [rowIndex, colIndex]
59195             };
59196             if(!preventViewNotify){
59197                 var v = this.grid.getView();
59198                 v.onCellSelect(rowIndex, colIndex);
59199                 if(preventFocus !== true){
59200                     v.focusCell(rowIndex, colIndex);
59201                 }
59202             }
59203             this.fireEvent("cellselect", this, rowIndex, colIndex);
59204             this.fireEvent("selectionchange", this, this.selection);
59205         }
59206     },
59207
59208         //private
59209     isSelectable : function(rowIndex, colIndex, cm){
59210         return !cm.isHidden(colIndex);
59211     },
59212
59213     /** @ignore */
59214     handleKeyDown : function(e){
59215         //Roo.log('Cell Sel Model handleKeyDown');
59216         if(!e.isNavKeyPress()){
59217             return;
59218         }
59219         var g = this.grid, s = this.selection;
59220         if(!s){
59221             e.stopEvent();
59222             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
59223             if(cell){
59224                 this.select(cell[0], cell[1]);
59225             }
59226             return;
59227         }
59228         var sm = this;
59229         var walk = function(row, col, step){
59230             return g.walkCells(row, col, step, sm.isSelectable,  sm);
59231         };
59232         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
59233         var newCell;
59234
59235       
59236
59237         switch(k){
59238             case e.TAB:
59239                 // handled by onEditorKey
59240                 if (g.isEditor && g.editing) {
59241                     return;
59242                 }
59243                 if(e.shiftKey) {
59244                     newCell = walk(r, c-1, -1);
59245                 } else {
59246                     newCell = walk(r, c+1, 1);
59247                 }
59248                 break;
59249             
59250             case e.DOWN:
59251                newCell = walk(r+1, c, 1);
59252                 break;
59253             
59254             case e.UP:
59255                 newCell = walk(r-1, c, -1);
59256                 break;
59257             
59258             case e.RIGHT:
59259                 newCell = walk(r, c+1, 1);
59260                 break;
59261             
59262             case e.LEFT:
59263                 newCell = walk(r, c-1, -1);
59264                 break;
59265             
59266             case e.ENTER:
59267                 
59268                 if(g.isEditor && !g.editing){
59269                    g.startEditing(r, c);
59270                    e.stopEvent();
59271                    return;
59272                 }
59273                 
59274                 
59275              break;
59276         };
59277         if(newCell){
59278             this.select(newCell[0], newCell[1]);
59279             e.stopEvent();
59280             
59281         }
59282     },
59283
59284     acceptsNav : function(row, col, cm){
59285         return !cm.isHidden(col) && cm.isCellEditable(col, row);
59286     },
59287     /**
59288      * Selects a cell.
59289      * @param {Number} field (not used) - as it's normally used as a listener
59290      * @param {Number} e - event - fake it by using
59291      *
59292      * var e = Roo.EventObjectImpl.prototype;
59293      * e.keyCode = e.TAB
59294      *
59295      * 
59296      */
59297     onEditorKey : function(field, e){
59298         
59299         var k = e.getKey(),
59300             newCell,
59301             g = this.grid,
59302             ed = g.activeEditor,
59303             forward = false;
59304         ///Roo.log('onEditorKey' + k);
59305         
59306         
59307         if (this.enter_is_tab && k == e.ENTER) {
59308             k = e.TAB;
59309         }
59310         
59311         if(k == e.TAB){
59312             if(e.shiftKey){
59313                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
59314             }else{
59315                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
59316                 forward = true;
59317             }
59318             
59319             e.stopEvent();
59320             
59321         } else if(k == e.ENTER &&  !e.ctrlKey){
59322             ed.completeEdit();
59323             e.stopEvent();
59324             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
59325         
59326                 } else if(k == e.ESC){
59327             ed.cancelEdit();
59328         }
59329                 
59330         if (newCell) {
59331             var ecall = { cell : newCell, forward : forward };
59332             this.fireEvent('beforeeditnext', ecall );
59333             newCell = ecall.cell;
59334                         forward = ecall.forward;
59335         }
59336                 
59337         if(newCell){
59338             //Roo.log('next cell after edit');
59339             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
59340         } else if (forward) {
59341             // tabbed past last
59342             this.fireEvent.defer(100, this, ['tabend',this]);
59343         }
59344     }
59345 });/*
59346  * Based on:
59347  * Ext JS Library 1.1.1
59348  * Copyright(c) 2006-2007, Ext JS, LLC.
59349  *
59350  * Originally Released Under LGPL - original licence link has changed is not relivant.
59351  *
59352  * Fork - LGPL
59353  * <script type="text/javascript">
59354  */
59355  
59356 /**
59357  * @class Roo.grid.EditorGrid
59358  * @extends Roo.grid.Grid
59359  * Class for creating and editable grid.
59360  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
59361  * The container MUST have some type of size defined for the grid to fill. The container will be 
59362  * automatically set to position relative if it isn't already.
59363  * @param {Object} dataSource The data model to bind to
59364  * @param {Object} colModel The column model with info about this grid's columns
59365  */
59366 Roo.grid.EditorGrid = function(container, config){
59367     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
59368     this.getGridEl().addClass("xedit-grid");
59369
59370     if(!this.selModel){
59371         this.selModel = new Roo.grid.CellSelectionModel();
59372     }
59373
59374     this.activeEditor = null;
59375
59376         this.addEvents({
59377             /**
59378              * @event beforeedit
59379              * Fires before cell editing is triggered. The edit event object has the following properties <br />
59380              * <ul style="padding:5px;padding-left:16px;">
59381              * <li>grid - This grid</li>
59382              * <li>record - The record being edited</li>
59383              * <li>field - The field name being edited</li>
59384              * <li>value - The value for the field being edited.</li>
59385              * <li>row - The grid row index</li>
59386              * <li>column - The grid column index</li>
59387              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
59388              * </ul>
59389              * @param {Object} e An edit event (see above for description)
59390              */
59391             "beforeedit" : true,
59392             /**
59393              * @event afteredit
59394              * Fires after a cell is edited. <br />
59395              * <ul style="padding:5px;padding-left:16px;">
59396              * <li>grid - This grid</li>
59397              * <li>record - The record being edited</li>
59398              * <li>field - The field name being edited</li>
59399              * <li>value - The value being set</li>
59400              * <li>originalValue - The original value for the field, before the edit.</li>
59401              * <li>row - The grid row index</li>
59402              * <li>column - The grid column index</li>
59403              * </ul>
59404              * @param {Object} e An edit event (see above for description)
59405              */
59406             "afteredit" : true,
59407             /**
59408              * @event validateedit
59409              * Fires after a cell is edited, but before the value is set in the record. 
59410          * You can use this to modify the value being set in the field, Return false
59411              * to cancel the change. The edit event object has the following properties <br />
59412              * <ul style="padding:5px;padding-left:16px;">
59413          * <li>editor - This editor</li>
59414              * <li>grid - This grid</li>
59415              * <li>record - The record being edited</li>
59416              * <li>field - The field name being edited</li>
59417              * <li>value - The value being set</li>
59418              * <li>originalValue - The original value for the field, before the edit.</li>
59419              * <li>row - The grid row index</li>
59420              * <li>column - The grid column index</li>
59421              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
59422              * </ul>
59423              * @param {Object} e An edit event (see above for description)
59424              */
59425             "validateedit" : true
59426         });
59427     this.on("bodyscroll", this.stopEditing,  this);
59428     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
59429 };
59430
59431 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
59432     /**
59433      * @cfg {Number} clicksToEdit
59434      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
59435      */
59436     clicksToEdit: 2,
59437
59438     // private
59439     isEditor : true,
59440     // private
59441     trackMouseOver: false, // causes very odd FF errors
59442
59443     onCellDblClick : function(g, row, col){
59444         this.startEditing(row, col);
59445     },
59446
59447     onEditComplete : function(ed, value, startValue){
59448         this.editing = false;
59449         this.activeEditor = null;
59450         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
59451         var r = ed.record;
59452         var field = this.colModel.getDataIndex(ed.col);
59453         var e = {
59454             grid: this,
59455             record: r,
59456             field: field,
59457             originalValue: startValue,
59458             value: value,
59459             row: ed.row,
59460             column: ed.col,
59461             cancel:false,
59462             editor: ed
59463         };
59464         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
59465         cell.show();
59466           
59467         if(String(value) !== String(startValue)){
59468             
59469             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
59470                 r.set(field, e.value);
59471                 // if we are dealing with a combo box..
59472                 // then we also set the 'name' colum to be the displayField
59473                 if (ed.field.displayField && ed.field.name) {
59474                     r.set(ed.field.name, ed.field.el.dom.value);
59475                 }
59476                 
59477                 delete e.cancel; //?? why!!!
59478                 this.fireEvent("afteredit", e);
59479             }
59480         } else {
59481             this.fireEvent("afteredit", e); // always fire it!
59482         }
59483         this.view.focusCell(ed.row, ed.col);
59484     },
59485
59486     /**
59487      * Starts editing the specified for the specified row/column
59488      * @param {Number} rowIndex
59489      * @param {Number} colIndex
59490      */
59491     startEditing : function(row, col){
59492         this.stopEditing();
59493         if(this.colModel.isCellEditable(col, row)){
59494             this.view.ensureVisible(row, col, true);
59495           
59496             var r = this.dataSource.getAt(row);
59497             var field = this.colModel.getDataIndex(col);
59498             var cell = Roo.get(this.view.getCell(row,col));
59499             var e = {
59500                 grid: this,
59501                 record: r,
59502                 field: field,
59503                 value: r.data[field],
59504                 row: row,
59505                 column: col,
59506                 cancel:false 
59507             };
59508             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
59509                 this.editing = true;
59510                 var ed = this.colModel.getCellEditor(col, row);
59511                 
59512                 if (!ed) {
59513                     return;
59514                 }
59515                 if(!ed.rendered){
59516                     ed.render(ed.parentEl || document.body);
59517                 }
59518                 ed.field.reset();
59519                
59520                 cell.hide();
59521                 
59522                 (function(){ // complex but required for focus issues in safari, ie and opera
59523                     ed.row = row;
59524                     ed.col = col;
59525                     ed.record = r;
59526                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
59527                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
59528                     this.activeEditor = ed;
59529                     var v = r.data[field];
59530                     ed.startEdit(this.view.getCell(row, col), v);
59531                     // combo's with 'displayField and name set
59532                     if (ed.field.displayField && ed.field.name) {
59533                         ed.field.el.dom.value = r.data[ed.field.name];
59534                     }
59535                     
59536                     
59537                 }).defer(50, this);
59538             }
59539         }
59540     },
59541         
59542     /**
59543      * Stops any active editing
59544      */
59545     stopEditing : function(){
59546         if(this.activeEditor){
59547             this.activeEditor.completeEdit();
59548         }
59549         this.activeEditor = null;
59550     },
59551         
59552          /**
59553      * Called to get grid's drag proxy text, by default returns this.ddText.
59554      * @return {String}
59555      */
59556     getDragDropText : function(){
59557         var count = this.selModel.getSelectedCell() ? 1 : 0;
59558         return String.format(this.ddText, count, count == 1 ? '' : 's');
59559     }
59560         
59561 });/*
59562  * Based on:
59563  * Ext JS Library 1.1.1
59564  * Copyright(c) 2006-2007, Ext JS, LLC.
59565  *
59566  * Originally Released Under LGPL - original licence link has changed is not relivant.
59567  *
59568  * Fork - LGPL
59569  * <script type="text/javascript">
59570  */
59571
59572 // private - not really -- you end up using it !
59573 // This is a support class used internally by the Grid components
59574
59575 /**
59576  * @class Roo.grid.GridEditor
59577  * @extends Roo.Editor
59578  * Class for creating and editable grid elements.
59579  * @param {Object} config any settings (must include field)
59580  */
59581 Roo.grid.GridEditor = function(field, config){
59582     if (!config && field.field) {
59583         config = field;
59584         field = Roo.factory(config.field, Roo.form);
59585     }
59586     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
59587     field.monitorTab = false;
59588 };
59589
59590 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
59591     
59592     /**
59593      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
59594      */
59595     
59596     alignment: "tl-tl",
59597     autoSize: "width",
59598     hideEl : false,
59599     cls: "x-small-editor x-grid-editor",
59600     shim:false,
59601     shadow:"frame"
59602 });/*
59603  * Based on:
59604  * Ext JS Library 1.1.1
59605  * Copyright(c) 2006-2007, Ext JS, LLC.
59606  *
59607  * Originally Released Under LGPL - original licence link has changed is not relivant.
59608  *
59609  * Fork - LGPL
59610  * <script type="text/javascript">
59611  */
59612   
59613
59614   
59615 Roo.grid.PropertyRecord = Roo.data.Record.create([
59616     {name:'name',type:'string'},  'value'
59617 ]);
59618
59619
59620 Roo.grid.PropertyStore = function(grid, source){
59621     this.grid = grid;
59622     this.store = new Roo.data.Store({
59623         recordType : Roo.grid.PropertyRecord
59624     });
59625     this.store.on('update', this.onUpdate,  this);
59626     if(source){
59627         this.setSource(source);
59628     }
59629     Roo.grid.PropertyStore.superclass.constructor.call(this);
59630 };
59631
59632
59633
59634 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
59635     setSource : function(o){
59636         this.source = o;
59637         this.store.removeAll();
59638         var data = [];
59639         for(var k in o){
59640             if(this.isEditableValue(o[k])){
59641                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
59642             }
59643         }
59644         this.store.loadRecords({records: data}, {}, true);
59645     },
59646
59647     onUpdate : function(ds, record, type){
59648         if(type == Roo.data.Record.EDIT){
59649             var v = record.data['value'];
59650             var oldValue = record.modified['value'];
59651             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
59652                 this.source[record.id] = v;
59653                 record.commit();
59654                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
59655             }else{
59656                 record.reject();
59657             }
59658         }
59659     },
59660
59661     getProperty : function(row){
59662        return this.store.getAt(row);
59663     },
59664
59665     isEditableValue: function(val){
59666         if(val && val instanceof Date){
59667             return true;
59668         }else if(typeof val == 'object' || typeof val == 'function'){
59669             return false;
59670         }
59671         return true;
59672     },
59673
59674     setValue : function(prop, value){
59675         this.source[prop] = value;
59676         this.store.getById(prop).set('value', value);
59677     },
59678
59679     getSource : function(){
59680         return this.source;
59681     }
59682 });
59683
59684 Roo.grid.PropertyColumnModel = function(grid, store){
59685     this.grid = grid;
59686     var g = Roo.grid;
59687     g.PropertyColumnModel.superclass.constructor.call(this, [
59688         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
59689         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
59690     ]);
59691     this.store = store;
59692     this.bselect = Roo.DomHelper.append(document.body, {
59693         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
59694             {tag: 'option', value: 'true', html: 'true'},
59695             {tag: 'option', value: 'false', html: 'false'}
59696         ]
59697     });
59698     Roo.id(this.bselect);
59699     var f = Roo.form;
59700     this.editors = {
59701         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
59702         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
59703         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
59704         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
59705         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
59706     };
59707     this.renderCellDelegate = this.renderCell.createDelegate(this);
59708     this.renderPropDelegate = this.renderProp.createDelegate(this);
59709 };
59710
59711 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
59712     
59713     
59714     nameText : 'Name',
59715     valueText : 'Value',
59716     
59717     dateFormat : 'm/j/Y',
59718     
59719     
59720     renderDate : function(dateVal){
59721         return dateVal.dateFormat(this.dateFormat);
59722     },
59723
59724     renderBool : function(bVal){
59725         return bVal ? 'true' : 'false';
59726     },
59727
59728     isCellEditable : function(colIndex, rowIndex){
59729         return colIndex == 1;
59730     },
59731
59732     getRenderer : function(col){
59733         return col == 1 ?
59734             this.renderCellDelegate : this.renderPropDelegate;
59735     },
59736
59737     renderProp : function(v){
59738         return this.getPropertyName(v);
59739     },
59740
59741     renderCell : function(val){
59742         var rv = val;
59743         if(val instanceof Date){
59744             rv = this.renderDate(val);
59745         }else if(typeof val == 'boolean'){
59746             rv = this.renderBool(val);
59747         }
59748         return Roo.util.Format.htmlEncode(rv);
59749     },
59750
59751     getPropertyName : function(name){
59752         var pn = this.grid.propertyNames;
59753         return pn && pn[name] ? pn[name] : name;
59754     },
59755
59756     getCellEditor : function(colIndex, rowIndex){
59757         var p = this.store.getProperty(rowIndex);
59758         var n = p.data['name'], val = p.data['value'];
59759         
59760         if(typeof(this.grid.customEditors[n]) == 'string'){
59761             return this.editors[this.grid.customEditors[n]];
59762         }
59763         if(typeof(this.grid.customEditors[n]) != 'undefined'){
59764             return this.grid.customEditors[n];
59765         }
59766         if(val instanceof Date){
59767             return this.editors['date'];
59768         }else if(typeof val == 'number'){
59769             return this.editors['number'];
59770         }else if(typeof val == 'boolean'){
59771             return this.editors['boolean'];
59772         }else{
59773             return this.editors['string'];
59774         }
59775     }
59776 });
59777
59778 /**
59779  * @class Roo.grid.PropertyGrid
59780  * @extends Roo.grid.EditorGrid
59781  * This class represents the  interface of a component based property grid control.
59782  * <br><br>Usage:<pre><code>
59783  var grid = new Roo.grid.PropertyGrid("my-container-id", {
59784       
59785  });
59786  // set any options
59787  grid.render();
59788  * </code></pre>
59789   
59790  * @constructor
59791  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
59792  * The container MUST have some type of size defined for the grid to fill. The container will be
59793  * automatically set to position relative if it isn't already.
59794  * @param {Object} config A config object that sets properties on this grid.
59795  */
59796 Roo.grid.PropertyGrid = function(container, config){
59797     config = config || {};
59798     var store = new Roo.grid.PropertyStore(this);
59799     this.store = store;
59800     var cm = new Roo.grid.PropertyColumnModel(this, store);
59801     store.store.sort('name', 'ASC');
59802     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
59803         ds: store.store,
59804         cm: cm,
59805         enableColLock:false,
59806         enableColumnMove:false,
59807         stripeRows:false,
59808         trackMouseOver: false,
59809         clicksToEdit:1
59810     }, config));
59811     this.getGridEl().addClass('x-props-grid');
59812     this.lastEditRow = null;
59813     this.on('columnresize', this.onColumnResize, this);
59814     this.addEvents({
59815          /**
59816              * @event beforepropertychange
59817              * Fires before a property changes (return false to stop?)
59818              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
59819              * @param {String} id Record Id
59820              * @param {String} newval New Value
59821          * @param {String} oldval Old Value
59822              */
59823         "beforepropertychange": true,
59824         /**
59825              * @event propertychange
59826              * Fires after a property changes
59827              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
59828              * @param {String} id Record Id
59829              * @param {String} newval New Value
59830          * @param {String} oldval Old Value
59831              */
59832         "propertychange": true
59833     });
59834     this.customEditors = this.customEditors || {};
59835 };
59836 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
59837     
59838      /**
59839      * @cfg {Object} customEditors map of colnames=> custom editors.
59840      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
59841      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
59842      * false disables editing of the field.
59843          */
59844     
59845       /**
59846      * @cfg {Object} propertyNames map of property Names to their displayed value
59847          */
59848     
59849     render : function(){
59850         Roo.grid.PropertyGrid.superclass.render.call(this);
59851         this.autoSize.defer(100, this);
59852     },
59853
59854     autoSize : function(){
59855         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
59856         if(this.view){
59857             this.view.fitColumns();
59858         }
59859     },
59860
59861     onColumnResize : function(){
59862         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
59863         this.autoSize();
59864     },
59865     /**
59866      * Sets the data for the Grid
59867      * accepts a Key => Value object of all the elements avaiable.
59868      * @param {Object} data  to appear in grid.
59869      */
59870     setSource : function(source){
59871         this.store.setSource(source);
59872         //this.autoSize();
59873     },
59874     /**
59875      * Gets all the data from the grid.
59876      * @return {Object} data  data stored in grid
59877      */
59878     getSource : function(){
59879         return this.store.getSource();
59880     }
59881 });/*
59882   
59883  * Licence LGPL
59884  
59885  */
59886  
59887 /**
59888  * @class Roo.grid.Calendar
59889  * @extends Roo.util.Grid
59890  * This class extends the Grid to provide a calendar widget
59891  * <br><br>Usage:<pre><code>
59892  var grid = new Roo.grid.Calendar("my-container-id", {
59893      ds: myDataStore,
59894      cm: myColModel,
59895      selModel: mySelectionModel,
59896      autoSizeColumns: true,
59897      monitorWindowResize: false,
59898      trackMouseOver: true
59899      eventstore : real data store..
59900  });
59901  // set any options
59902  grid.render();
59903   
59904   * @constructor
59905  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
59906  * The container MUST have some type of size defined for the grid to fill. The container will be
59907  * automatically set to position relative if it isn't already.
59908  * @param {Object} config A config object that sets properties on this grid.
59909  */
59910 Roo.grid.Calendar = function(container, config){
59911         // initialize the container
59912         this.container = Roo.get(container);
59913         this.container.update("");
59914         this.container.setStyle("overflow", "hidden");
59915     this.container.addClass('x-grid-container');
59916
59917     this.id = this.container.id;
59918
59919     Roo.apply(this, config);
59920     // check and correct shorthanded configs
59921     
59922     var rows = [];
59923     var d =1;
59924     for (var r = 0;r < 6;r++) {
59925         
59926         rows[r]=[];
59927         for (var c =0;c < 7;c++) {
59928             rows[r][c]= '';
59929         }
59930     }
59931     if (this.eventStore) {
59932         this.eventStore= Roo.factory(this.eventStore, Roo.data);
59933         this.eventStore.on('load',this.onLoad, this);
59934         this.eventStore.on('beforeload',this.clearEvents, this);
59935          
59936     }
59937     
59938     this.dataSource = new Roo.data.Store({
59939             proxy: new Roo.data.MemoryProxy(rows),
59940             reader: new Roo.data.ArrayReader({}, [
59941                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
59942     });
59943
59944     this.dataSource.load();
59945     this.ds = this.dataSource;
59946     this.ds.xmodule = this.xmodule || false;
59947     
59948     
59949     var cellRender = function(v,x,r)
59950     {
59951         return String.format(
59952             '<div class="fc-day  fc-widget-content"><div>' +
59953                 '<div class="fc-event-container"></div>' +
59954                 '<div class="fc-day-number">{0}</div>'+
59955                 
59956                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
59957             '</div></div>', v);
59958     
59959     }
59960     
59961     
59962     this.colModel = new Roo.grid.ColumnModel( [
59963         {
59964             xtype: 'ColumnModel',
59965             xns: Roo.grid,
59966             dataIndex : 'weekday0',
59967             header : 'Sunday',
59968             renderer : cellRender
59969         },
59970         {
59971             xtype: 'ColumnModel',
59972             xns: Roo.grid,
59973             dataIndex : 'weekday1',
59974             header : 'Monday',
59975             renderer : cellRender
59976         },
59977         {
59978             xtype: 'ColumnModel',
59979             xns: Roo.grid,
59980             dataIndex : 'weekday2',
59981             header : 'Tuesday',
59982             renderer : cellRender
59983         },
59984         {
59985             xtype: 'ColumnModel',
59986             xns: Roo.grid,
59987             dataIndex : 'weekday3',
59988             header : 'Wednesday',
59989             renderer : cellRender
59990         },
59991         {
59992             xtype: 'ColumnModel',
59993             xns: Roo.grid,
59994             dataIndex : 'weekday4',
59995             header : 'Thursday',
59996             renderer : cellRender
59997         },
59998         {
59999             xtype: 'ColumnModel',
60000             xns: Roo.grid,
60001             dataIndex : 'weekday5',
60002             header : 'Friday',
60003             renderer : cellRender
60004         },
60005         {
60006             xtype: 'ColumnModel',
60007             xns: Roo.grid,
60008             dataIndex : 'weekday6',
60009             header : 'Saturday',
60010             renderer : cellRender
60011         }
60012     ]);
60013     this.cm = this.colModel;
60014     this.cm.xmodule = this.xmodule || false;
60015  
60016         
60017           
60018     //this.selModel = new Roo.grid.CellSelectionModel();
60019     //this.sm = this.selModel;
60020     //this.selModel.init(this);
60021     
60022     
60023     if(this.width){
60024         this.container.setWidth(this.width);
60025     }
60026
60027     if(this.height){
60028         this.container.setHeight(this.height);
60029     }
60030     /** @private */
60031         this.addEvents({
60032         // raw events
60033         /**
60034          * @event click
60035          * The raw click event for the entire grid.
60036          * @param {Roo.EventObject} e
60037          */
60038         "click" : true,
60039         /**
60040          * @event dblclick
60041          * The raw dblclick event for the entire grid.
60042          * @param {Roo.EventObject} e
60043          */
60044         "dblclick" : true,
60045         /**
60046          * @event contextmenu
60047          * The raw contextmenu event for the entire grid.
60048          * @param {Roo.EventObject} e
60049          */
60050         "contextmenu" : true,
60051         /**
60052          * @event mousedown
60053          * The raw mousedown event for the entire grid.
60054          * @param {Roo.EventObject} e
60055          */
60056         "mousedown" : true,
60057         /**
60058          * @event mouseup
60059          * The raw mouseup event for the entire grid.
60060          * @param {Roo.EventObject} e
60061          */
60062         "mouseup" : true,
60063         /**
60064          * @event mouseover
60065          * The raw mouseover event for the entire grid.
60066          * @param {Roo.EventObject} e
60067          */
60068         "mouseover" : true,
60069         /**
60070          * @event mouseout
60071          * The raw mouseout event for the entire grid.
60072          * @param {Roo.EventObject} e
60073          */
60074         "mouseout" : true,
60075         /**
60076          * @event keypress
60077          * The raw keypress event for the entire grid.
60078          * @param {Roo.EventObject} e
60079          */
60080         "keypress" : true,
60081         /**
60082          * @event keydown
60083          * The raw keydown event for the entire grid.
60084          * @param {Roo.EventObject} e
60085          */
60086         "keydown" : true,
60087
60088         // custom events
60089
60090         /**
60091          * @event cellclick
60092          * Fires when a cell is clicked
60093          * @param {Grid} this
60094          * @param {Number} rowIndex
60095          * @param {Number} columnIndex
60096          * @param {Roo.EventObject} e
60097          */
60098         "cellclick" : true,
60099         /**
60100          * @event celldblclick
60101          * Fires when a cell is double clicked
60102          * @param {Grid} this
60103          * @param {Number} rowIndex
60104          * @param {Number} columnIndex
60105          * @param {Roo.EventObject} e
60106          */
60107         "celldblclick" : true,
60108         /**
60109          * @event rowclick
60110          * Fires when a row is clicked
60111          * @param {Grid} this
60112          * @param {Number} rowIndex
60113          * @param {Roo.EventObject} e
60114          */
60115         "rowclick" : true,
60116         /**
60117          * @event rowdblclick
60118          * Fires when a row is double clicked
60119          * @param {Grid} this
60120          * @param {Number} rowIndex
60121          * @param {Roo.EventObject} e
60122          */
60123         "rowdblclick" : true,
60124         /**
60125          * @event headerclick
60126          * Fires when a header is clicked
60127          * @param {Grid} this
60128          * @param {Number} columnIndex
60129          * @param {Roo.EventObject} e
60130          */
60131         "headerclick" : true,
60132         /**
60133          * @event headerdblclick
60134          * Fires when a header cell is double clicked
60135          * @param {Grid} this
60136          * @param {Number} columnIndex
60137          * @param {Roo.EventObject} e
60138          */
60139         "headerdblclick" : true,
60140         /**
60141          * @event rowcontextmenu
60142          * Fires when a row is right clicked
60143          * @param {Grid} this
60144          * @param {Number} rowIndex
60145          * @param {Roo.EventObject} e
60146          */
60147         "rowcontextmenu" : true,
60148         /**
60149          * @event cellcontextmenu
60150          * Fires when a cell is right clicked
60151          * @param {Grid} this
60152          * @param {Number} rowIndex
60153          * @param {Number} cellIndex
60154          * @param {Roo.EventObject} e
60155          */
60156          "cellcontextmenu" : true,
60157         /**
60158          * @event headercontextmenu
60159          * Fires when a header is right clicked
60160          * @param {Grid} this
60161          * @param {Number} columnIndex
60162          * @param {Roo.EventObject} e
60163          */
60164         "headercontextmenu" : true,
60165         /**
60166          * @event bodyscroll
60167          * Fires when the body element is scrolled
60168          * @param {Number} scrollLeft
60169          * @param {Number} scrollTop
60170          */
60171         "bodyscroll" : true,
60172         /**
60173          * @event columnresize
60174          * Fires when the user resizes a column
60175          * @param {Number} columnIndex
60176          * @param {Number} newSize
60177          */
60178         "columnresize" : true,
60179         /**
60180          * @event columnmove
60181          * Fires when the user moves a column
60182          * @param {Number} oldIndex
60183          * @param {Number} newIndex
60184          */
60185         "columnmove" : true,
60186         /**
60187          * @event startdrag
60188          * Fires when row(s) start being dragged
60189          * @param {Grid} this
60190          * @param {Roo.GridDD} dd The drag drop object
60191          * @param {event} e The raw browser event
60192          */
60193         "startdrag" : true,
60194         /**
60195          * @event enddrag
60196          * Fires when a drag operation is complete
60197          * @param {Grid} this
60198          * @param {Roo.GridDD} dd The drag drop object
60199          * @param {event} e The raw browser event
60200          */
60201         "enddrag" : true,
60202         /**
60203          * @event dragdrop
60204          * Fires when dragged row(s) are dropped on a valid DD target
60205          * @param {Grid} this
60206          * @param {Roo.GridDD} dd The drag drop object
60207          * @param {String} targetId The target drag drop object
60208          * @param {event} e The raw browser event
60209          */
60210         "dragdrop" : true,
60211         /**
60212          * @event dragover
60213          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
60214          * @param {Grid} this
60215          * @param {Roo.GridDD} dd The drag drop object
60216          * @param {String} targetId The target drag drop object
60217          * @param {event} e The raw browser event
60218          */
60219         "dragover" : true,
60220         /**
60221          * @event dragenter
60222          *  Fires when the dragged row(s) first cross another DD target while being dragged
60223          * @param {Grid} this
60224          * @param {Roo.GridDD} dd The drag drop object
60225          * @param {String} targetId The target drag drop object
60226          * @param {event} e The raw browser event
60227          */
60228         "dragenter" : true,
60229         /**
60230          * @event dragout
60231          * Fires when the dragged row(s) leave another DD target while being dragged
60232          * @param {Grid} this
60233          * @param {Roo.GridDD} dd The drag drop object
60234          * @param {String} targetId The target drag drop object
60235          * @param {event} e The raw browser event
60236          */
60237         "dragout" : true,
60238         /**
60239          * @event rowclass
60240          * Fires when a row is rendered, so you can change add a style to it.
60241          * @param {GridView} gridview   The grid view
60242          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
60243          */
60244         'rowclass' : true,
60245
60246         /**
60247          * @event render
60248          * Fires when the grid is rendered
60249          * @param {Grid} grid
60250          */
60251         'render' : true,
60252             /**
60253              * @event select
60254              * Fires when a date is selected
60255              * @param {DatePicker} this
60256              * @param {Date} date The selected date
60257              */
60258         'select': true,
60259         /**
60260              * @event monthchange
60261              * Fires when the displayed month changes 
60262              * @param {DatePicker} this
60263              * @param {Date} date The selected month
60264              */
60265         'monthchange': true,
60266         /**
60267              * @event evententer
60268              * Fires when mouse over an event
60269              * @param {Calendar} this
60270              * @param {event} Event
60271              */
60272         'evententer': true,
60273         /**
60274              * @event eventleave
60275              * Fires when the mouse leaves an
60276              * @param {Calendar} this
60277              * @param {event}
60278              */
60279         'eventleave': true,
60280         /**
60281              * @event eventclick
60282              * Fires when the mouse click an
60283              * @param {Calendar} this
60284              * @param {event}
60285              */
60286         'eventclick': true,
60287         /**
60288              * @event eventrender
60289              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
60290              * @param {Calendar} this
60291              * @param {data} data to be modified
60292              */
60293         'eventrender': true
60294         
60295     });
60296
60297     Roo.grid.Grid.superclass.constructor.call(this);
60298     this.on('render', function() {
60299         this.view.el.addClass('x-grid-cal'); 
60300         
60301         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
60302
60303     },this);
60304     
60305     if (!Roo.grid.Calendar.style) {
60306         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
60307             
60308             
60309             '.x-grid-cal .x-grid-col' :  {
60310                 height: 'auto !important',
60311                 'vertical-align': 'top'
60312             },
60313             '.x-grid-cal  .fc-event-hori' : {
60314                 height: '14px'
60315             }
60316              
60317             
60318         }, Roo.id());
60319     }
60320
60321     
60322     
60323 };
60324 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
60325     /**
60326      * @cfg {Store} eventStore The store that loads events.
60327      */
60328     eventStore : 25,
60329
60330      
60331     activeDate : false,
60332     startDay : 0,
60333     autoWidth : true,
60334     monitorWindowResize : false,
60335
60336     
60337     resizeColumns : function() {
60338         var col = (this.view.el.getWidth() / 7) - 3;
60339         // loop through cols, and setWidth
60340         for(var i =0 ; i < 7 ; i++){
60341             this.cm.setColumnWidth(i, col);
60342         }
60343     },
60344      setDate :function(date) {
60345         
60346         Roo.log('setDate?');
60347         
60348         this.resizeColumns();
60349         var vd = this.activeDate;
60350         this.activeDate = date;
60351 //        if(vd && this.el){
60352 //            var t = date.getTime();
60353 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
60354 //                Roo.log('using add remove');
60355 //                
60356 //                this.fireEvent('monthchange', this, date);
60357 //                
60358 //                this.cells.removeClass("fc-state-highlight");
60359 //                this.cells.each(function(c){
60360 //                   if(c.dateValue == t){
60361 //                       c.addClass("fc-state-highlight");
60362 //                       setTimeout(function(){
60363 //                            try{c.dom.firstChild.focus();}catch(e){}
60364 //                       }, 50);
60365 //                       return false;
60366 //                   }
60367 //                   return true;
60368 //                });
60369 //                return;
60370 //            }
60371 //        }
60372         
60373         var days = date.getDaysInMonth();
60374         
60375         var firstOfMonth = date.getFirstDateOfMonth();
60376         var startingPos = firstOfMonth.getDay()-this.startDay;
60377         
60378         if(startingPos < this.startDay){
60379             startingPos += 7;
60380         }
60381         
60382         var pm = date.add(Date.MONTH, -1);
60383         var prevStart = pm.getDaysInMonth()-startingPos;
60384 //        
60385         
60386         
60387         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
60388         
60389         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
60390         //this.cells.addClassOnOver('fc-state-hover');
60391         
60392         var cells = this.cells.elements;
60393         var textEls = this.textNodes;
60394         
60395         //Roo.each(cells, function(cell){
60396         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
60397         //});
60398         
60399         days += startingPos;
60400
60401         // convert everything to numbers so it's fast
60402         var day = 86400000;
60403         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
60404         //Roo.log(d);
60405         //Roo.log(pm);
60406         //Roo.log(prevStart);
60407         
60408         var today = new Date().clearTime().getTime();
60409         var sel = date.clearTime().getTime();
60410         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
60411         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
60412         var ddMatch = this.disabledDatesRE;
60413         var ddText = this.disabledDatesText;
60414         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
60415         var ddaysText = this.disabledDaysText;
60416         var format = this.format;
60417         
60418         var setCellClass = function(cal, cell){
60419             
60420             //Roo.log('set Cell Class');
60421             cell.title = "";
60422             var t = d.getTime();
60423             
60424             //Roo.log(d);
60425             
60426             
60427             cell.dateValue = t;
60428             if(t == today){
60429                 cell.className += " fc-today";
60430                 cell.className += " fc-state-highlight";
60431                 cell.title = cal.todayText;
60432             }
60433             if(t == sel){
60434                 // disable highlight in other month..
60435                 cell.className += " fc-state-highlight";
60436                 
60437             }
60438             // disabling
60439             if(t < min) {
60440                 //cell.className = " fc-state-disabled";
60441                 cell.title = cal.minText;
60442                 return;
60443             }
60444             if(t > max) {
60445                 //cell.className = " fc-state-disabled";
60446                 cell.title = cal.maxText;
60447                 return;
60448             }
60449             if(ddays){
60450                 if(ddays.indexOf(d.getDay()) != -1){
60451                     // cell.title = ddaysText;
60452                    // cell.className = " fc-state-disabled";
60453                 }
60454             }
60455             if(ddMatch && format){
60456                 var fvalue = d.dateFormat(format);
60457                 if(ddMatch.test(fvalue)){
60458                     cell.title = ddText.replace("%0", fvalue);
60459                    cell.className = " fc-state-disabled";
60460                 }
60461             }
60462             
60463             if (!cell.initialClassName) {
60464                 cell.initialClassName = cell.dom.className;
60465             }
60466             
60467             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
60468         };
60469
60470         var i = 0;
60471         
60472         for(; i < startingPos; i++) {
60473             cells[i].dayName =  (++prevStart);
60474             Roo.log(textEls[i]);
60475             d.setDate(d.getDate()+1);
60476             
60477             //cells[i].className = "fc-past fc-other-month";
60478             setCellClass(this, cells[i]);
60479         }
60480         
60481         var intDay = 0;
60482         
60483         for(; i < days; i++){
60484             intDay = i - startingPos + 1;
60485             cells[i].dayName =  (intDay);
60486             d.setDate(d.getDate()+1);
60487             
60488             cells[i].className = ''; // "x-date-active";
60489             setCellClass(this, cells[i]);
60490         }
60491         var extraDays = 0;
60492         
60493         for(; i < 42; i++) {
60494             //textEls[i].innerHTML = (++extraDays);
60495             
60496             d.setDate(d.getDate()+1);
60497             cells[i].dayName = (++extraDays);
60498             cells[i].className = "fc-future fc-other-month";
60499             setCellClass(this, cells[i]);
60500         }
60501         
60502         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
60503         
60504         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
60505         
60506         // this will cause all the cells to mis
60507         var rows= [];
60508         var i =0;
60509         for (var r = 0;r < 6;r++) {
60510             for (var c =0;c < 7;c++) {
60511                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
60512             }    
60513         }
60514         
60515         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
60516         for(i=0;i<cells.length;i++) {
60517             
60518             this.cells.elements[i].dayName = cells[i].dayName ;
60519             this.cells.elements[i].className = cells[i].className;
60520             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
60521             this.cells.elements[i].title = cells[i].title ;
60522             this.cells.elements[i].dateValue = cells[i].dateValue ;
60523         }
60524         
60525         
60526         
60527         
60528         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
60529         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
60530         
60531         ////if(totalRows != 6){
60532             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
60533            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
60534        // }
60535         
60536         this.fireEvent('monthchange', this, date);
60537         
60538         
60539     },
60540  /**
60541      * Returns the grid's SelectionModel.
60542      * @return {SelectionModel}
60543      */
60544     getSelectionModel : function(){
60545         if(!this.selModel){
60546             this.selModel = new Roo.grid.CellSelectionModel();
60547         }
60548         return this.selModel;
60549     },
60550
60551     load: function() {
60552         this.eventStore.load()
60553         
60554         
60555         
60556     },
60557     
60558     findCell : function(dt) {
60559         dt = dt.clearTime().getTime();
60560         var ret = false;
60561         this.cells.each(function(c){
60562             //Roo.log("check " +c.dateValue + '?=' + dt);
60563             if(c.dateValue == dt){
60564                 ret = c;
60565                 return false;
60566             }
60567             return true;
60568         });
60569         
60570         return ret;
60571     },
60572     
60573     findCells : function(rec) {
60574         var s = rec.data.start_dt.clone().clearTime().getTime();
60575        // Roo.log(s);
60576         var e= rec.data.end_dt.clone().clearTime().getTime();
60577        // Roo.log(e);
60578         var ret = [];
60579         this.cells.each(function(c){
60580              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
60581             
60582             if(c.dateValue > e){
60583                 return ;
60584             }
60585             if(c.dateValue < s){
60586                 return ;
60587             }
60588             ret.push(c);
60589         });
60590         
60591         return ret;    
60592     },
60593     
60594     findBestRow: function(cells)
60595     {
60596         var ret = 0;
60597         
60598         for (var i =0 ; i < cells.length;i++) {
60599             ret  = Math.max(cells[i].rows || 0,ret);
60600         }
60601         return ret;
60602         
60603     },
60604     
60605     
60606     addItem : function(rec)
60607     {
60608         // look for vertical location slot in
60609         var cells = this.findCells(rec);
60610         
60611         rec.row = this.findBestRow(cells);
60612         
60613         // work out the location.
60614         
60615         var crow = false;
60616         var rows = [];
60617         for(var i =0; i < cells.length; i++) {
60618             if (!crow) {
60619                 crow = {
60620                     start : cells[i],
60621                     end :  cells[i]
60622                 };
60623                 continue;
60624             }
60625             if (crow.start.getY() == cells[i].getY()) {
60626                 // on same row.
60627                 crow.end = cells[i];
60628                 continue;
60629             }
60630             // different row.
60631             rows.push(crow);
60632             crow = {
60633                 start: cells[i],
60634                 end : cells[i]
60635             };
60636             
60637         }
60638         
60639         rows.push(crow);
60640         rec.els = [];
60641         rec.rows = rows;
60642         rec.cells = cells;
60643         for (var i = 0; i < cells.length;i++) {
60644             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
60645             
60646         }
60647         
60648         
60649     },
60650     
60651     clearEvents: function() {
60652         
60653         if (!this.eventStore.getCount()) {
60654             return;
60655         }
60656         // reset number of rows in cells.
60657         Roo.each(this.cells.elements, function(c){
60658             c.rows = 0;
60659         });
60660         
60661         this.eventStore.each(function(e) {
60662             this.clearEvent(e);
60663         },this);
60664         
60665     },
60666     
60667     clearEvent : function(ev)
60668     {
60669         if (ev.els) {
60670             Roo.each(ev.els, function(el) {
60671                 el.un('mouseenter' ,this.onEventEnter, this);
60672                 el.un('mouseleave' ,this.onEventLeave, this);
60673                 el.remove();
60674             },this);
60675             ev.els = [];
60676         }
60677     },
60678     
60679     
60680     renderEvent : function(ev,ctr) {
60681         if (!ctr) {
60682              ctr = this.view.el.select('.fc-event-container',true).first();
60683         }
60684         
60685          
60686         this.clearEvent(ev);
60687             //code
60688        
60689         
60690         
60691         ev.els = [];
60692         var cells = ev.cells;
60693         var rows = ev.rows;
60694         this.fireEvent('eventrender', this, ev);
60695         
60696         for(var i =0; i < rows.length; i++) {
60697             
60698             cls = '';
60699             if (i == 0) {
60700                 cls += ' fc-event-start';
60701             }
60702             if ((i+1) == rows.length) {
60703                 cls += ' fc-event-end';
60704             }
60705             
60706             //Roo.log(ev.data);
60707             // how many rows should it span..
60708             var cg = this.eventTmpl.append(ctr,Roo.apply({
60709                 fccls : cls
60710                 
60711             }, ev.data) , true);
60712             
60713             
60714             cg.on('mouseenter' ,this.onEventEnter, this, ev);
60715             cg.on('mouseleave' ,this.onEventLeave, this, ev);
60716             cg.on('click', this.onEventClick, this, ev);
60717             
60718             ev.els.push(cg);
60719             
60720             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
60721             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
60722             //Roo.log(cg);
60723              
60724             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
60725             cg.setWidth(ebox.right - sbox.x -2);
60726         }
60727     },
60728     
60729     renderEvents: function()
60730     {   
60731         // first make sure there is enough space..
60732         
60733         if (!this.eventTmpl) {
60734             this.eventTmpl = new Roo.Template(
60735                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
60736                     '<div class="fc-event-inner">' +
60737                         '<span class="fc-event-time">{time}</span>' +
60738                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
60739                     '</div>' +
60740                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
60741                 '</div>'
60742             );
60743                 
60744         }
60745                
60746         
60747         
60748         this.cells.each(function(c) {
60749             //Roo.log(c.select('.fc-day-content div',true).first());
60750             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
60751         });
60752         
60753         var ctr = this.view.el.select('.fc-event-container',true).first();
60754         
60755         var cls;
60756         this.eventStore.each(function(ev){
60757             
60758             this.renderEvent(ev);
60759              
60760              
60761         }, this);
60762         this.view.layout();
60763         
60764     },
60765     
60766     onEventEnter: function (e, el,event,d) {
60767         this.fireEvent('evententer', this, el, event);
60768     },
60769     
60770     onEventLeave: function (e, el,event,d) {
60771         this.fireEvent('eventleave', this, el, event);
60772     },
60773     
60774     onEventClick: function (e, el,event,d) {
60775         this.fireEvent('eventclick', this, el, event);
60776     },
60777     
60778     onMonthChange: function () {
60779         this.store.load();
60780     },
60781     
60782     onLoad: function () {
60783         
60784         //Roo.log('calendar onload');
60785 //         
60786         if(this.eventStore.getCount() > 0){
60787             
60788            
60789             
60790             this.eventStore.each(function(d){
60791                 
60792                 
60793                 // FIXME..
60794                 var add =   d.data;
60795                 if (typeof(add.end_dt) == 'undefined')  {
60796                     Roo.log("Missing End time in calendar data: ");
60797                     Roo.log(d);
60798                     return;
60799                 }
60800                 if (typeof(add.start_dt) == 'undefined')  {
60801                     Roo.log("Missing Start time in calendar data: ");
60802                     Roo.log(d);
60803                     return;
60804                 }
60805                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
60806                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
60807                 add.id = add.id || d.id;
60808                 add.title = add.title || '??';
60809                 
60810                 this.addItem(d);
60811                 
60812              
60813             },this);
60814         }
60815         
60816         this.renderEvents();
60817     }
60818     
60819
60820 });
60821 /*
60822  grid : {
60823                 xtype: 'Grid',
60824                 xns: Roo.grid,
60825                 listeners : {
60826                     render : function ()
60827                     {
60828                         _this.grid = this;
60829                         
60830                         if (!this.view.el.hasClass('course-timesheet')) {
60831                             this.view.el.addClass('course-timesheet');
60832                         }
60833                         if (this.tsStyle) {
60834                             this.ds.load({});
60835                             return; 
60836                         }
60837                         Roo.log('width');
60838                         Roo.log(_this.grid.view.el.getWidth());
60839                         
60840                         
60841                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
60842                             '.course-timesheet .x-grid-row' : {
60843                                 height: '80px'
60844                             },
60845                             '.x-grid-row td' : {
60846                                 'vertical-align' : 0
60847                             },
60848                             '.course-edit-link' : {
60849                                 'color' : 'blue',
60850                                 'text-overflow' : 'ellipsis',
60851                                 'overflow' : 'hidden',
60852                                 'white-space' : 'nowrap',
60853                                 'cursor' : 'pointer'
60854                             },
60855                             '.sub-link' : {
60856                                 'color' : 'green'
60857                             },
60858                             '.de-act-sup-link' : {
60859                                 'color' : 'purple',
60860                                 'text-decoration' : 'line-through'
60861                             },
60862                             '.de-act-link' : {
60863                                 'color' : 'red',
60864                                 'text-decoration' : 'line-through'
60865                             },
60866                             '.course-timesheet .course-highlight' : {
60867                                 'border-top-style': 'dashed !important',
60868                                 'border-bottom-bottom': 'dashed !important'
60869                             },
60870                             '.course-timesheet .course-item' : {
60871                                 'font-family'   : 'tahoma, arial, helvetica',
60872                                 'font-size'     : '11px',
60873                                 'overflow'      : 'hidden',
60874                                 'padding-left'  : '10px',
60875                                 'padding-right' : '10px',
60876                                 'padding-top' : '10px' 
60877                             }
60878                             
60879                         }, Roo.id());
60880                                 this.ds.load({});
60881                     }
60882                 },
60883                 autoWidth : true,
60884                 monitorWindowResize : false,
60885                 cellrenderer : function(v,x,r)
60886                 {
60887                     return v;
60888                 },
60889                 sm : {
60890                     xtype: 'CellSelectionModel',
60891                     xns: Roo.grid
60892                 },
60893                 dataSource : {
60894                     xtype: 'Store',
60895                     xns: Roo.data,
60896                     listeners : {
60897                         beforeload : function (_self, options)
60898                         {
60899                             options.params = options.params || {};
60900                             options.params._month = _this.monthField.getValue();
60901                             options.params.limit = 9999;
60902                             options.params['sort'] = 'when_dt';    
60903                             options.params['dir'] = 'ASC';    
60904                             this.proxy.loadResponse = this.loadResponse;
60905                             Roo.log("load?");
60906                             //this.addColumns();
60907                         },
60908                         load : function (_self, records, options)
60909                         {
60910                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
60911                                 // if you click on the translation.. you can edit it...
60912                                 var el = Roo.get(this);
60913                                 var id = el.dom.getAttribute('data-id');
60914                                 var d = el.dom.getAttribute('data-date');
60915                                 var t = el.dom.getAttribute('data-time');
60916                                 //var id = this.child('span').dom.textContent;
60917                                 
60918                                 //Roo.log(this);
60919                                 Pman.Dialog.CourseCalendar.show({
60920                                     id : id,
60921                                     when_d : d,
60922                                     when_t : t,
60923                                     productitem_active : id ? 1 : 0
60924                                 }, function() {
60925                                     _this.grid.ds.load({});
60926                                 });
60927                            
60928                            });
60929                            
60930                            _this.panel.fireEvent('resize', [ '', '' ]);
60931                         }
60932                     },
60933                     loadResponse : function(o, success, response){
60934                             // this is overridden on before load..
60935                             
60936                             Roo.log("our code?");       
60937                             //Roo.log(success);
60938                             //Roo.log(response)
60939                             delete this.activeRequest;
60940                             if(!success){
60941                                 this.fireEvent("loadexception", this, o, response);
60942                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
60943                                 return;
60944                             }
60945                             var result;
60946                             try {
60947                                 result = o.reader.read(response);
60948                             }catch(e){
60949                                 Roo.log("load exception?");
60950                                 this.fireEvent("loadexception", this, o, response, e);
60951                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
60952                                 return;
60953                             }
60954                             Roo.log("ready...");        
60955                             // loop through result.records;
60956                             // and set this.tdate[date] = [] << array of records..
60957                             _this.tdata  = {};
60958                             Roo.each(result.records, function(r){
60959                                 //Roo.log(r.data);
60960                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
60961                                     _this.tdata[r.data.when_dt.format('j')] = [];
60962                                 }
60963                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
60964                             });
60965                             
60966                             //Roo.log(_this.tdata);
60967                             
60968                             result.records = [];
60969                             result.totalRecords = 6;
60970                     
60971                             // let's generate some duumy records for the rows.
60972                             //var st = _this.dateField.getValue();
60973                             
60974                             // work out monday..
60975                             //st = st.add(Date.DAY, -1 * st.format('w'));
60976                             
60977                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
60978                             
60979                             var firstOfMonth = date.getFirstDayOfMonth();
60980                             var days = date.getDaysInMonth();
60981                             var d = 1;
60982                             var firstAdded = false;
60983                             for (var i = 0; i < result.totalRecords ; i++) {
60984                                 //var d= st.add(Date.DAY, i);
60985                                 var row = {};
60986                                 var added = 0;
60987                                 for(var w = 0 ; w < 7 ; w++){
60988                                     if(!firstAdded && firstOfMonth != w){
60989                                         continue;
60990                                     }
60991                                     if(d > days){
60992                                         continue;
60993                                     }
60994                                     firstAdded = true;
60995                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
60996                                     row['weekday'+w] = String.format(
60997                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
60998                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
60999                                                     d,
61000                                                     date.format('Y-m-')+dd
61001                                                 );
61002                                     added++;
61003                                     if(typeof(_this.tdata[d]) != 'undefined'){
61004                                         Roo.each(_this.tdata[d], function(r){
61005                                             var is_sub = '';
61006                                             var deactive = '';
61007                                             var id = r.id;
61008                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
61009                                             if(r.parent_id*1>0){
61010                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
61011                                                 id = r.parent_id;
61012                                             }
61013                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
61014                                                 deactive = 'de-act-link';
61015                                             }
61016                                             
61017                                             row['weekday'+w] += String.format(
61018                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
61019                                                     id, //0
61020                                                     r.product_id_name, //1
61021                                                     r.when_dt.format('h:ia'), //2
61022                                                     is_sub, //3
61023                                                     deactive, //4
61024                                                     desc // 5
61025                                             );
61026                                         });
61027                                     }
61028                                     d++;
61029                                 }
61030                                 
61031                                 // only do this if something added..
61032                                 if(added > 0){ 
61033                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
61034                                 }
61035                                 
61036                                 
61037                                 // push it twice. (second one with an hour..
61038                                 
61039                             }
61040                             //Roo.log(result);
61041                             this.fireEvent("load", this, o, o.request.arg);
61042                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
61043                         },
61044                     sortInfo : {field: 'when_dt', direction : 'ASC' },
61045                     proxy : {
61046                         xtype: 'HttpProxy',
61047                         xns: Roo.data,
61048                         method : 'GET',
61049                         url : baseURL + '/Roo/Shop_course.php'
61050                     },
61051                     reader : {
61052                         xtype: 'JsonReader',
61053                         xns: Roo.data,
61054                         id : 'id',
61055                         fields : [
61056                             {
61057                                 'name': 'id',
61058                                 'type': 'int'
61059                             },
61060                             {
61061                                 'name': 'when_dt',
61062                                 'type': 'string'
61063                             },
61064                             {
61065                                 'name': 'end_dt',
61066                                 'type': 'string'
61067                             },
61068                             {
61069                                 'name': 'parent_id',
61070                                 'type': 'int'
61071                             },
61072                             {
61073                                 'name': 'product_id',
61074                                 'type': 'int'
61075                             },
61076                             {
61077                                 'name': 'productitem_id',
61078                                 'type': 'int'
61079                             },
61080                             {
61081                                 'name': 'guid',
61082                                 'type': 'int'
61083                             }
61084                         ]
61085                     }
61086                 },
61087                 toolbar : {
61088                     xtype: 'Toolbar',
61089                     xns: Roo,
61090                     items : [
61091                         {
61092                             xtype: 'Button',
61093                             xns: Roo.Toolbar,
61094                             listeners : {
61095                                 click : function (_self, e)
61096                                 {
61097                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
61098                                     sd.setMonth(sd.getMonth()-1);
61099                                     _this.monthField.setValue(sd.format('Y-m-d'));
61100                                     _this.grid.ds.load({});
61101                                 }
61102                             },
61103                             text : "Back"
61104                         },
61105                         {
61106                             xtype: 'Separator',
61107                             xns: Roo.Toolbar
61108                         },
61109                         {
61110                             xtype: 'MonthField',
61111                             xns: Roo.form,
61112                             listeners : {
61113                                 render : function (_self)
61114                                 {
61115                                     _this.monthField = _self;
61116                                    // _this.monthField.set  today
61117                                 },
61118                                 select : function (combo, date)
61119                                 {
61120                                     _this.grid.ds.load({});
61121                                 }
61122                             },
61123                             value : (function() { return new Date(); })()
61124                         },
61125                         {
61126                             xtype: 'Separator',
61127                             xns: Roo.Toolbar
61128                         },
61129                         {
61130                             xtype: 'TextItem',
61131                             xns: Roo.Toolbar,
61132                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
61133                         },
61134                         {
61135                             xtype: 'Fill',
61136                             xns: Roo.Toolbar
61137                         },
61138                         {
61139                             xtype: 'Button',
61140                             xns: Roo.Toolbar,
61141                             listeners : {
61142                                 click : function (_self, e)
61143                                 {
61144                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
61145                                     sd.setMonth(sd.getMonth()+1);
61146                                     _this.monthField.setValue(sd.format('Y-m-d'));
61147                                     _this.grid.ds.load({});
61148                                 }
61149                             },
61150                             text : "Next"
61151                         }
61152                     ]
61153                 },
61154                  
61155             }
61156         };
61157         
61158         *//*
61159  * Based on:
61160  * Ext JS Library 1.1.1
61161  * Copyright(c) 2006-2007, Ext JS, LLC.
61162  *
61163  * Originally Released Under LGPL - original licence link has changed is not relivant.
61164  *
61165  * Fork - LGPL
61166  * <script type="text/javascript">
61167  */
61168  
61169 /**
61170  * @class Roo.LoadMask
61171  * A simple utility class for generically masking elements while loading data.  If the element being masked has
61172  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
61173  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
61174  * element's UpdateManager load indicator and will be destroyed after the initial load.
61175  * @constructor
61176  * Create a new LoadMask
61177  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
61178  * @param {Object} config The config object
61179  */
61180 Roo.LoadMask = function(el, config){
61181     this.el = Roo.get(el);
61182     Roo.apply(this, config);
61183     if(this.store){
61184         this.store.on('beforeload', this.onBeforeLoad, this);
61185         this.store.on('load', this.onLoad, this);
61186         this.store.on('loadexception', this.onLoadException, this);
61187         this.removeMask = false;
61188     }else{
61189         var um = this.el.getUpdateManager();
61190         um.showLoadIndicator = false; // disable the default indicator
61191         um.on('beforeupdate', this.onBeforeLoad, this);
61192         um.on('update', this.onLoad, this);
61193         um.on('failure', this.onLoad, this);
61194         this.removeMask = true;
61195     }
61196 };
61197
61198 Roo.LoadMask.prototype = {
61199     /**
61200      * @cfg {Boolean} removeMask
61201      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
61202      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
61203      */
61204     /**
61205      * @cfg {String} msg
61206      * The text to display in a centered loading message box (defaults to 'Loading...')
61207      */
61208     msg : 'Loading...',
61209     /**
61210      * @cfg {String} msgCls
61211      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
61212      */
61213     msgCls : 'x-mask-loading',
61214
61215     /**
61216      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
61217      * @type Boolean
61218      */
61219     disabled: false,
61220
61221     /**
61222      * Disables the mask to prevent it from being displayed
61223      */
61224     disable : function(){
61225        this.disabled = true;
61226     },
61227
61228     /**
61229      * Enables the mask so that it can be displayed
61230      */
61231     enable : function(){
61232         this.disabled = false;
61233     },
61234     
61235     onLoadException : function()
61236     {
61237         Roo.log(arguments);
61238         
61239         if (typeof(arguments[3]) != 'undefined') {
61240             Roo.MessageBox.alert("Error loading",arguments[3]);
61241         } 
61242         /*
61243         try {
61244             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
61245                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
61246             }   
61247         } catch(e) {
61248             
61249         }
61250         */
61251     
61252         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
61253     },
61254     // private
61255     onLoad : function()
61256     {
61257         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
61258     },
61259
61260     // private
61261     onBeforeLoad : function(){
61262         if(!this.disabled){
61263             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
61264         }
61265     },
61266
61267     // private
61268     destroy : function(){
61269         if(this.store){
61270             this.store.un('beforeload', this.onBeforeLoad, this);
61271             this.store.un('load', this.onLoad, this);
61272             this.store.un('loadexception', this.onLoadException, this);
61273         }else{
61274             var um = this.el.getUpdateManager();
61275             um.un('beforeupdate', this.onBeforeLoad, this);
61276             um.un('update', this.onLoad, this);
61277             um.un('failure', this.onLoad, this);
61278         }
61279     }
61280 };/*
61281  * Based on:
61282  * Ext JS Library 1.1.1
61283  * Copyright(c) 2006-2007, Ext JS, LLC.
61284  *
61285  * Originally Released Under LGPL - original licence link has changed is not relivant.
61286  *
61287  * Fork - LGPL
61288  * <script type="text/javascript">
61289  */
61290
61291
61292 /**
61293  * @class Roo.XTemplate
61294  * @extends Roo.Template
61295  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
61296 <pre><code>
61297 var t = new Roo.XTemplate(
61298         '&lt;select name="{name}"&gt;',
61299                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
61300         '&lt;/select&gt;'
61301 );
61302  
61303 // then append, applying the master template values
61304  </code></pre>
61305  *
61306  * Supported features:
61307  *
61308  *  Tags:
61309
61310 <pre><code>
61311       {a_variable} - output encoded.
61312       {a_variable.format:("Y-m-d")} - call a method on the variable
61313       {a_variable:raw} - unencoded output
61314       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
61315       {a_variable:this.method_on_template(...)} - call a method on the template object.
61316  
61317 </code></pre>
61318  *  The tpl tag:
61319 <pre><code>
61320         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
61321         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
61322         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
61323         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
61324   
61325         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
61326         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
61327 </code></pre>
61328  *      
61329  */
61330 Roo.XTemplate = function()
61331 {
61332     Roo.XTemplate.superclass.constructor.apply(this, arguments);
61333     if (this.html) {
61334         this.compile();
61335     }
61336 };
61337
61338
61339 Roo.extend(Roo.XTemplate, Roo.Template, {
61340
61341     /**
61342      * The various sub templates
61343      */
61344     tpls : false,
61345     /**
61346      *
61347      * basic tag replacing syntax
61348      * WORD:WORD()
61349      *
61350      * // you can fake an object call by doing this
61351      *  x.t:(test,tesT) 
61352      * 
61353      */
61354     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
61355
61356     /**
61357      * compile the template
61358      *
61359      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
61360      *
61361      */
61362     compile: function()
61363     {
61364         var s = this.html;
61365      
61366         s = ['<tpl>', s, '</tpl>'].join('');
61367     
61368         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
61369             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
61370             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
61371             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
61372             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
61373             m,
61374             id     = 0,
61375             tpls   = [];
61376     
61377         while(true == !!(m = s.match(re))){
61378             var forMatch   = m[0].match(nameRe),
61379                 ifMatch   = m[0].match(ifRe),
61380                 execMatch   = m[0].match(execRe),
61381                 namedMatch   = m[0].match(namedRe),
61382                 
61383                 exp  = null, 
61384                 fn   = null,
61385                 exec = null,
61386                 name = forMatch && forMatch[1] ? forMatch[1] : '';
61387                 
61388             if (ifMatch) {
61389                 // if - puts fn into test..
61390                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
61391                 if(exp){
61392                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
61393                 }
61394             }
61395             
61396             if (execMatch) {
61397                 // exec - calls a function... returns empty if true is  returned.
61398                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
61399                 if(exp){
61400                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
61401                 }
61402             }
61403             
61404             
61405             if (name) {
61406                 // for = 
61407                 switch(name){
61408                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
61409                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
61410                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
61411                 }
61412             }
61413             var uid = namedMatch ? namedMatch[1] : id;
61414             
61415             
61416             tpls.push({
61417                 id:     namedMatch ? namedMatch[1] : id,
61418                 target: name,
61419                 exec:   exec,
61420                 test:   fn,
61421                 body:   m[1] || ''
61422             });
61423             if (namedMatch) {
61424                 s = s.replace(m[0], '');
61425             } else { 
61426                 s = s.replace(m[0], '{xtpl'+ id + '}');
61427             }
61428             ++id;
61429         }
61430         this.tpls = [];
61431         for(var i = tpls.length-1; i >= 0; --i){
61432             this.compileTpl(tpls[i]);
61433             this.tpls[tpls[i].id] = tpls[i];
61434         }
61435         this.master = tpls[tpls.length-1];
61436         return this;
61437     },
61438     /**
61439      * same as applyTemplate, except it's done to one of the subTemplates
61440      * when using named templates, you can do:
61441      *
61442      * var str = pl.applySubTemplate('your-name', values);
61443      *
61444      * 
61445      * @param {Number} id of the template
61446      * @param {Object} values to apply to template
61447      * @param {Object} parent (normaly the instance of this object)
61448      */
61449     applySubTemplate : function(id, values, parent)
61450     {
61451         
61452         
61453         var t = this.tpls[id];
61454         
61455         
61456         try { 
61457             if(t.test && !t.test.call(this, values, parent)){
61458                 return '';
61459             }
61460         } catch(e) {
61461             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
61462             Roo.log(e.toString());
61463             Roo.log(t.test);
61464             return ''
61465         }
61466         try { 
61467             
61468             if(t.exec && t.exec.call(this, values, parent)){
61469                 return '';
61470             }
61471         } catch(e) {
61472             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
61473             Roo.log(e.toString());
61474             Roo.log(t.exec);
61475             return ''
61476         }
61477         try {
61478             var vs = t.target ? t.target.call(this, values, parent) : values;
61479             parent = t.target ? values : parent;
61480             if(t.target && vs instanceof Array){
61481                 var buf = [];
61482                 for(var i = 0, len = vs.length; i < len; i++){
61483                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
61484                 }
61485                 return buf.join('');
61486             }
61487             return t.compiled.call(this, vs, parent);
61488         } catch (e) {
61489             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
61490             Roo.log(e.toString());
61491             Roo.log(t.compiled);
61492             return '';
61493         }
61494     },
61495
61496     compileTpl : function(tpl)
61497     {
61498         var fm = Roo.util.Format;
61499         var useF = this.disableFormats !== true;
61500         var sep = Roo.isGecko ? "+" : ",";
61501         var undef = function(str) {
61502             Roo.log("Property not found :"  + str);
61503             return '';
61504         };
61505         
61506         var fn = function(m, name, format, args)
61507         {
61508             //Roo.log(arguments);
61509             args = args ? args.replace(/\\'/g,"'") : args;
61510             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
61511             if (typeof(format) == 'undefined') {
61512                 format= 'htmlEncode';
61513             }
61514             if (format == 'raw' ) {
61515                 format = false;
61516             }
61517             
61518             if(name.substr(0, 4) == 'xtpl'){
61519                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
61520             }
61521             
61522             // build an array of options to determine if value is undefined..
61523             
61524             // basically get 'xxxx.yyyy' then do
61525             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
61526             //    (function () { Roo.log("Property not found"); return ''; })() :
61527             //    ......
61528             
61529             var udef_ar = [];
61530             var lookfor = '';
61531             Roo.each(name.split('.'), function(st) {
61532                 lookfor += (lookfor.length ? '.': '') + st;
61533                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
61534             });
61535             
61536             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
61537             
61538             
61539             if(format && useF){
61540                 
61541                 args = args ? ',' + args : "";
61542                  
61543                 if(format.substr(0, 5) != "this."){
61544                     format = "fm." + format + '(';
61545                 }else{
61546                     format = 'this.call("'+ format.substr(5) + '", ';
61547                     args = ", values";
61548                 }
61549                 
61550                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
61551             }
61552              
61553             if (args.length) {
61554                 // called with xxyx.yuu:(test,test)
61555                 // change to ()
61556                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
61557             }
61558             // raw.. - :raw modifier..
61559             return "'"+ sep + udef_st  + name + ")"+sep+"'";
61560             
61561         };
61562         var body;
61563         // branched to use + in gecko and [].join() in others
61564         if(Roo.isGecko){
61565             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
61566                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
61567                     "';};};";
61568         }else{
61569             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
61570             body.push(tpl.body.replace(/(\r\n|\n)/g,
61571                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
61572             body.push("'].join('');};};");
61573             body = body.join('');
61574         }
61575         
61576         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
61577        
61578         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
61579         eval(body);
61580         
61581         return this;
61582     },
61583
61584     applyTemplate : function(values){
61585         return this.master.compiled.call(this, values, {});
61586         //var s = this.subs;
61587     },
61588
61589     apply : function(){
61590         return this.applyTemplate.apply(this, arguments);
61591     }
61592
61593  });
61594
61595 Roo.XTemplate.from = function(el){
61596     el = Roo.getDom(el);
61597     return new Roo.XTemplate(el.value || el.innerHTML);
61598 };